Merge pull request #438 from mchangrh/tempVIP

channel-specific VIP
This commit is contained in:
Ajay Ramachandran
2021-12-31 14:02:05 -05:00
committed by GitHub
11 changed files with 308 additions and 18 deletions

View File

@@ -0,0 +1,71 @@
import { VideoID } from "../types/segments.model";
import { YouTubeAPI } from "../utils/youtubeApi";
import { APIVideoInfo } from "../types/youtubeApi.model";
import { config } from "../config";
import { getHashCache } from "../utils/getHashCache";
import { privateDB } from "../databases/databases";
import { Request, Response } from "express";
import { isUserVIP } from "../utils/isUserVIP";
import { HashedUserID } from "../types/user.model";
import redis from "../utils/redis";
import { tempVIPKey } from "../utils/redisKeys";
interface AddUserAsTempVIPRequest extends Request {
query: {
userID: HashedUserID;
adminUserID: string;
enabled: string;
channelVideoID: string;
}
}
function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<APIVideoInfo> {
return (config.newLeafURLs) ? YouTubeAPI.listVideos(videoID, ignoreCache) : null;
}
const getChannelInfo = async (videoID: VideoID): Promise<{id: string | null, name: string | null }> => {
const videoInfo = await getYouTubeVideoInfo(videoID);
return {
id: videoInfo?.data?.authorId,
name: videoInfo?.data?.author
};
};
export async function addUserAsTempVIP(req: AddUserAsTempVIPRequest, res: Response): Promise<Response> {
const { query: { userID, adminUserID } } = req;
const enabled = req.query?.enabled === "true";
const channelVideoID = req.query?.channelVideoID as VideoID;
if (!userID || !adminUserID || !channelVideoID ) {
// invalid request
return res.sendStatus(400);
}
// hash the issuer userID
const issuerUserID = await getHashCache(adminUserID);
// check if issuer is VIP
const issuerIsVIP = await isUserVIP(issuerUserID as HashedUserID);
if (!issuerIsVIP) {
return res.sendStatus(403);
}
// check to see if this user is already a vip
const targetIsVIP = await isUserVIP(userID);
if (targetIsVIP) {
return res.sendStatus(409);
}
const startTime = Date.now();
const dayInSeconds = 86400;
const channelInfo = await getChannelInfo(channelVideoID);
await privateDB.prepare("run", `INSERT INTO "tempVipLog" VALUES (?, ?, ?, ?)`, [adminUserID, userID, + enabled, startTime]);
if (enabled) { // add to redis
await redis.setAsyncEx(tempVIPKey(userID), channelInfo?.id, dayInSeconds);
} else { // delete key
await redis.delAsync(tempVIPKey(userID));
}
return res.sendStatus(200);
}

View File

@@ -9,11 +9,13 @@ import { getFormattedTime } from "../utils/getFormattedTime";
import { getIP } from "../utils/getIP";
import { getHashCache } from "../utils/getHashCache";
import { config } from "../config";
import { UserID } from "../types/user.model";
import { HashedUserID, UserID } from "../types/user.model";
import { Category, CategoryActionType, HashedIP, IPAddress, SegmentUUID, Service, VideoID, VideoIDHash, Visibility, VideoDuration } from "../types/segments.model";
import { getCategoryActionType } from "../utils/categoryInfo";
import { QueryCacher } from "../utils/queryCacher";
import axios from "axios";
import redis from "../utils/redis";
import { tempVIPKey } from "../utils/redisKeys";
const voteTypes = {
normal: 0,
@@ -37,6 +39,7 @@ interface VoteData {
UUID: string;
nonAnonUserID: string;
voteTypeEnum: number;
isTempVIP: boolean;
isVIP: boolean;
isOwnSubmission: boolean;
row: {
@@ -57,6 +60,13 @@ function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promise<API
}
}
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 ? false : (reply == channelID);
};
const videoDurationChanged = (segmentDuration: number, APIDuration: number) => (APIDuration > 0 && Math.abs(segmentDuration - APIDuration) > 2);
async function checkVideoDurationChange(UUID: SegmentUUID) {
@@ -105,7 +115,7 @@ async function sendWebhooks(voteData: VoteData) {
// Send custom webhooks
dispatchEvent(isUpvote ? "vote.up" : "vote.down", {
"user": {
"status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission),
"status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, voteData.isTempVIP, voteData.isVIP, voteData.isOwnSubmission),
},
"video": {
"id": submissionInfoRow.videoID,
@@ -153,7 +163,7 @@ async function sendWebhooks(voteData: VoteData) {
"author": {
"name": voteData.finalResponse?.webhookMessage ??
voteData.finalResponse?.finalMessage ??
getVoteAuthor(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission),
getVoteAuthor(userSubmissionCountRow.submissionCount, voteData.isTempVIP, voteData.isVIP, voteData.isOwnSubmission),
},
"thumbnail": {
"url": getMaxResThumbnail(data) || "",
@@ -311,7 +321,9 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise<Re
const hashedIP: HashedIP = await getHashCache((ip + config.globalSalt) as IPAddress);
//check if this user is on the vip list
const isVIP = await isUserVIP(nonAnonUserID);
const videoID = await db.prepare("get", `select "videoID" from "sponsorTimes" where "UUID" = ?`, [UUID]);
const isTempVIP = await isUserTempVIP(nonAnonUserID, videoID?.videoID || null);
const isVIP = await isUserVIP(nonAnonUserID) || isTempVIP;
//check if user voting on own submission
const isOwnSubmission = (await db.prepare("get", `SELECT "UUID" as "submissionCount" FROM "sponsorTimes" where "userID" = ? AND "UUID" = ?`, [nonAnonUserID, UUID])) !== undefined;
@@ -480,6 +492,7 @@ export async function voteOnSponsorTime(req: Request, res: Response): Promise<Re
UUID,
nonAnonUserID,
voteTypeEnum,
isTempVIP,
isVIP,
isOwnSubmission,
row: videoInfo,