diff --git a/src/routes/postSkipSegments.ts b/src/routes/postSkipSegments.ts index 88bc9af..3a4a294 100644 --- a/src/routes/postSkipSegments.ts +++ b/src/routes/postSkipSegments.ts @@ -9,17 +9,18 @@ import { getIP } from "../utils/getIP"; import { getFormattedTime } from "../utils/getFormattedTime"; import { dispatchEvent } from "../utils/webhookUtils"; import { Request, Response } from "express"; -import { ActionType, Category, CategoryActionType, IncomingSegment, SegmentUUID, Service, VideoDuration, VideoID } from "../types/segments.model"; +import { ActionType, Category, CategoryActionType, IncomingSegment, IPAddress, SegmentUUID, Service, VideoDuration, VideoID } from "../types/segments.model"; import { deleteLockCategories } from "./deleteLockCategories"; import { getCategoryActionType } from "../utils/categoryInfo"; import { QueryCacher } from "../utils/queryCacher"; import { getReputation } from "../utils/reputation"; import { APIVideoData, APIVideoInfo } from "../types/youtubeApi.model"; -import { UserID } from "../types/user.model"; +import { HashedUserID, UserID } from "../types/user.model"; import { isUserVIP } from "../utils/isUserVIP"; import { parseUserAgent } from "../utils/userAgent"; import { getService } from "../utils/getService"; import axios from "axios"; +import { vote } from "./voteOnSponsorTime"; type CheckResult = { pass: boolean, @@ -343,7 +344,7 @@ function checkInvalidFields(videoID: VideoID, userID: UserID, segments: Incoming return CHECK_PASS; } -async function checkEachSegmentValid(userID: string, videoID: VideoID, +async function checkEachSegmentValid(rawIP: IPAddress, paramUserID: UserID, userID: HashedUserID, videoID: VideoID, segments: IncomingSegment[], service: string, isVIP: boolean, lockedCategoryList: Array): Promise { for (let i = 0; i < segments.length; i++) { @@ -399,14 +400,21 @@ async function checkEachSegmentValid(userID: string, videoID: VideoID, if (!isVIP && segments[i].category === "sponsor" && segments[i].actionType !== ActionType.Full && Math.abs(startTime - endTime) < 1) { // Too short - return { pass: false, errorMessage: "Sponsors must be longer than 1 second long", errorCode: 400 }; + return { pass: false, errorMessage: "Segments must be longer than 1 second long", errorCode: 400 }; } //check if this info has already been submitted before - const duplicateCheck2Row = await db.prepare("get", `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "startTime" = ? + const duplicateCheck2Row = await db.prepare("get", `SELECT "UUID" FROM "sponsorTimes" WHERE "startTime" = ? and "endTime" = ? and "category" = ? and "actionType" = ? and "videoID" = ? and "service" = ?`, [startTime, endTime, segments[i].category, segments[i].actionType, videoID, service]); - if (duplicateCheck2Row.count > 0) { - return { pass: false, errorMessage: "Sponsors has already been submitted before.", errorCode: 409 }; + if (duplicateCheck2Row) { + if (segments[i].actionType === ActionType.Full) { + // Forward as vote + vote(rawIP, duplicateCheck2Row.UUID, paramUserID, 1); + segments[i].ignoreSegment = true; + continue; + } else { + return { pass: false, errorMessage: "Segment has already been submitted before.", errorCode: 409 }; + } } } @@ -576,15 +584,15 @@ export async function postSkipSegments(req: Request, res: Response): Promise { + , hashedIP: HashedIP, finalResponse: FinalResponse): Promise<{ status: number, message?: string }> { // Check if they've already made a vote const usersLastVoteInfo = await privateDB.prepare("get", `select count(*) as votes, category from "categoryVotes" where "UUID" = ? and "userID" = ? group by category`, [UUID, userID]); if (usersLastVoteInfo?.category === category) { // Double vote, ignore - return res.sendStatus(finalResponse.finalStatus); + return { status: finalResponse.finalStatus }; } const videoInfo = (await db.prepare("get", `SELECT "category", "videoID", "hashedVideoID", "service", "userID", "locked" FROM "sponsorTimes" WHERE "UUID" = ?`, [UUID])) as {category: Category, videoID: VideoID, hashedVideoID: VideoIDHash, service: Service, userID: UserID, locked: number}; if (!videoInfo) { // Submission doesn't exist - return res.status(400).send("Submission doesn't exist."); + return { status: 400, message: "Submission doesn't exist." }; } if (!config.categoryList.includes(category)) { - return res.status(400).send("Category doesn't exist."); + return { status: 400, message: "Category doesn't exist." }; } if (getCategoryActionType(category) !== CategoryActionType.Skippable) { - return res.status(400).send("Cannot vote for this category"); + return { status: 400, message: "Cannot vote for this category" }; } // Ignore vote if the next category is locked const nextCategoryLocked = await db.prepare("get", `SELECT "videoID", "category" FROM "lockCategories" WHERE "videoID" = ? AND "service" = ? AND "category" = ?`, [videoInfo.videoID, videoInfo.service, category]); if (nextCategoryLocked && !isVIP) { - return res.sendStatus(200); + return { status: 200 }; } // Ignore vote if the segment is locked if (!isVIP && videoInfo.locked === 1) { - return res.sendStatus(200); + return { status: 200 }; } const nextCategoryInfo = await db.prepare("get", `select votes from "categoryVotes" where "UUID" = ? and category = ?`, [UUID, category]); @@ -279,7 +279,7 @@ async function categoryVote(UUID: SegmentUUID, userID: UserID, isVIP: boolean, i QueryCacher.clearSegmentCache(videoInfo); - return res.sendStatus(finalResponse.finalStatus); + return { status: finalResponse.finalStatus }; } export function getUserID(req: Request): UserID { @@ -289,16 +289,30 @@ export function getUserID(req: Request): UserID { export async function voteOnSponsorTime(req: Request, res: Response): Promise { const UUID = req.query.UUID as SegmentUUID; const paramUserID = getUserID(req); - let type = req.query.type !== undefined ? parseInt(req.query.type as string) : undefined; + const type = req.query.type !== undefined ? parseInt(req.query.type as string) : undefined; const category = req.query.category as Category; + const ip = getIP(req); + const result = await vote(ip, UUID, paramUserID, type, category); + + const response = res.status(result.status); + if (result.message) { + return response.send(result.message); + } else if (result.json) { + return response.json(result.json); + } else { + return response.send(); + } +} + +export async function vote(ip: IPAddress, UUID: SegmentUUID, paramUserID: UserID, type: number, category?: Category): Promise<{ status: number, message?: string, json?: unknown }> { if (UUID === undefined || paramUserID === undefined || (type === undefined && category === undefined)) { //invalid request - return res.sendStatus(400); + return { status: 400 }; } if (paramUserID.length < 30 && config.mode !== "test") { // Ignore this vote, invalid - return res.sendStatus(200); + return { status: 200 }; } //hash the userID @@ -314,9 +328,6 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise