add 80% tempVIP

- move isUserTempVIP to own file
- reduce allSegmentDuration instead of forEach
- don't return decreaseVotes from autoModerator
- completely skip autoModCheck if VIP
This commit is contained in:
Michael C
2022-03-31 16:43:10 -04:00
parent 76cc603a3f
commit d02d78f325
3 changed files with 43 additions and 37 deletions

View File

@@ -16,6 +16,7 @@ import { getReputation } from "../utils/reputation";
import { APIVideoData, APIVideoInfo } from "../types/youtubeApi.model"; import { APIVideoData, APIVideoInfo } from "../types/youtubeApi.model";
import { HashedUserID, UserID } from "../types/user.model"; import { HashedUserID, UserID } from "../types/user.model";
import { isUserVIP } from "../utils/isUserVIP"; import { isUserVIP } from "../utils/isUserVIP";
import { isUserTempVIP } from "../utils/isUserTempVIP";
import { parseUserAgent } from "../utils/userAgent"; import { parseUserAgent } from "../utils/userAgent";
import { getService } from "../utils/getService"; import { getService } from "../utils/getService";
import axios from "axios"; import axios from "axios";
@@ -81,19 +82,19 @@ async function sendWebhooks(apiVideoInfo: APIVideoInfo, userID: string, videoID:
if (config.discordFirstTimeSubmissionsWebhookURL === null || userSubmissionCountRow.submissionCount > 1) return; if (config.discordFirstTimeSubmissionsWebhookURL === null || userSubmissionCountRow.submissionCount > 1) return;
axios.post(config.discordFirstTimeSubmissionsWebhookURL, { axios.post(config.discordFirstTimeSubmissionsWebhookURL, {
"embeds": [{ embeds: [{
"title": data?.title, title: data?.title,
"url": `https://www.youtube.com/watch?v=${videoID}&t=${(parseInt(startTime.toFixed(0)) - 2)}s#requiredSegment=${UUID}`, url: `https://www.youtube.com/watch?v=${videoID}&t=${(parseInt(startTime.toFixed(0)) - 2)}s#requiredSegment=${UUID}`,
"description": `Submission ID: ${UUID}\ description: `Submission ID: ${UUID}\
\n\nTimestamp: \ \n\nTimestamp: \
${getFormattedTime(startTime)} to ${getFormattedTime(endTime)}\ ${getFormattedTime(startTime)} to ${getFormattedTime(endTime)}\
\n\nCategory: ${segmentInfo.category}`, \n\nCategory: ${segmentInfo.category}`,
"color": 10813440, color: 10813440,
"author": { author: {
"name": userID, name: userID,
}, },
"thumbnail": { thumbnail: {
"url": getMaxResThumbnail(data) || "", url: getMaxResThumbnail(data) || "",
}, },
}], }],
}) })
@@ -151,9 +152,8 @@ async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
//merge all the times into non-overlapping arrays //merge all the times into non-overlapping arrays
const allSegmentsSorted = mergeTimeSegments(allSegmentTimes.sort((a, b) => a[0] - b[0] || a[1] - b[1])); const allSegmentsSorted = mergeTimeSegments(allSegmentTimes.sort((a, b) => a[0] - b[0] || a[1] - b[1]));
let allSegmentDuration = 0;
//sum all segment times together //sum all segment times together
allSegmentsSorted.forEach(segmentInfo => allSegmentDuration += segmentInfo[1] - segmentInfo[0]); const allSegmentDuration = allSegmentsSorted.reduce((acc, curr) => acc + (curr[1] - curr[0]), 0);
if (allSegmentDuration > (duration / 100) * 80) { if (allSegmentDuration > (duration / 100) * 80) {
// Reject submission if all segments combine are over 80% of the video // Reject submission if all segments combine are over 80% of the video
@@ -325,24 +325,20 @@ async function checkEachSegmentValid(rawIP: IPAddress, paramUserID: UserID, user
return CHECK_PASS; return CHECK_PASS;
} }
async function checkByAutoModerator(videoID: any, userID: any, segments: Array<any>, isVIP: boolean, service:string, apiVideoInfo: APIVideoInfo, decreaseVotes: number, videoDuration: number): Promise<CheckResult & { decreaseVotes: number; } > { async function checkByAutoModerator(videoID: any, userID: any, segments: Array<any>, service:string, apiVideoInfo: APIVideoInfo, videoDuration: number): Promise<CheckResult> {
// Auto moderator check // Auto moderator check
if (!isVIP && service == Service.YouTube) { if (service == Service.YouTube) {
const autoModerateResult = await autoModerateSubmission(apiVideoInfo, { userID, videoID, segments, service, videoDuration });//startTime, endTime, category: segments[i].category}); const autoModerateResult = await autoModerateSubmission(apiVideoInfo, { userID, videoID, segments, service, videoDuration });//startTime, endTime, category: segments[i].category});
if (autoModerateResult) { if (autoModerateResult) {
return { return {
pass: false, pass: false,
errorCode: 403, errorCode: 403,
errorMessage: `Request rejected by auto moderator: ${autoModerateResult} If this is an issue, send a message on Discord.`, errorMessage: `Request rejected by auto moderator: ${autoModerateResult} If this is an issue, send a message on Discord.`
decreaseVotes
}; };
} }
} }
return { return CHECK_PASS;
...CHECK_PASS,
decreaseVotes
};
} }
async function updateDataIfVideoDurationChange(videoID: VideoID, service: Service, videoDuration: VideoDuration, videoDurationParam: VideoDuration) { async function updateDataIfVideoDurationChange(videoID: VideoID, service: Service, videoDuration: VideoDuration, videoDurationParam: VideoDuration) {
@@ -498,6 +494,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
} }
const isVIP = await isUserVIP(userID); const isVIP = await isUserVIP(userID);
const isTempVIP = await isUserTempVIP(userID, videoID);
const rawIP = getIP(req); const rawIP = getIP(req);
const newData = await updateDataIfVideoDurationChange(videoID, service, videoDuration, videoDurationParam); const newData = await updateDataIfVideoDurationChange(videoID, service, videoDuration, videoDurationParam);
@@ -510,12 +507,11 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
return res.status(segmentCheckResult.errorCode).send(segmentCheckResult.errorMessage); return res.status(segmentCheckResult.errorCode).send(segmentCheckResult.errorMessage);
} }
let decreaseVotes = 0; if (!isVIP || !isTempVIP) {
const autoModerateCheckResult = await checkByAutoModerator(videoID, userID, segments, isVIP, service, apiVideoInfo, decreaseVotes, videoDurationParam); const autoModerateCheckResult = await checkByAutoModerator(videoID, userID, segments, service, apiVideoInfo, videoDurationParam);
if (!autoModerateCheckResult.pass) { if (!autoModerateCheckResult.pass) {
return res.status(autoModerateCheckResult.errorCode).send(autoModerateCheckResult.errorMessage); return res.status(autoModerateCheckResult.errorCode).send(autoModerateCheckResult.errorMessage);
} else { }
decreaseVotes = autoModerateCheckResult.decreaseVotes;
} }
// Will be filled when submitting // Will be filled when submitting
@@ -536,7 +532,7 @@ export async function postSkipSegments(req: Request, res: Response): Promise<Res
//check to see if this user is shadowbanned //check to see if this user is shadowbanned
const shadowBanRow = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedUsers" WHERE "userID" = ? LIMIT 1`, [userID]); const shadowBanRow = await db.prepare("get", `SELECT count(*) as "userCount" FROM "shadowBannedUsers" WHERE "userID" = ? LIMIT 1`, [userID]);
const startingVotes = 0 + decreaseVotes; const startingVotes = 0;
const reputation = await getReputation(userID); const reputation = await getReputation(userID);
for (const segmentInfo of segments) { for (const segmentInfo of segments) {

View File

@@ -1,6 +1,7 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import { Logger } from "../utils/logger"; import { Logger } from "../utils/logger";
import { isUserVIP } from "../utils/isUserVIP"; import { isUserVIP } from "../utils/isUserVIP";
import { isUserTempVIP } from "../utils/isUserTempVIP";
import { getMaxResThumbnail, YouTubeAPI } from "../utils/youtubeApi"; import { getMaxResThumbnail, YouTubeAPI } from "../utils/youtubeApi";
import { APIVideoInfo } from "../types/youtubeApi.model"; import { APIVideoInfo } from "../types/youtubeApi.model";
import { db, privateDB } from "../databases/databases"; import { db, privateDB } from "../databases/databases";
@@ -9,12 +10,10 @@ import { getFormattedTime } from "../utils/getFormattedTime";
import { getIP } from "../utils/getIP"; import { getIP } from "../utils/getIP";
import { getHashCache } from "../utils/getHashCache"; import { getHashCache } from "../utils/getHashCache";
import { config } from "../config"; import { config } from "../config";
import { HashedUserID, UserID } from "../types/user.model"; import { UserID } from "../types/user.model";
import { DBSegment, Category, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash, VideoDuration, ActionType } from "../types/segments.model"; import { DBSegment, Category, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash, VideoDuration, ActionType } from "../types/segments.model";
import { QueryCacher } from "../utils/queryCacher"; import { QueryCacher } from "../utils/queryCacher";
import axios from "axios"; import axios from "axios";
import redis from "../utils/redis";
import { tempVIPKey } from "../utils/redisKeys";
const voteTypes = { const voteTypes = {
normal: 0, normal: 0,
@@ -55,14 +54,6 @@ function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<API
return config.newLeafURLs ? YouTubeAPI.listVideos(videoID, ignoreCache) : null; return config.newLeafURLs ? YouTubeAPI.listVideos(videoID, ignoreCache) : null;
} }
const isUserTempVIP = async (nonAnonUserID: HashedUserID, videoID: VideoID): Promise<boolean> => {
const apiVideoInfo = await getYouTubeVideoInfo(videoID);
const channelID = apiVideoInfo?.data?.authorId;
const { err, reply } = await redis.getAsync(tempVIPKey(nonAnonUserID));
return err || !reply ? false : (reply == channelID);
};
const videoDurationChanged = (segmentDuration: number, APIDuration: number) => (APIDuration > 0 && Math.abs(segmentDuration - APIDuration) > 2); const videoDurationChanged = (segmentDuration: number, APIDuration: number) => (APIDuration > 0 && Math.abs(segmentDuration - APIDuration) > 2);
async function updateSegmentVideoDuration(UUID: SegmentUUID) { async function updateSegmentVideoDuration(UUID: SegmentUUID) {

View File

@@ -0,0 +1,19 @@
import redis from "../utils/redis";
import { tempVIPKey } from "../utils/redisKeys";
import { HashedUserID } from "../types/user.model";
import { YouTubeAPI } from "../utils/youtubeApi";
import { APIVideoInfo } from "../types/youtubeApi.model";
import { VideoID } from "../types/segments.model";
import { config } from "../config";
function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<APIVideoInfo> {
return config.newLeafURLs ? YouTubeAPI.listVideos(videoID, ignoreCache) : null;
}
export const isUserTempVIP = async (hashedUserID: HashedUserID, videoID: VideoID): Promise<boolean> => {
const apiVideoInfo = await getYouTubeVideoInfo(videoID);
const channelID = apiVideoInfo?.data?.authorId;
const { err, reply } = await redis.getAsync(tempVIPKey(hashedUserID));
return err || !reply ? false : (reply == channelID);
};