More logging

This commit is contained in:
Ajay
2025-04-21 10:53:49 -04:00
parent 3d596f4528
commit b849328fae
5 changed files with 85 additions and 14 deletions

View File

@@ -47,6 +47,7 @@ addDefaults(config, {
discordMaliciousReportWebhookURL: null, discordMaliciousReportWebhookURL: null,
discordDeArrowLockedWebhookURL: null, discordDeArrowLockedWebhookURL: null,
discordDeArrowWarnedWebhookURL: null, discordDeArrowWarnedWebhookURL: null,
discordNewUserWebhookURL: null,
minReputationToSubmitChapter: 0, minReputationToSubmitChapter: 0,
minReputationToSubmitFiller: 0, minReputationToSubmitFiller: 0,
getTopUsersCacheTimeMinutes: 240, getTopUsersCacheTimeMinutes: 240,

View File

@@ -60,10 +60,36 @@ export async function postBranding(req: Request, res: Response) {
const permission = await canSubmitDeArrow(hashedUserID); const permission = await canSubmitDeArrow(hashedUserID);
if (!permission.canSubmit) { if (!permission.canSubmit) {
Logger.warn(`New user trying to submit dearrow: ${userID} ${videoID} ${title} ${req.headers["user-agent"]}`); Logger.warn(`New user trying to submit dearrow: ${userID} ${videoID} ${videoDuration} ${title} ${req.headers["user-agent"]}`);
res.status(403).send(permission.reason); res.status(403).send(permission.reason);
return; return;
} else if (permission.newUser && config.discordNewUserWebhookURL) {
axios.post(config.discordNewUserWebhookURL, {
"embeds": [{
"title": userID,
"url": `https://www.youtube.com/watch?v=${videoID}`,
"description": `**User Agent**: ${userAgent}\
\n\n**Real User Agent**: ${req.headers["user-agent"]}\
\n**Video Duration**: ${videoDuration}`,
"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");
});
} }
if (videoDuration && thumbnail && await checkForWrongVideoDuration(videoID, videoDuration)) { if (videoDuration && thumbnail && await checkForWrongVideoDuration(videoID, videoDuration)) {

View File

@@ -513,6 +513,32 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
if (!permission.canSubmit) { if (!permission.canSubmit) {
Logger.warn(`New user trying to submit: ${userID} ${videoID} ${videoDurationParam} ${userAgent} ${req.headers["user-agent"]}`); Logger.warn(`New user trying to submit: ${userID} ${videoID} ${videoDurationParam} ${userAgent} ${req.headers["user-agent"]}`);
return res.status(403).send(permission.reason); return res.status(403).send(permission.reason);
} else if (permission.newUser && config.discordNewUserWebhookURL) {
axios.post(config.discordNewUserWebhookURL, {
"embeds": [{
"title": userID,
"url": `https://www.youtube.com/watch?v=${videoID}`,
"description": `**User Agent**: ${userAgent}\
\n\n**Real User Agent**: ${req.headers["user-agent"]}\
\n**Video Duration**: ${videoDurationParam}`,
"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");
});
} }
const invalidCheckResult = await checkInvalidFields(videoID, paramUserID, userID, segments, videoDurationParam, userAgent, service); const invalidCheckResult = await checkInvalidFields(videoID, paramUserID, userID, segments, videoDurationParam, userAgent, service);

View File

@@ -55,6 +55,7 @@ export interface SBSConfig {
discordMaliciousReportWebhookURL?: string; discordMaliciousReportWebhookURL?: string;
discordDeArrowLockedWebhookURL?: string, discordDeArrowLockedWebhookURL?: string,
discordDeArrowWarnedWebhookURL?: string, discordDeArrowWarnedWebhookURL?: string,
discordNewUserWebhookURL?: string;
neuralBlockURL?: string; neuralBlockURL?: string;
discordNeuralBlockRejectWebhookURL?: string; discordNeuralBlockRejectWebhookURL?: string;
minReputationToSubmitChapter: number; minReputationToSubmitChapter: number;

View File

@@ -9,11 +9,22 @@ import redis from "./redis";
import { getReputation } from "./reputation"; import { getReputation } from "./reputation";
import { getServerConfig } from "./serverConfig"; import { getServerConfig } from "./serverConfig";
interface OldSubmitterResult {
canSubmit: boolean;
newUser: boolean;
}
interface CanSubmitResult { interface CanSubmitResult {
canSubmit: boolean; canSubmit: boolean;
reason: string; reason: string;
} }
interface CanSubmitGlobalResult {
canSubmit: boolean;
newUser: boolean;
reason: string;
}
async function lowDownvotes(userID: HashedUserID): Promise<boolean> { async function lowDownvotes(userID: HashedUserID): Promise<boolean> {
const result = await db.prepare("get", `SELECT count(*) as "submissionCount", SUM(CASE WHEN "votes" < 0 AND "views" > 5 THEN 1 ELSE 0 END) AS "downvotedSubmissions" FROM "sponsorTimes" WHERE "userID" = ?` const result = await db.prepare("get", `SELECT count(*) as "submissionCount", SUM(CASE WHEN "votes" < 0 AND "views" > 5 THEN 1 ELSE 0 END) AS "downvotedSubmissions" FROM "sponsorTimes" WHERE "userID" = ?`
, [userID], { useReplica: true }); , [userID], { useReplica: true });
@@ -22,11 +33,11 @@ async function lowDownvotes(userID: HashedUserID): Promise<boolean> {
} }
const fiveMinutes = 5 * 60 * 1000; const fiveMinutes = 5 * 60 * 1000;
async function oldSubmitterOrAllowed(userID: HashedUserID): Promise<boolean> { async function oldSubmitterOrAllowed(userID: HashedUserID): Promise<OldSubmitterResult> {
const submitterThreshold = await getServerConfig("old-submitter-block-date"); const submitterThreshold = await getServerConfig("old-submitter-block-date");
const maxUsers = await getServerConfig("max-users-per-minute"); const maxUsers = await getServerConfig("max-users-per-minute");
if (!submitterThreshold && !maxUsers) { if (!submitterThreshold && !maxUsers) {
return true; return { canSubmit: true, newUser: false };
} }
const result = await db.prepare("get", `SELECT count(*) as "submissionCount" FROM "sponsorTimes" WHERE "userID" = ? AND "shadowHidden" = 0 AND "votes" >= 0 AND "timeSubmitted" < ?` const result = await db.prepare("get", `SELECT count(*) as "submissionCount" FROM "sponsorTimes" WHERE "userID" = ? AND "shadowHidden" = 0 AND "votes" >= 0 AND "timeSubmitted" < ?`
@@ -39,18 +50,18 @@ async function oldSubmitterOrAllowed(userID: HashedUserID): Promise<boolean> {
if (maxUsers && last5MinUsers < parseInt(maxUsers)) { if (maxUsers && last5MinUsers < parseInt(maxUsers)) {
await redis.zAdd("submitters", { score: Date.now(), value: userID }); await redis.zAdd("submitters", { score: Date.now(), value: userID });
return true; return { canSubmit: true, newUser: true };
} }
} }
return isOldSubmitter; return { canSubmit: isOldSubmitter, newUser: false };
} }
async function oldDeArrowSubmitterOrAllowed(userID: HashedUserID): Promise<boolean> { async function oldDeArrowSubmitterOrAllowed(userID: HashedUserID): Promise<OldSubmitterResult> {
const submitterThreshold = await getServerConfig("old-submitter-block-date"); const submitterThreshold = await getServerConfig("old-submitter-block-date");
const maxUsers = await getServerConfig("max-users-per-minute-dearrow"); const maxUsers = await getServerConfig("max-users-per-minute-dearrow");
if (!submitterThreshold && !maxUsers) { if (!submitterThreshold && !maxUsers) {
return true; return { canSubmit: true, newUser: false };
} }
const result = await db.prepare("get", `SELECT count(*) as "submissionCount" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "userID" = ? AND "shadowHidden" = 0 AND "votes" >= 0 AND "timeSubmitted" < ?` const result = await db.prepare("get", `SELECT count(*) as "submissionCount" FROM "titles" JOIN "titleVotes" ON "titles"."UUID" = "titleVotes"."UUID" WHERE "userID" = ? AND "shadowHidden" = 0 AND "votes" >= 0 AND "timeSubmitted" < ?`
@@ -62,7 +73,7 @@ async function oldDeArrowSubmitterOrAllowed(userID: HashedUserID): Promise<boole
const voteResult = await privateDB.prepare("get", `SELECT "UUID" from "titleVotes" where "userID" = ?`, [userID], { useReplica: true }); const voteResult = await privateDB.prepare("get", `SELECT "UUID" from "titleVotes" where "userID" = ?`, [userID], { useReplica: true });
if (voteResult?.UUID) { if (voteResult?.UUID) {
// Count at least one vote as an old submitter as well // Count at least one vote as an old submitter as well
return true; return { canSubmit: true, newUser: false };
} }
} }
@@ -71,11 +82,11 @@ async function oldDeArrowSubmitterOrAllowed(userID: HashedUserID): Promise<boole
if (maxUsers && last5MinUsers < parseInt(maxUsers)) { if (maxUsers && last5MinUsers < parseInt(maxUsers)) {
await redis.zAdd("submittersDeArrow", { score: Date.now(), value: userID }); await redis.zAdd("submittersDeArrow", { score: Date.now(), value: userID });
return true; return { canSubmit: true, newUser: true };
} }
} }
return isOldSubmitter; return { canSubmit: isOldSubmitter, newUser: false };
} }
export async function canSubmit(userID: HashedUserID, category: Category): Promise<CanSubmitResult> { export async function canSubmit(userID: HashedUserID, category: Category): Promise<CanSubmitResult> {
@@ -97,20 +108,26 @@ export async function canSubmit(userID: HashedUserID, category: Category): Promi
} }
} }
export async function canSubmitGlobal(userID: HashedUserID): Promise<CanSubmitResult> { export async function canSubmitGlobal(userID: HashedUserID): Promise<CanSubmitGlobalResult> {
const oldSubmitterOrAllowedPromise = oldSubmitterOrAllowed(userID);
return { return {
canSubmit: await oneOf([isUserVIP(userID), canSubmit: await oneOf([isUserVIP(userID),
oldSubmitterOrAllowed(userID) (async () => (await oldSubmitterOrAllowedPromise).canSubmit)()
]), ]),
newUser: (await oldSubmitterOrAllowedPromise).newUser,
reason: "We are currently experiencing a mass spam attack, we are restricting submissions for now" reason: "We are currently experiencing a mass spam attack, we are restricting submissions for now"
}; };
} }
export async function canSubmitDeArrow(userID: HashedUserID): Promise<CanSubmitResult> { export async function canSubmitDeArrow(userID: HashedUserID): Promise<CanSubmitGlobalResult> {
const oldSubmitterOrAllowedPromise = oldDeArrowSubmitterOrAllowed(userID);
return { return {
canSubmit: await oneOf([isUserVIP(userID), canSubmit: await oneOf([isUserVIP(userID),
oldDeArrowSubmitterOrAllowed(userID) (async () => (await oldSubmitterOrAllowedPromise).canSubmit)()
]), ]),
newUser: (await oldSubmitterOrAllowedPromise).newUser,
reason: "We are currently experiencing a mass spam attack, we are restricting submissions for now" reason: "We are currently experiencing a mass spam attack, we are restricting submissions for now"
}; };
} }