From 8b5e69f36fdc3132d6ee7d99594fbf66b443b15b Mon Sep 17 00:00:00 2001 From: Tristan Date: Mon, 24 Jun 2024 03:14:05 +0200 Subject: [PATCH 1/6] docs: fix typos in DatabaseSchema.md --- DatabaseSchema.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/DatabaseSchema.md b/DatabaseSchema.md index 1b69da4..adb1923 100644 --- a/DatabaseSchema.md +++ b/DatabaseSchema.md @@ -241,7 +241,7 @@ | title | TEXT | not null | | original | INTEGER | default 0 | | userID | TEXT | not null -| service | TEXT not null | +| service | TEXT | not null | | hashedVideoID | TEXT | not null | | timeSubmitted | INTEGER | not null | | UUID | TEXT | not null, primary key @@ -277,7 +277,6 @@ | Name | Type | | | -- | :--: | -- | -| UUID | TEXT | not null | | original | INTEGER | default 0 | | userID | TEXT | not null | | service | TEXT | not null | @@ -305,8 +304,8 @@ ### thumbnailVotes -| index | field | -| -- | :--: | +| Name | Type | | +| -- | :--: | -- | | UUID | TEXT | not null, primary key | | votes | INTEGER | not null, default 0 | | locked | INTEGER |not null, default 0 | From 3f026409cdfffd2451361c708263cf38c34951e3 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Mon, 29 Jul 2024 02:11:30 +0200 Subject: [PATCH 2/6] Don't show completely unrelated chapter suggestions Chapter suggestions should be at least slightly related to what the user has already typed. This change stops the server from sending suggestions that postgresql deems to be "less than 10% similar" Also modified tests to reflect this change. --- src/routes/getChapterNames.ts | 3 ++- test/cases/getChapterNames.ts | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/routes/getChapterNames.ts b/src/routes/getChapterNames.ts index c40030a..e78cc84 100644 --- a/src/routes/getChapterNames.ts +++ b/src/routes/getChapterNames.ts @@ -27,10 +27,11 @@ export async function getChapterNames(req: Request, res: Response): Promise= 0.1 GROUP BY "description" ORDER BY SUM("votes"), similarity("description", ?) DESC LIMIT 5;` - , [channelID, description]) as { description: string }[]; + , [channelID, description, description]) as { description: string }[]; if (descriptions?.length > 0) { return res.status(200).json(descriptions.map(d => ({ diff --git a/test/cases/getChapterNames.ts b/test/cases/getChapterNames.ts index 01bb7c5..df386ac 100644 --- a/test/cases/getChapterNames.ts +++ b/test/cases/getChapterNames.ts @@ -16,17 +16,18 @@ describe("getChapterNames", function () { "Weird name", "A different one", "Something else", + "Weirder name", ]; - const nameSearch = (query: string, expected: string): Promise => { + const nameSearch = (query: string, expected: string | null, expectedResults: number): Promise => { const expectedData = [{ description: expected }]; return client.get(`${endpoint}?description=${query}&channelID=${chapterChannelID}`) .then(res => { - assert.strictEqual(res.status, 200); - assert.strictEqual(res.data.length, chapterNames.length); - assert.ok(partialDeepEquals(res.data, expectedData)); + assert.strictEqual(res.status, expectedResults == 0 ? 404 : 200); + assert.strictEqual(res.data.length, expectedResults); + if (expected != null) assert.ok(partialDeepEquals(res.data, expectedData)); }); }; @@ -35,11 +36,13 @@ describe("getChapterNames", function () { await insertChapter(db, chapterNames[0], { videoID: chapterNamesVid1, startTime: 60, endTime: 80 }); await insertChapter(db, chapterNames[1], { videoID: chapterNamesVid1, startTime: 70, endTime: 75 }); await insertChapter(db, chapterNames[2], { videoID: chapterNamesVid1, startTime: 71, endTime: 76 }); + await insertChapter(db, chapterNames[3], { videoID: chapterNamesVid1, startTime: 72, endTime: 77 }); await insertVideoInfo(db, chapterNamesVid1, chapterChannelID); }); - it("Search for 'weird'", () => nameSearch("weird", chapterNames[0])); - it("Search for 'different'", () => nameSearch("different", chapterNames[1])); - it("Search for 'something'", () => nameSearch("something", chapterNames[2])); -}); \ No newline at end of file + it("Search for 'weird' (2 results)", () => nameSearch("weird", chapterNames[0], 2)); + it("Search for 'different' (1 result)", () => nameSearch("different", chapterNames[1], 1)); + it("Search for 'something' (1 result)", () => nameSearch("something", chapterNames[2], 1)); + it("Search for 'unrelated' (0 result)", () => nameSearch("unrelated", null, 0)); +}); From 814ceb56f1e4ba802081d7530c6ff23b4d040631 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:23:44 +0200 Subject: [PATCH 3/6] fix postgres+redis tests made on request https://discord.com/channels/603643120093233162/607338052221665320/1269373542550470730 --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1e5a11f..8407bf6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -64,7 +64,7 @@ jobs: env: PG_USER: ci_db_user PG_PASS: ci_db_pass - run: docker-compose -f docker/docker-compose-ci.yml up -d + run: docker compose -f docker/docker-compose-ci.yml up -d - name: Check running containers run: docker ps - uses: actions/setup-node@v3 From 61dcfeb69ff131b1db9a252dddf69c7d2509eae8 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:10:06 +0200 Subject: [PATCH 4/6] Don't send to #dearrow-locked-titles when downvoting unlocked title voteType passed to sendWebhooks() function to avoid confusion in the future should someone forget about the if statement --- src/routes/postBranding.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index 33128e7..7fa2e6b 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -113,7 +113,7 @@ export async function postBranding(req: Request, res: Response) { await db.prepare("run", `UPDATE "titleVotes" as tv SET "locked" = 0 FROM "titles" t WHERE tv."UUID" = t."UUID" AND tv."UUID" != ? AND t."videoID" = ?`, [UUID, videoID]); } - sendWebhooks(videoID, UUID).catch((e) => Logger.error(e)); + sendWebhooks(videoID, UUID, voteType).catch((e) => Logger.error(e)); } })(), (async () => { if (thumbnail) { @@ -290,7 +290,9 @@ export async function verifyOldSubmissions(hashedUserID: HashedUserID, verificat } } -async function sendWebhooks(videoID: VideoID, UUID: BrandingUUID) { +async function sendWebhooks(videoID: VideoID, UUID: BrandingUUID, voteType: BrandingVoteType) { + if (voteType === BrandingVoteType.Downvote) return; // Don't send messages to dearrow-locked-titles on downvotes + const lockedSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title", "titles"."userID" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."videoID" = ? AND "titles"."UUID" != ? AND "titleVotes"."locked" = 1`, [videoID, UUID]); if (lockedSubmission) { @@ -336,4 +338,4 @@ async function checkForWrongVideoDuration(videoID: VideoID, duration: number): P const apiDuration = apiVideoDetails?.duration; return apiDuration && apiDuration > 2 && duration && duration > 2 && Math.abs(apiDuration - duration) > 3; -} \ No newline at end of file +} From 72086b01957b8ff1fe4b947946a795d9e89d592b Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:38:30 +0200 Subject: [PATCH 5/6] Send webhook messages when a locked title is downvoted also take downvotes & verification into consideration when comparing titles in webhook code --- src/routes/postBranding.ts | 46 ++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index 7fa2e6b..039b788 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -85,6 +85,7 @@ export async function postBranding(req: Request, res: Response) { const existingIsLocked = !!existingUUID && (await db.prepare("get", `SELECT "locked" from "titleVotes" where "UUID" = ?`, [existingUUID]))?.locked; if (existingUUID != undefined && isBanned) return; // ignore votes on existing details from banned users if (downvote && existingIsLocked && !isVip) { + sendWebhooks(videoID, existingUUID, voteType).catch((e) => Logger.error(e)); errorCode = 403; return; } @@ -291,15 +292,14 @@ export async function verifyOldSubmissions(hashedUserID: HashedUserID, verificat } async function sendWebhooks(videoID: VideoID, UUID: BrandingUUID, voteType: BrandingVoteType) { - if (voteType === BrandingVoteType.Downvote) return; // Don't send messages to dearrow-locked-titles on downvotes + const currentSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title", "titleVotes"."locked", "titles"."userID", "titleVotes"."votes"-"titleVotes"."downvotes"+"titleVotes"."verification" AS "score" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."UUID" = ?`, [UUID]); - const lockedSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title", "titles"."userID" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."videoID" = ? AND "titles"."UUID" != ? AND "titleVotes"."locked" = 1`, [videoID, UUID]); - - if (lockedSubmission) { - const currentSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."UUID" = ?`, [UUID]); + // Unlocked title getting more upvotes than the locked one + if (voteType === BrandingVoteType.Upvote) { + const lockedSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title", "titles"."userID", "titleVotes"."votes"-"titleVotes"."downvotes"+"titleVotes"."verification" AS "score" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."videoID" = ? AND "titles"."UUID" != ? AND "titleVotes"."locked" = 1`, [videoID, UUID]); // Time to warn that there may be an issue - if (currentSubmission.votes - lockedSubmission.votes > 2) { + if (lockedSubmission && currentSubmission.score - lockedSubmission.score > 2) { const usernameRow = await db.prepare("get", `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [lockedSubmission.userID]); const data = await getVideoDetails(videoID); @@ -307,7 +307,7 @@ async function sendWebhooks(videoID: VideoID, UUID: BrandingUUID, voteType: Bran "embeds": [{ "title": data?.title, "url": `https://www.youtube.com/watch?v=${videoID}`, - "description": `**${lockedSubmission.votes}** Votes vs **${currentSubmission.votes}**\ + "description": `**${lockedSubmission.score}** score vs **${currentSubmission.score}**\ \n\n**Locked title:** ${lockedSubmission.title}\ \n**New title:** ${currentSubmission.title}\ \n\n**Submitted by:** ${usernameRow?.userName ?? ""}\n${lockedSubmission.userID}`, @@ -331,6 +331,38 @@ async function sendWebhooks(videoID: VideoID, UUID: BrandingUUID, voteType: Bran }); } } + + // Downvotes on locked title + if (voteType === BrandingVoteType.Downvote && currentSubmission.locked === 1) { + const usernameRow = await db.prepare("get", `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [currentSubmission.userID]); + + const data = await getVideoDetails(videoID); + axios.post(config.discordDeArrowLockedWebhookURL, { + "embeds": [{ + "title": data?.title, + "url": `https://www.youtube.com/watch?v=${videoID}`, + "description": `Locked title with **${currentSubmission.score}** score received a downvote\ + \n\n**Locked title:** ${currentSubmission.title}\ + \n**Submitted by:** ${usernameRow?.userName ?? ""}\n${currentSubmission.userID}`, + "color": 10813440, + "thumbnail": { + "url": getMaxResThumbnail(videoID), + }, + }], + }) + .then(res => { + if (res.status >= 400) { + Logger.error("Error sending reported submission Discord hook"); + Logger.error(JSON.stringify((res.data))); + Logger.error("\n"); + } + }) + .catch(err => { + Logger.error("Failed to send reported submission Discord hook."); + Logger.error(JSON.stringify(err)); + Logger.error("\n"); + }); + } } async function checkForWrongVideoDuration(videoID: VideoID, duration: number): Promise { From 380ec8d0caaf792fa5ed42ff6739dfddb38224f8 Mon Sep 17 00:00:00 2001 From: mini-bomba <55105495+mini-bomba@users.noreply.github.com> Date: Sat, 3 Aug 2024 22:01:46 +0200 Subject: [PATCH 6/6] Reformat SQL code in postBranding.ts webhook code --- src/routes/postBranding.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/routes/postBranding.ts b/src/routes/postBranding.ts index 039b788..d45d6d0 100644 --- a/src/routes/postBranding.ts +++ b/src/routes/postBranding.ts @@ -292,11 +292,30 @@ export async function verifyOldSubmissions(hashedUserID: HashedUserID, verificat } async function sendWebhooks(videoID: VideoID, UUID: BrandingUUID, voteType: BrandingVoteType) { - const currentSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title", "titleVotes"."locked", "titles"."userID", "titleVotes"."votes"-"titleVotes"."downvotes"+"titleVotes"."verification" AS "score" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."UUID" = ?`, [UUID]); + const currentSubmission = await db.prepare( + "get", + `SELECT + "titles"."title", + "titleVotes"."locked", + "titles"."userID", + "titleVotes"."votes"-"titleVotes"."downvotes"+"titleVotes"."verification" AS "score" + FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" + WHERE "titles"."UUID" = ?`, + [UUID]); // Unlocked title getting more upvotes than the locked one if (voteType === BrandingVoteType.Upvote) { - const lockedSubmission = await db.prepare("get", `SELECT "titleVotes"."votes", "titles"."title", "titles"."userID", "titleVotes"."votes"-"titleVotes"."downvotes"+"titleVotes"."verification" AS "score" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "titles"."videoID" = ? AND "titles"."UUID" != ? AND "titleVotes"."locked" = 1`, [videoID, UUID]); + const lockedSubmission = await db.prepare( + "get", + `SELECT + "titles"."title", + "titles"."userID", + "titleVotes"."votes"-"titleVotes"."downvotes"+"titleVotes"."verification" AS "score" + FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" + WHERE "titles"."videoID" = ? + AND "titles"."UUID" != ? + AND "titleVotes"."locked" = 1`, + [videoID, UUID]); // Time to warn that there may be an issue if (lockedSubmission && currentSubmission.score - lockedSubmission.score > 2) {