mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2026-02-01 15:21:07 +03:00
fix eslint-errors
This commit is contained in:
@@ -34,13 +34,14 @@ import { addUnlistedVideo } from './routes/addUnlistedVideo';
|
||||
import {postPurgeAllSegments} from './routes/postPurgeAllSegments';
|
||||
import {getUserID} from './routes/getUserID';
|
||||
import ExpressPromiseRouter from 'express-promise-router';
|
||||
import { Server } from 'http';
|
||||
|
||||
export function createServer(callback: () => void) {
|
||||
export function createServer(callback: () => void): Server {
|
||||
// Create a service (the app object is just a callback).
|
||||
const app = express();
|
||||
|
||||
const router = ExpressPromiseRouter()
|
||||
app.use(router)
|
||||
const router = ExpressPromiseRouter();
|
||||
app.use(router);
|
||||
|
||||
//setup CORS correctly
|
||||
router.use(corsMiddleware);
|
||||
@@ -158,7 +159,7 @@ function setupRoutes(router: Router) {
|
||||
if (config.postgres) {
|
||||
router.get('/database', (req, res) => dumpDatabase(req, res, true));
|
||||
router.get('/database.json', (req, res) => dumpDatabase(req, res, false));
|
||||
router.get('/database/*', redirectLink)
|
||||
router.get('/database/*', redirectLink);
|
||||
} else {
|
||||
router.get('/database.db', function (req: Request, res: Response) {
|
||||
res.sendFile("./databases/sponsorTimes.db", {root: "./"});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import {SBSConfig} from "./types/config.model";
|
||||
import packageJson from "../package.json"
|
||||
import packageJson from "../package.json";
|
||||
|
||||
const isTestMode = process.env.npm_lifecycle_script === packageJson.scripts.test;
|
||||
const configFile = process.env.TEST_POSTGRES ? 'ci.json'
|
||||
@@ -83,7 +83,7 @@ addDefaults(config, {
|
||||
// Add defaults
|
||||
function addDefaults(config: SBSConfig, defaults: SBSConfig) {
|
||||
for (const key in defaults) {
|
||||
if (!config.hasOwnProperty(key)) {
|
||||
if (!Object.prototype.hasOwnProperty.call(config, key)) {
|
||||
// @ts-ignore
|
||||
config[key] = defaults[key];
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import {Logger} from '../utils/logger';
|
||||
import {IDatabase, QueryType} from './IDatabase';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import MysqlInterface from 'sync-mysql';
|
||||
|
||||
export class Mysql implements IDatabase {
|
||||
private connection: any;
|
||||
|
||||
constructor(private config: any) {
|
||||
constructor(private config: unknown) {
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
this.connection = new MysqlInterface(this.config);
|
||||
}
|
||||
|
||||
prepare(type: QueryType, query: string, params?: any[]) {
|
||||
prepare(type: QueryType, query: string, params?: any[]): Promise<any[]> {
|
||||
Logger.debug(`prepare (mysql): type: ${type}, query: ${query}, params: ${params}`);
|
||||
const queryResult = this.connection.query(query, params);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ types.setTypeParser(20, function(val) {
|
||||
export class Postgres implements IDatabase {
|
||||
private pool: Pool;
|
||||
|
||||
constructor(private config: any) {}
|
||||
constructor(private config: Record<string, any>) {}
|
||||
|
||||
async init(): Promise<void> {
|
||||
this.pool = new Pool(this.config.postgres);
|
||||
@@ -43,7 +43,7 @@ export class Postgres implements IDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
async prepare(type: QueryType, query: string, params?: any[]) {
|
||||
async prepare(type: QueryType, query: string, params?: any[]): Promise<any[]> {
|
||||
// Convert query to use numbered parameters
|
||||
let count = 1;
|
||||
for (let char = 0; char < query.length; char++) {
|
||||
@@ -64,7 +64,7 @@ export class Postgres implements IDatabase {
|
||||
return value;
|
||||
}
|
||||
case 'all': {
|
||||
let values = queryResult.rows;
|
||||
const values = queryResult.rows;
|
||||
Logger.debug(`result (postgres): ${values}`);
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export class Sqlite implements IDatabase {
|
||||
{
|
||||
}
|
||||
|
||||
async prepare(type: QueryType, query: string, params: any[] = []) {
|
||||
async prepare(type: QueryType, query: string, params: any[] = []): Promise<any[]> {
|
||||
// Logger.debug(`prepare (sqlite): type: ${type}, query: ${query}, params: ${params}`);
|
||||
const preparedQuery = this.db.prepare(query);
|
||||
|
||||
@@ -30,7 +30,7 @@ export class Sqlite implements IDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
async init() {
|
||||
async init(): Promise<void> {
|
||||
// Make dirs if required
|
||||
if (!fs.existsSync(path.join(this.config.dbPath, "../"))) {
|
||||
fs.mkdirSync(path.join(this.config.dbPath, "../"));
|
||||
@@ -61,7 +61,7 @@ export class Sqlite implements IDatabase {
|
||||
this.db.exec("pragma mmap_size= 500000000;");
|
||||
}
|
||||
|
||||
attachDatabase(database: string, attachAs: string) {
|
||||
attachDatabase(database: string, attachAs: string): void {
|
||||
this.db.prepare(`ATTACH ? as ${attachAs}`).run(database);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export class Sqlite implements IDatabase {
|
||||
}
|
||||
|
||||
private static processUpgradeQuery(query: string): string {
|
||||
let result = query.replace(/^.*--!sqlite-ignore/gm, "");
|
||||
const result = query.replace(/^.*--!sqlite-ignore/gm, "");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ if (config.mysql) {
|
||||
enableWalCheckpointNumber: false
|
||||
});
|
||||
}
|
||||
async function initDb() {
|
||||
async function initDb(): Promise<void> {
|
||||
await db.init();
|
||||
await privateDB.init();
|
||||
|
||||
@@ -74,4 +74,4 @@ export {
|
||||
db,
|
||||
privateDB,
|
||||
initDb,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
|
||||
export function apiCspMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||
export function apiCspMiddleware(req: Request, res: Response, next: NextFunction): void {
|
||||
res.header("Content-Security-Policy", "script-src 'none'; object-src 'none'");
|
||||
next();
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
|
||||
export function corsMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||
export function corsMiddleware(req: Request, res: Response, next: NextFunction): void {
|
||||
res.header("Access-Control-Allow-Origin", "*");
|
||||
res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept");
|
||||
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE")
|
||||
res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE");
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Logger} from '../utils/logger';
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
|
||||
export function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||
export function loggerMiddleware(req: Request, res: Response, next: NextFunction): void {
|
||||
Logger.info(`Request received: ${req.method} ${req.url}`);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {getIP} from '../utils/getIP';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
|
||||
export function userCounter(req: Request, res: Response, next: NextFunction) {
|
||||
export function userCounter(req: Request, res: Response, next: NextFunction): void {
|
||||
fetch(config.userCounterURL + "/api/v1/addIP?hashedIP=" + getHash(getIP(req), 1), {method: "POST"})
|
||||
.catch(() => Logger.debug("Failing to connect to user counter at: " + config.userCounterURL));
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { time } from 'console';
|
||||
import {Request, Response} from 'express';
|
||||
import { db } from '../databases/databases';
|
||||
import { Logger } from '../utils/logger';
|
||||
@@ -10,7 +9,7 @@ import { Logger } from '../utils/logger';
|
||||
* https://support.google.com/youtube/answer/9230970
|
||||
*/
|
||||
|
||||
export function addUnlistedVideo(req: Request, res: Response) {
|
||||
export function addUnlistedVideo(req: Request, res: Response): void {
|
||||
const videoID = req.body.videoID;
|
||||
const year = req.body.year || 0;
|
||||
const views = req.body.views || 0;
|
||||
@@ -27,7 +26,6 @@ export function addUnlistedVideo(req: Request, res: Response) {
|
||||
} catch (err) {
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {db} from '../databases/databases';
|
||||
import {config} from '../config';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function addUserAsVIP(req: Request, res: Response) {
|
||||
export async function addUserAsVIP(req: Request, res: Response): Promise<void> {
|
||||
const userID = req.query.userID as string;
|
||||
let adminUserIDInput = req.query.adminUserID as string;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {db} from '../databases/databases';
|
||||
import { Category, VideoID } from '../types/segments.model';
|
||||
import { UserID } from '../types/user.model';
|
||||
|
||||
export async function deleteLockCategoriesEndpoint(req: Request, res: Response) {
|
||||
export async function deleteLockCategoriesEndpoint(req: Request, res: Response): Promise<void> {
|
||||
// Collect user input data
|
||||
const videoID = req.body.videoID as VideoID;
|
||||
const userID = req.body.userID as UserID;
|
||||
|
||||
@@ -23,7 +23,7 @@ const styleHeader = `<style>
|
||||
table tbody tr:nth-child(odd) {
|
||||
background: #efefef;
|
||||
}
|
||||
</style>`
|
||||
</style>`;
|
||||
|
||||
const licenseHeader = `<p>The API and database follow <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="nofollow">CC BY-NC-SA 4.0</a> unless you have explicit permission.</p>
|
||||
<p><a href="https://gist.github.com/ajayyy/4b27dfc66e33941a45aeaadccb51de71">Attribution Template</a></p>
|
||||
@@ -38,13 +38,13 @@ const tableNames = tables.map(table => table.name);
|
||||
interface TableDumpList {
|
||||
fileName: string;
|
||||
tableName: string;
|
||||
};
|
||||
}
|
||||
let latestDumpFiles: TableDumpList[] = [];
|
||||
|
||||
interface TableFile {
|
||||
file: string,
|
||||
timestamp: number
|
||||
};
|
||||
}
|
||||
|
||||
if (tables.length === 0) {
|
||||
Logger.warn('[dumpDatabase] No tables configured');
|
||||
@@ -55,7 +55,7 @@ let updateQueued = false;
|
||||
let updateRunning = false;
|
||||
|
||||
function removeOutdatedDumps(exportPath: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
// Get list of table names
|
||||
// Create array for each table
|
||||
const tableFiles: Record<string, TableFile[]> = tableNames.reduce((obj: any, tableName) => {
|
||||
@@ -81,7 +81,7 @@ function removeOutdatedDumps(exportPath: string): Promise<void> {
|
||||
});
|
||||
});
|
||||
|
||||
for (let tableName in tableFiles) {
|
||||
for (const tableName in tableFiles) {
|
||||
const files = tableFiles[tableName].sort((a, b) => b.timestamp - a.timestamp);
|
||||
for (let i = 2; i < files.length; i++) {
|
||||
// remove old file
|
||||
@@ -90,13 +90,12 @@ function removeOutdatedDumps(exportPath: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default async function dumpDatabase(req: Request, res: Response, showPage: boolean) {
|
||||
export default async function dumpDatabase(req: Request, res: Response, showPage: boolean): Promise<void> {
|
||||
if (!config?.dumpDatabase?.enabled) {
|
||||
res.status(404).send("Database dump is disabled");
|
||||
return;
|
||||
@@ -108,7 +107,7 @@ export default async function dumpDatabase(req: Request, res: Response, showPage
|
||||
|
||||
updateQueueTime();
|
||||
|
||||
res.status(200)
|
||||
res.status(200);
|
||||
|
||||
if (showPage) {
|
||||
res.send(`${styleHeader}
|
||||
@@ -153,7 +152,7 @@ export default async function dumpDatabase(req: Request, res: Response, showPage
|
||||
size: item.fileSize,
|
||||
};
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
await queueDump();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {db} from '../databases/databases';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function getDaysSavedFormatted(req: Request, res: Response) {
|
||||
let row = await db.prepare('get', 'SELECT SUM(("endTime" - "startTime") / 60 / 60 / 24 * "views") as "daysSaved" from "sponsorTimes" where "shadowHidden" != 1', []);
|
||||
export async function getDaysSavedFormatted(req: Request, res: Response): Promise<void> {
|
||||
const row = await db.prepare('get', 'SELECT SUM(("endTime" - "startTime") / 60 / 60 / 24 * "views") as "daysSaved" from "sponsorTimes" where "shadowHidden" != 1', []);
|
||||
|
||||
if (row !== undefined) {
|
||||
//send this result
|
||||
|
||||
@@ -17,7 +17,7 @@ export async function getIsUserVIP(req: Request, res: Response): Promise<void> {
|
||||
const hashedUserID: HashedUserID = getHash(userID);
|
||||
|
||||
try {
|
||||
let vipState = await isUserVIP(hashedUserID);
|
||||
const vipState = await isUserVIP(hashedUserID);
|
||||
res.status(200).json({
|
||||
hashedUserID: hashedUserID,
|
||||
vip: vipState,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Logger } from '../utils/logger';
|
||||
|
||||
const maxRewardTimePerSegmentInSeconds = config.maxRewardTimePerSegmentInSeconds ?? 86400;
|
||||
|
||||
export async function getSavedTimeForUser(req: Request, res: Response) {
|
||||
export async function getSavedTimeForUser(req: Request, res: Response): Promise<void> {
|
||||
let userID = req.query.userID as string;
|
||||
|
||||
if (userID == undefined) {
|
||||
@@ -19,7 +19,7 @@ export async function getSavedTimeForUser(req: Request, res: Response) {
|
||||
userID = getHash(userID);
|
||||
|
||||
try {
|
||||
let row = await db.prepare("get", 'SELECT SUM(((CASE WHEN "endTime" - "startTime" > ? THEN ? ELSE "endTime" - "startTime" END) / 60) * "views") as "minutesSaved" FROM "sponsorTimes" WHERE "userID" = ? AND "votes" > -1 AND "shadowHidden" != 1 ', [maxRewardTimePerSegmentInSeconds, maxRewardTimePerSegmentInSeconds, userID]);
|
||||
const row = await db.prepare("get", 'SELECT SUM(((CASE WHEN "endTime" - "startTime" > ? THEN ? ELSE "endTime" - "startTime" END) / 60) * "views") as "minutesSaved" FROM "sponsorTimes" WHERE "userID" = ? AND "votes" > -1 AND "shadowHidden" != 1 ', [maxRewardTimePerSegmentInSeconds, maxRewardTimePerSegmentInSeconds, userID]);
|
||||
|
||||
if (row.minutesSaved != null) {
|
||||
res.send({
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Request, Response } from 'express';
|
||||
import { db } from '../databases/databases';
|
||||
import { DBSegment, SegmentUUID } from "../types/segments.model";
|
||||
|
||||
const isValidSegmentUUID = (str: string): Boolean => /^([a-f0-9]{64}|[a-f0-9]{8}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{4}\-[a-f0-9]{12})/.test(str)
|
||||
const isValidSegmentUUID = (str: string): boolean => /^([a-f0-9]{64}|[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/.test(str);
|
||||
|
||||
async function getSegmentFromDBByUUID(UUID: SegmentUUID): Promise<DBSegment> {
|
||||
try {
|
||||
@@ -18,7 +18,7 @@ async function getSegmentFromDBByUUID(UUID: SegmentUUID): Promise<DBSegment> {
|
||||
|
||||
async function getSegmentsByUUID(UUIDs: SegmentUUID[]): Promise<DBSegment[]> {
|
||||
const DBSegments: DBSegment[] = [];
|
||||
for (let UUID of UUIDs) {
|
||||
for (const UUID of UUIDs) {
|
||||
// if UUID is invalid, skip
|
||||
if (!isValidSegmentUUID(UUID)) continue;
|
||||
DBSegments.push(await getSegmentFromDBByUUID(UUID as SegmentUUID));
|
||||
@@ -26,7 +26,7 @@ async function getSegmentsByUUID(UUIDs: SegmentUUID[]): Promise<DBSegment[]> {
|
||||
return DBSegments;
|
||||
}
|
||||
|
||||
async function handleGetSegmentInfo(req: Request, res: Response) {
|
||||
async function handleGetSegmentInfo(req: Request, res: Response): Promise<DBSegment[]> {
|
||||
// If using params instead of JSON, only one UUID can be pulled
|
||||
let UUIDs = req.query.UUIDs
|
||||
? JSON.parse(req.query.UUIDs as string)
|
||||
@@ -41,18 +41,18 @@ async function handleGetSegmentInfo(req: Request, res: Response) {
|
||||
if (UUIDs.length > 10) UUIDs = UUIDs.slice(0, 10);
|
||||
if (!Array.isArray(UUIDs) || !UUIDs) {
|
||||
res.status(400).send("UUIDs parameter does not match format requirements.");
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
const DBSegments = await getSegmentsByUUID(UUIDs);
|
||||
// all uuids failed lookup
|
||||
if (!DBSegments?.length) {
|
||||
res.sendStatus(400);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
// uuids valid but not found
|
||||
if (DBSegments[0] === null || DBSegments[0] === undefined) {
|
||||
res.sendStatus(400);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
return DBSegments;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { getCategoryActionType } from '../utils/categoryInfo';
|
||||
import { getHash } from '../utils/getHash';
|
||||
import { getIP } from '../utils/getIP';
|
||||
import { Logger } from '../utils/logger';
|
||||
import { QueryCacher } from '../utils/queryCacher'
|
||||
import { QueryCacher } from '../utils/queryCacher';
|
||||
import { getReputation } from '../utils/reputation';
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ async function prepareCategorySegments(req: Request, videoID: VideoID, category:
|
||||
|
||||
const filteredSegments = segments.filter((_, index) => shouldFilter[index]);
|
||||
|
||||
const maxSegments = getCategoryActionType(category) === CategoryActionType.Skippable ? 32 : 1
|
||||
const maxSegments = getCategoryActionType(category) === CategoryActionType.Skippable ? 32 : 1;
|
||||
return (await chooseSegments(filteredSegments, maxSegments)).map((chosenSegment) => ({
|
||||
category,
|
||||
segment: [chosenSegment.startTime, chosenSegment.endTime],
|
||||
@@ -135,7 +135,7 @@ async function getSegmentsFromDBByHash(hashedVideoIDPrefix: VideoIDHash, service
|
||||
) as Promise<DBSegment[]>;
|
||||
|
||||
if (hashedVideoIDPrefix.length === 4) {
|
||||
return await QueryCacher.get(fetchFromDB, skipSegmentsHashKey(hashedVideoIDPrefix, service))
|
||||
return await QueryCacher.get(fetchFromDB, skipSegmentsHashKey(hashedVideoIDPrefix, service));
|
||||
}
|
||||
|
||||
return await fetchFromDB();
|
||||
@@ -150,7 +150,7 @@ async function getSegmentsFromDBByVideoID(videoID: VideoID, service: Service): P
|
||||
[videoID, service]
|
||||
) as Promise<DBSegment[]>;
|
||||
|
||||
return await QueryCacher.get(fetchFromDB, skipSegmentsKey(videoID, service))
|
||||
return await QueryCacher.get(fetchFromDB, skipSegmentsKey(videoID, service));
|
||||
}
|
||||
|
||||
//gets a weighted random choice from the choices array based on their `votes` property.
|
||||
@@ -168,7 +168,7 @@ function getWeightedRandomChoice<T extends VotableObject>(choices: T[], amountOf
|
||||
|
||||
//assign a weight to each choice
|
||||
let totalWeight = 0;
|
||||
let choicesWithWeights: TWithWeight[] = choices.map(choice => {
|
||||
const choicesWithWeights: TWithWeight[] = choices.map(choice => {
|
||||
const boost = Math.min(choice.reputation, 4);
|
||||
|
||||
//The 3 makes -2 the minimum votes before being ignored completely
|
||||
@@ -234,7 +234,7 @@ async function chooseSegments(segments: DBSegment[], max: number): Promise<DBSeg
|
||||
}
|
||||
|
||||
cursor = Math.max(cursor, segment.endTime);
|
||||
};
|
||||
}
|
||||
|
||||
overlappingSegmentsGroups.forEach((group) => {
|
||||
if (group.locked) {
|
||||
|
||||
@@ -3,11 +3,10 @@ import {getSegmentsByHash} from './getSkipSegments';
|
||||
import {Request, Response} from 'express';
|
||||
import { Category, Service, VideoIDHash } from '../types/segments.model';
|
||||
|
||||
export async function getSkipSegmentsByHash(req: Request, res: Response) {
|
||||
export async function getSkipSegmentsByHash(req: Request, res: Response): Promise<Response> {
|
||||
let hashPrefix = req.params.prefix as VideoIDHash;
|
||||
if (!hashPrefixTester(req.params.prefix)) {
|
||||
res.status(400).send("Hash prefix does not match format requirements."); // Exit early on faulty prefix
|
||||
return;
|
||||
return res.status(400).send("Hash prefix does not match format requirements."); // Exit early on faulty prefix
|
||||
}
|
||||
hashPrefix = hashPrefix.toLowerCase() as VideoIDHash;
|
||||
|
||||
@@ -44,6 +43,5 @@ export async function getSkipSegmentsByHash(req: Request, res: Response) {
|
||||
hash: data.hash,
|
||||
segments: data.segments,
|
||||
}));
|
||||
|
||||
res.status(output.length === 0 ? 404 : 200).json(output);
|
||||
return res.status(output.length === 0 ? 404 : 200).json(output);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ const MILLISECONDS_IN_MINUTE = 60000;
|
||||
const getTopUsersWithCache = createMemoryCache(generateTopUsersStats, config.getTopUsersCacheTimeMinutes * MILLISECONDS_IN_MINUTE);
|
||||
const maxRewardTimePerSegmentInSeconds = config.maxRewardTimePerSegmentInSeconds ?? 86400;
|
||||
|
||||
async function generateTopUsersStats(sortBy: string, categoryStatsEnabled: boolean = false) {
|
||||
async function generateTopUsersStats(sortBy: string, categoryStatsEnabled = false) {
|
||||
const userNames = [];
|
||||
const viewCounts = [];
|
||||
const totalSubmissions = [];
|
||||
@@ -63,7 +63,7 @@ async function generateTopUsersStats(sortBy: string, categoryStatsEnabled: boole
|
||||
};
|
||||
}
|
||||
|
||||
export async function getTopUsers(req: Request, res: Response) {
|
||||
export async function getTopUsers(req: Request, res: Response): Promise<Response> {
|
||||
const sortType = parseInt(req.query.sortType as string);
|
||||
const categoryStatsEnabled = req.query.categoryStats;
|
||||
|
||||
@@ -89,5 +89,5 @@ export async function getTopUsers(req: Request, res: Response) {
|
||||
const stats = await getTopUsersWithCache(sortBy, categoryStatsEnabled);
|
||||
|
||||
//send this result
|
||||
res.send(stats);
|
||||
return res.send(stats);
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ let apiUsersCache = 0;
|
||||
|
||||
let lastUserCountCheck = 0;
|
||||
|
||||
export async function getTotalStats(req: Request, res: Response) {
|
||||
export async function getTotalStats(req: Request, res: Response): Promise<void> {
|
||||
const userCountQuery = `(SELECT COUNT(*) FROM (SELECT DISTINCT "userID" from "sponsorTimes") t) "userCount",`;
|
||||
|
||||
let row = await db.prepare('get', `SELECT ${req.query.countContributingUsers ? userCountQuery : ""} COUNT(*) as "totalSubmissions",
|
||||
const row = await db.prepare('get', `SELECT ${req.query.countContributingUsers ? userCountQuery : ""} COUNT(*) as "totalSubmissions",
|
||||
SUM("views") as "viewCount", SUM(("endTime" - "startTime") / 60 * "views") as "minutesSaved" FROM "sponsorTimes" WHERE "shadowHidden" != 1 AND "votes" >= 0`, []);
|
||||
|
||||
if (row !== undefined) {
|
||||
let extensionUsers = chromeUsersCache + firefoxUsersCache;
|
||||
const extensionUsers = chromeUsersCache + firefoxUsersCache;
|
||||
|
||||
//send this result
|
||||
res.send({
|
||||
@@ -33,7 +33,7 @@ export async function getTotalStats(req: Request, res: Response) {
|
||||
});
|
||||
|
||||
// Check if the cache should be updated (every ~14 hours)
|
||||
let now = Date.now();
|
||||
const now = Date.now();
|
||||
if (now - lastUserCountCheck > 5000000) {
|
||||
lastUserCountCheck = now;
|
||||
|
||||
@@ -79,7 +79,7 @@ function updateExtensionUsers() {
|
||||
})
|
||||
.catch(() => Logger.debug("Failing to connect to " + chromeExtensionUrl));
|
||||
})
|
||||
.catch(err => {
|
||||
.catch(() => {
|
||||
Logger.debug("Failing to connect to " + mozillaAddonsUrl);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {db} from '../databases/databases';
|
||||
import {Request, Response} from 'express';
|
||||
import {UserID} from '../types/user.model';
|
||||
|
||||
function getFuzzyUserID(userName: String): Promise<{userName: String, userID: UserID }[]> {
|
||||
function getFuzzyUserID(userName: string): Promise<{userName: string, userID: UserID }[]> {
|
||||
// escape [_ % \] to avoid ReDOS
|
||||
userName = userName.replace(/\\/g, '\\\\')
|
||||
.replace(/_/g, '\\_')
|
||||
@@ -11,13 +11,13 @@ function getFuzzyUserID(userName: String): Promise<{userName: String, userID: Us
|
||||
// LIMIT to reduce overhead | ESCAPE to escape LIKE wildcards
|
||||
try {
|
||||
return db.prepare('all', `SELECT "userName", "userID" FROM "userNames" WHERE "userName"
|
||||
LIKE ? ESCAPE '\\' LIMIT 10`, [userName])
|
||||
LIKE ? ESCAPE '\\' LIMIT 10`, [userName]);
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getExactUserID(userName: String): Promise<{userName: String, userID: UserID }[]> {
|
||||
function getExactUserID(userName: string): Promise<{userName: string, userID: UserID }[]> {
|
||||
try {
|
||||
return db.prepare('all', `SELECT "userName", "userID" from "userNames" WHERE "userName" = ? LIMIT 10`, [userName]);
|
||||
} catch (err) {
|
||||
@@ -25,31 +25,27 @@ function getExactUserID(userName: String): Promise<{userName: String, userID: Us
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserID(req: Request, res: Response) {
|
||||
let userName = req.query.username as string;
|
||||
export async function getUserID(req: Request, res: Response): Promise<Response> {
|
||||
const userName = req.query.username as string;
|
||||
const exactSearch = req.query.exact
|
||||
? req.query.exact == "true"
|
||||
: false as Boolean;
|
||||
: false as boolean;
|
||||
|
||||
// if not exact and length is 1, also skip
|
||||
if (userName == undefined || userName.length > 64 ||
|
||||
(!exactSearch && userName.length < 3)) {
|
||||
// invalid request
|
||||
res.sendStatus(400);
|
||||
return false;
|
||||
return res.sendStatus(400);
|
||||
}
|
||||
const results = exactSearch
|
||||
? await getExactUserID(userName)
|
||||
: await getFuzzyUserID(userName);
|
||||
|
||||
if (results === undefined || results === null) {
|
||||
res.sendStatus(500);
|
||||
return false;
|
||||
return res.sendStatus(500);
|
||||
} else if (results.length === 0) {
|
||||
res.sendStatus(404);
|
||||
return false;
|
||||
return res.sendStatus(404);
|
||||
} else {
|
||||
res.send(results);
|
||||
return false;
|
||||
return res.send(results);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { SegmentUUID } from "../types/segments.model";
|
||||
|
||||
async function dbGetSubmittedSegmentSummary(userID: HashedUserID): Promise<{ minutesSaved: number, segmentCount: number }> {
|
||||
try {
|
||||
let row = await db.prepare("get", `SELECT SUM((("endTime" - "startTime") / 60) * "views") as "minutesSaved",
|
||||
const row = await db.prepare("get", `SELECT SUM((("endTime" - "startTime") / 60) * "views") as "minutesSaved",
|
||||
count(*) as "segmentCount" FROM "sponsorTimes"
|
||||
WHERE "userID" = ? AND "votes" > -2 AND "shadowHidden" != 1`, [userID]);
|
||||
if (row.minutesSaved != null) {
|
||||
@@ -30,8 +30,8 @@ async function dbGetSubmittedSegmentSummary(userID: HashedUserID): Promise<{ min
|
||||
|
||||
async function dbGetIgnoredSegmentCount(userID: HashedUserID): Promise<number> {
|
||||
try {
|
||||
let row = await db.prepare("get", `SELECT COUNT(*) as "ignoredSegmentCount" FROM "sponsorTimes" WHERE "userID" = ? AND ( "votes" <= -2 OR "shadowHidden" = 1 )`, [userID]);
|
||||
return row?.ignoredSegmentCount ?? 0
|
||||
const row = await db.prepare("get", `SELECT COUNT(*) as "ignoredSegmentCount" FROM "sponsorTimes" WHERE "userID" = ? AND ( "votes" <= -2 OR "shadowHidden" = 1 )`, [userID]);
|
||||
return row?.ignoredSegmentCount ?? 0;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ async function dbGetIgnoredSegmentCount(userID: HashedUserID): Promise<number> {
|
||||
|
||||
async function dbGetUsername(userID: HashedUserID) {
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);
|
||||
if (row !== undefined) {
|
||||
return row.userName;
|
||||
} else {
|
||||
@@ -53,7 +53,7 @@ async function dbGetUsername(userID: HashedUserID) {
|
||||
|
||||
async function dbGetViewsForUser(userID: HashedUserID) {
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT SUM("views") as "viewCount" FROM "sponsorTimes" WHERE "userID" = ? AND "votes" > -2 AND "shadowHidden" != 1`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT SUM("views") as "viewCount" FROM "sponsorTimes" WHERE "userID" = ? AND "votes" > -2 AND "shadowHidden" != 1`, [userID]);
|
||||
return row?.viewCount ?? 0;
|
||||
} catch (err) {
|
||||
return false;
|
||||
@@ -62,7 +62,7 @@ async function dbGetViewsForUser(userID: HashedUserID) {
|
||||
|
||||
async function dbGetIgnoredViewsForUser(userID: HashedUserID) {
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT SUM("views") as "ignoredViewCount" FROM "sponsorTimes" WHERE "userID" = ? AND ( "votes" <= -2 OR "shadowHidden" = 1 )`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT SUM("views") as "ignoredViewCount" FROM "sponsorTimes" WHERE "userID" = ? AND ( "votes" <= -2 OR "shadowHidden" = 1 )`, [userID]);
|
||||
return row?.ignoredViewCount ?? 0;
|
||||
} catch (err) {
|
||||
return false;
|
||||
@@ -71,7 +71,7 @@ async function dbGetIgnoredViewsForUser(userID: HashedUserID) {
|
||||
|
||||
async function dbGetWarningsForUser(userID: HashedUserID): Promise<number> {
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT COUNT(*) as total FROM "warnings" WHERE "userID" = ? AND "enabled" = 1`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT COUNT(*) as total FROM "warnings" WHERE "userID" = ? AND "enabled" = 1`, [userID]);
|
||||
return row?.total ?? 0;
|
||||
} catch (err) {
|
||||
Logger.error('Couldn\'t get warnings for user ' + userID + '. returning 0');
|
||||
@@ -81,14 +81,14 @@ async function dbGetWarningsForUser(userID: HashedUserID): Promise<number> {
|
||||
|
||||
async function dbGetLastSegmentForUser(userID: HashedUserID): Promise<SegmentUUID> {
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT "UUID" FROM "sponsorTimes" WHERE "userID" = ? ORDER BY "timeSubmitted" DESC LIMIT 1`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT "UUID" FROM "sponsorTimes" WHERE "userID" = ? ORDER BY "timeSubmitted" DESC LIMIT 1`, [userID]);
|
||||
return row?.UUID ?? null;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getUserInfo(req: Request, res: Response) {
|
||||
export async function getUserInfo(req: Request, res: Response): Promise<void> {
|
||||
const userID = req.query.userID as UserID;
|
||||
const hashedUserID: HashedUserID = userID ? getHash(userID) : req.query.publicUserID as HashedUserID;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {getHash} from '../utils/getHash';
|
||||
import {Logger} from '../utils/logger';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function getUsername(req: Request, res: Response) {
|
||||
export async function getUsername(req: Request, res: Response): Promise<void> {
|
||||
let userID = req.query.userID as string;
|
||||
|
||||
if (userID == undefined) {
|
||||
@@ -16,7 +16,7 @@ export async function getUsername(req: Request, res: Response) {
|
||||
userID = getHash(userID);
|
||||
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT "userName" FROM "userNames" WHERE "userID" = ?`, [userID]);
|
||||
|
||||
if (row !== undefined) {
|
||||
res.send({
|
||||
@@ -31,7 +31,6 @@ export async function getUsername(req: Request, res: Response) {
|
||||
} catch (err) {
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {Request, Response} from 'express';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {Logger} from '../utils/logger';
|
||||
|
||||
export async function getViewsForUser(req: Request, res: Response) {
|
||||
export async function getViewsForUser(req: Request, res: Response): Promise<void> {
|
||||
let userID = req.query.userID as string;
|
||||
|
||||
if (userID == undefined) {
|
||||
@@ -16,7 +16,7 @@ export async function getViewsForUser(req: Request, res: Response) {
|
||||
userID = getHash(userID);
|
||||
|
||||
try {
|
||||
let row = await db.prepare('get', `SELECT SUM("views") as "viewCount" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]);
|
||||
const row = await db.prepare('get', `SELECT SUM("views") as "viewCount" FROM "sponsorTimes" WHERE "userID" = ?`, [userID]);
|
||||
|
||||
//increase the view count by one
|
||||
if (row.viewCount != null) {
|
||||
@@ -29,7 +29,6 @@ export async function getViewsForUser(req: Request, res: Response) {
|
||||
} catch (err) {
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import {handleGetSegments} from './getSkipSegments';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function oldGetVideoSponsorTimes(req: Request, res: Response): Promise<void> {
|
||||
let segments = await handleGetSegments(req, res);
|
||||
const segments = await handleGetSegments(req, res);
|
||||
|
||||
if (segments) {
|
||||
// Convert to old outputs
|
||||
let sponsorTimes = [];
|
||||
let UUIDs = [];
|
||||
const sponsorTimes = [];
|
||||
const UUIDs = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
sponsorTimes.push(segment.segment);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import {postSkipSegments} from './postSkipSegments';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function oldSubmitSponsorTimes(req: Request, res: Response) {
|
||||
export async function oldSubmitSponsorTimes(req: Request, res: Response): Promise<void> {
|
||||
req.query.category = "sponsor";
|
||||
|
||||
return postSkipSegments(req, res);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import { QueryCacher } from '../utils/queryCacher';
|
||||
import { isUserVIP } from '../utils/isUserVIP';
|
||||
import { VideoIDHash } from "../types/segments.model";
|
||||
|
||||
export async function postClearCache(req: Request, res: Response) {
|
||||
export async function postClearCache(req: Request, res: Response): Promise<void> {
|
||||
const videoID = req.query.videoID as VideoID;
|
||||
let userID = req.query.userID as UserID;
|
||||
const userID = req.query.userID as UserID;
|
||||
const service = req.query.service as Service ?? Service.YouTube;
|
||||
|
||||
const invalidFields = [];
|
||||
@@ -24,7 +24,7 @@ export async function postClearCache(req: Request, res: Response) {
|
||||
// invalid request
|
||||
const fields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ', ' : '') + c, '');
|
||||
res.status(400).send(`No valid ${fields} field(s) provided`);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// hash the userID as early as possible
|
||||
@@ -36,7 +36,7 @@ export async function postClearCache(req: Request, res: Response) {
|
||||
if (!(await isUserVIP(hashedUserID))){
|
||||
Logger.warn("Permission violation: User " + hashedUserID + " attempted to clear cache for video " + videoID + ".");
|
||||
res.status(403).json({"message": "Not a VIP"});
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -49,7 +49,7 @@ export async function postClearCache(req: Request, res: Response) {
|
||||
message: "Cache cleared on video " + videoID
|
||||
});
|
||||
} catch(err) {
|
||||
res.status(500).send()
|
||||
return false;
|
||||
res.status(500).send();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import {isUserVIP} from '../utils/isUserVIP';
|
||||
import {db} from '../databases/databases';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function postLockCategories(req: Request, res: Response) {
|
||||
export async function postLockCategories(req: Request, res: Response): Promise<string[]> {
|
||||
// Collect user input data
|
||||
let videoID = req.body.videoID;
|
||||
const videoID = req.body.videoID;
|
||||
let userID = req.body.userID;
|
||||
let categories = req.body.categories;
|
||||
const categories = req.body.categories;
|
||||
|
||||
// Check input data is valid
|
||||
if (!videoID
|
||||
@@ -25,7 +25,7 @@ export async function postLockCategories(req: Request, res: Response) {
|
||||
|
||||
// Check if user is VIP
|
||||
userID = getHash(userID);
|
||||
let userIsVIP = await isUserVIP(userID);
|
||||
const userIsVIP = await isUserVIP(userID);
|
||||
|
||||
if (!userIsVIP) {
|
||||
res.status(403).json({
|
||||
@@ -67,7 +67,7 @@ export async function postLockCategories(req: Request, res: Response) {
|
||||
message: "Internal Server Error: Could not write marker to the database.",
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
submitted: categoriesToMark,
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function postPurgeAllSegments(req: Request, res: Response): Promise
|
||||
const hashedUserID: HashedUserID = getHash(userID);
|
||||
|
||||
try {
|
||||
let vipState = await isUserVIP(hashedUserID);
|
||||
const vipState = await isUserVIP(hashedUserID);
|
||||
if (!vipState) {
|
||||
res.status(403).json({
|
||||
message: 'Must be a VIP to perform this action.',
|
||||
|
||||
@@ -92,7 +92,7 @@ export async function postSegmentShift(req: Request, res: Response): Promise<Res
|
||||
await db.prepare('run', 'UPDATE "sponsorTimes" SET "startTime" = ?, "endTime" = ?, "votes" = -2 WHERE "UUID" = ?', [result.segment.startTime, result.segment.endTime, result.segment.UUID]);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
@@ -4,16 +4,13 @@ import {db, privateDB} from '../databases/databases';
|
||||
import {getMaxResThumbnail, YouTubeAPI} from '../utils/youtubeApi';
|
||||
import {getSubmissionUUID} from '../utils/getSubmissionUUID';
|
||||
import fetch from 'node-fetch';
|
||||
import isoDurations, { end } from 'iso8601-duration';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {getIP} from '../utils/getIP';
|
||||
import {getFormattedTime} from '../utils/getFormattedTime';
|
||||
import {isUserTrustworthy} from '../utils/isUserTrustworthy';
|
||||
import {dispatchEvent} from '../utils/webhookUtils';
|
||||
import {Request, Response} from 'express';
|
||||
import { skipSegmentsHashKey, skipSegmentsKey } from '../utils/redisKeys';
|
||||
import redis from '../utils/redis';
|
||||
import { Category, CategoryActionType, IncomingSegment, Segment, SegmentUUID, Service, VideoDuration, VideoID } from '../types/segments.model';
|
||||
import { Category, CategoryActionType, IncomingSegment, SegmentUUID, Service, VideoDuration, VideoID } from '../types/segments.model';
|
||||
import { deleteLockCategories } from './deleteLockCategories';
|
||||
import { getCategoryActionType } from '../utils/categoryInfo';
|
||||
import { QueryCacher } from '../utils/queryCacher';
|
||||
@@ -176,9 +173,6 @@ async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
|
||||
const segments = submission.segments;
|
||||
let nbString = "";
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const startTime = parseFloat(segments[i].segment[0]);
|
||||
const endTime = parseFloat(segments[i].segment[1]);
|
||||
|
||||
if (duration == 0) {
|
||||
// Allow submission if the duration is 0 (bug in youtube api)
|
||||
return false;
|
||||
@@ -202,8 +196,8 @@ async function autoModerateSubmission(apiVideoInfo: APIVideoInfo,
|
||||
|
||||
//add segments they are trying to add in this submission
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
let startTime = parseFloat(segments[i].segment[0]);
|
||||
let endTime = parseFloat(segments[i].segment[1]);
|
||||
const startTime = parseFloat(segments[i].segment[0]);
|
||||
const endTime = parseFloat(segments[i].segment[1]);
|
||||
allSegmentTimes.push([startTime, endTime]);
|
||||
}
|
||||
|
||||
@@ -280,12 +274,12 @@ function proxySubmission(req: Request) {
|
||||
.then(async res => {
|
||||
Logger.debug('Proxy Submission: ' + res.status + ' (' + (await res.text()) + ')');
|
||||
})
|
||||
.catch(err => {
|
||||
.catch(() => {
|
||||
Logger.error("Proxy Submission: Failed to make call");
|
||||
});
|
||||
}
|
||||
|
||||
export async function postSkipSegments(req: Request, res: Response) {
|
||||
export async function postSkipSegments(req: Request, res: Response): Promise<void> {
|
||||
if (config.proxySubmission) {
|
||||
proxySubmission(req);
|
||||
}
|
||||
@@ -338,7 +332,8 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
)).count;
|
||||
|
||||
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
||||
return res.status(403).send('Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?');
|
||||
res.status(403).send('Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?');
|
||||
return;
|
||||
}
|
||||
|
||||
let lockedCategoryList = (await db.prepare('all', 'SELECT category from "lockCategories" where "videoID" = ?', [videoID])).map((list: any) => {
|
||||
@@ -406,8 +401,8 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
}
|
||||
|
||||
|
||||
let startTime = parseFloat(segments[i].segment[0]);
|
||||
let endTime = parseFloat(segments[i].segment[1]);
|
||||
const startTime = parseFloat(segments[i].segment[0]);
|
||||
const endTime = parseFloat(segments[i].segment[1]);
|
||||
|
||||
if (isNaN(startTime) || isNaN(endTime)
|
||||
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime > endTime
|
||||
@@ -459,6 +454,7 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
const yesterday = timeSubmitted - 86400000;
|
||||
|
||||
// Disable IP ratelimiting for now
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
//check to see if this ip has submitted too many sponsors today
|
||||
const rateLimitCheckRow = await privateDB.prepare('get', `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "hashedIP" = ? AND "videoID" = ? AND "timeSubmitted" > ?`, [hashedIP, videoID, yesterday]);
|
||||
@@ -466,12 +462,12 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
if (rateLimitCheckRow.count >= 10) {
|
||||
//too many sponsors for the same video from the same ip address
|
||||
res.sendStatus(429);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable max submissions for now
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (false) {
|
||||
//check to see if the user has already submitted sponsors for this video
|
||||
const duplicateCheckRow = await db.prepare('get', `SELECT COUNT(*) as count FROM "sponsorTimes" WHERE "userID" = ? and "videoID" = ?`, [userID, videoID]);
|
||||
@@ -479,7 +475,6 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
if (duplicateCheckRow.count >= 16) {
|
||||
//too many sponsors for the same video from the same user
|
||||
res.sendStatus(429);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -494,7 +489,7 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
shadowBanned = 1;
|
||||
}
|
||||
|
||||
let startingVotes = 0 + decreaseVotes;
|
||||
const startingVotes = 0 + decreaseVotes;
|
||||
const reputation = await getReputation(userID);
|
||||
|
||||
for (const segmentInfo of segments) {
|
||||
@@ -528,7 +523,6 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
res.sendStatus(500);
|
||||
Logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
||||
segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -541,9 +535,7 @@ export async function postSkipSegments(req: Request, res: Response) {
|
||||
}
|
||||
} catch (err) {
|
||||
Logger.error(err);
|
||||
|
||||
res.sendStatus(500);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {isUserVIP} from '../utils/isUserVIP';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import { HashedUserID, UserID } from '../types/user.model';
|
||||
|
||||
export async function postWarning(req: Request, res: Response) {
|
||||
export async function postWarning(req: Request, res: Response): Promise<void> {
|
||||
// Collect user input data
|
||||
const issuerUserID: HashedUserID = getHash(<UserID> req.body.issuerUserID);
|
||||
const userID: UserID = req.body.userID;
|
||||
@@ -23,7 +23,7 @@ export async function postWarning(req: Request, res: Response) {
|
||||
let resultStatus = "";
|
||||
|
||||
if (enabled) {
|
||||
let previousWarning = await db.prepare('get', 'SELECT * FROM "warnings" WHERE "userID" = ? AND "issuerUserID" = ?', [userID, issuerUserID]);
|
||||
const previousWarning = await db.prepare('get', 'SELECT * FROM "warnings" WHERE "userID" = ? AND "issuerUserID" = ?', [userID, issuerUserID]);
|
||||
|
||||
if (!previousWarning) {
|
||||
await db.prepare(
|
||||
|
||||
@@ -11,7 +11,7 @@ async function logUserNameChange(userID: string, newUserName: string, oldUserNam
|
||||
);
|
||||
}
|
||||
|
||||
export async function setUsername(req: Request, res: Response) {
|
||||
export async function setUsername(req: Request, res: Response): Promise<void> {
|
||||
let userID = req.query.userID as string;
|
||||
let userName = req.query.username as string;
|
||||
|
||||
@@ -31,6 +31,7 @@ export async function setUsername(req: Request, res: Response) {
|
||||
|
||||
// remove unicode control characters from username (example: \n, \r, \t etc.)
|
||||
// source: https://en.wikipedia.org/wiki/Control_character#In_Unicode
|
||||
// eslint-disable-next-line no-control-regex
|
||||
userName = userName.replace(/[\u0000-\u001F\u007F-\u009F]/g, '');
|
||||
|
||||
if (adminUserIDInput != undefined) {
|
||||
@@ -81,7 +82,6 @@ export async function setUsername(req: Request, res: Response) {
|
||||
} catch (err) {
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Category, Service, VideoID, VideoIDHash } from '../types/segments.model
|
||||
import { UserID } from '../types/user.model';
|
||||
import { QueryCacher } from '../utils/queryCacher';
|
||||
|
||||
export async function shadowBanUser(req: Request, res: Response) {
|
||||
export async function shadowBanUser(req: Request, res: Response): Promise<void> {
|
||||
const userID = req.query.userID as string;
|
||||
const hashedIP = req.query.hashedIP as string;
|
||||
let adminUserIDInput = req.query.adminUserID as string;
|
||||
@@ -66,10 +66,10 @@ export async function shadowBanUser(req: Request, res: Response) {
|
||||
|
||||
//find all previous submissions and unhide them
|
||||
if (unHideOldSubmissions) {
|
||||
let segmentsToIgnore = (await db.prepare('all', `SELECT "UUID" FROM "sponsorTimes" st
|
||||
const segmentsToIgnore = (await db.prepare('all', `SELECT "UUID" FROM "sponsorTimes" st
|
||||
JOIN "lockCategories" ns on "st"."videoID" = "ns"."videoID" AND st.category = ns.category WHERE "st"."userID" = ?`
|
||||
, [userID])).map((item: {UUID: string}) => item.UUID);
|
||||
let allSegments = (await db.prepare('all', `SELECT "UUID" FROM "sponsorTimes" st WHERE "st"."userID" = ?`, [userID]))
|
||||
const allSegments = (await db.prepare('all', `SELECT "UUID" FROM "sponsorTimes" st WHERE "st"."userID" = ?`, [userID]))
|
||||
.map((item: {UUID: string}) => item.UUID);
|
||||
|
||||
await Promise.all(allSegments.filter((item: {uuid: string}) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {db} from '../databases/databases';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
export async function viewedVideoSponsorTime(req: Request, res: Response): Promise<Response> {
|
||||
let UUID = req.query.UUID;
|
||||
const UUID = req.query.UUID;
|
||||
|
||||
if (UUID == undefined) {
|
||||
//invalid request
|
||||
|
||||
@@ -252,7 +252,7 @@ export function getUserID(req: Request): UserID {
|
||||
return req.query.userID as UserID;
|
||||
}
|
||||
|
||||
export async function voteOnSponsorTime(req: Request, res: Response) {
|
||||
export async function voteOnSponsorTime(req: Request, res: Response): Promise<void> {
|
||||
const UUID = req.query.UUID as SegmentUUID;
|
||||
const paramUserID = getUserID(req);
|
||||
let type = req.query.type !== undefined ? parseInt(req.query.type as string) : undefined;
|
||||
@@ -269,13 +269,13 @@ export async function voteOnSponsorTime(req: Request, res: Response) {
|
||||
const userID = getHash(paramUserID + UUID);
|
||||
|
||||
// To force a non 200, change this early
|
||||
let finalResponse: FinalResponse = {
|
||||
const finalResponse: FinalResponse = {
|
||||
blockVote: false,
|
||||
finalStatus: 200,
|
||||
finalMessage: null,
|
||||
webhookType: VoteWebhookType.Normal,
|
||||
webhookMessage: null
|
||||
}
|
||||
};
|
||||
|
||||
//x-forwarded-for if this server is behind a proxy
|
||||
const ip = getIP(req);
|
||||
@@ -292,7 +292,7 @@ export async function voteOnSponsorTime(req: Request, res: Response) {
|
||||
// disallow vote types 10/11
|
||||
if (type === 10 || type === 11) {
|
||||
// no longer allow type 10/11 alternative votes
|
||||
res.sendStatus(400)
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -305,8 +305,8 @@ export async function voteOnSponsorTime(req: Request, res: Response) {
|
||||
|
||||
if (await isSegmentLocked() || await isVideoLocked()) {
|
||||
finalResponse.blockVote = true;
|
||||
finalResponse.webhookType = VoteWebhookType.Rejected
|
||||
finalResponse.webhookMessage = "Vote rejected: A moderator has decided that this segment is correct"
|
||||
finalResponse.webhookType = VoteWebhookType.Rejected;
|
||||
finalResponse.webhookMessage = "Vote rejected: A moderator has decided that this segment is correct";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +337,8 @@ export async function voteOnSponsorTime(req: Request, res: Response) {
|
||||
)).count;
|
||||
|
||||
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
||||
return res.status(403).send('Vote rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?');
|
||||
res.status(403).send('Vote rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?');
|
||||
return;
|
||||
}
|
||||
|
||||
const voteTypeEnum = (type == 0 || type == 1 || type == 20) ? voteTypes.normal : voteTypes.incorrect;
|
||||
|
||||
@@ -10,22 +10,22 @@ if (config.diskCache) {
|
||||
DiskCache = {
|
||||
// constructor(rootPath, options): {};
|
||||
|
||||
init() {},
|
||||
init(): void { return; },
|
||||
|
||||
reset() {},
|
||||
reset(): void { return; },
|
||||
|
||||
has(key) { return false; },
|
||||
has(key: string): boolean { return false; },
|
||||
|
||||
get(key, opts) { return null; },
|
||||
get(key: string, opts): string { return null; },
|
||||
|
||||
// Returns size
|
||||
set(key, dataOrSteam) { return new Promise((resolve) => 0); },
|
||||
set(key: string, dataOrSteam): Promise<number> { return new Promise(() => 0); },
|
||||
|
||||
del(key) {},
|
||||
del(key: string): void { return; },
|
||||
|
||||
size() { return 0; },
|
||||
size(): number { return 0; },
|
||||
|
||||
prune() {},
|
||||
prune(): void {return; },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Converts time in seconds to minutes:seconds
|
||||
*/
|
||||
export function getFormattedTime(totalSeconds: number) {
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
let seconds = totalSeconds - minutes * 60;
|
||||
export function getFormattedTime(totalSeconds: number): string {
|
||||
const minutes = Math.floor(totalSeconds / 60);
|
||||
const seconds = totalSeconds - minutes * 60;
|
||||
let secondsDisplay = seconds.toFixed(3);
|
||||
if (seconds < 10) {
|
||||
//add a zero
|
||||
|
||||
@@ -5,7 +5,7 @@ export function getHash<T extends string>(value: T, times = 5000): T & HashedVal
|
||||
if (times <= 0) return "" as T & HashedValue;
|
||||
|
||||
for (let i = 0; i < times; i++) {
|
||||
let hashCreator = crypto.createHash('sha256');
|
||||
const hashCreator = crypto.createHash('sha256');
|
||||
value = hashCreator.update(value).digest('hex') as T;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {getHash} from './getHash';
|
||||
import { HashedValue } from '../types/hash.model';
|
||||
|
||||
export function getSubmissionUUID(videoID: string, category: string, userID: string, startTime: number, endTime: number) {
|
||||
export function getSubmissionUUID(videoID: string, category: string, userID: string, startTime: number, endTime: number): HashedValue{
|
||||
return getHash('v2-categories' + videoID + startTime + endTime + category + userID, 1);
|
||||
}
|
||||
|
||||
@@ -86,4 +86,4 @@ class Logger {
|
||||
const loggerInstance = new Logger();
|
||||
export {
|
||||
loggerInstance as Logger
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ async function get<T>(fetchFromDB: () => Promise<T>, key: string): Promise<T> {
|
||||
return data;
|
||||
}
|
||||
|
||||
function clearVideoCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID?: UserID; }) {
|
||||
function clearVideoCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHash; service: Service; userID?: UserID; }): void {
|
||||
if (videoInfo) {
|
||||
redis.delAsync(skipSegmentsKey(videoInfo.videoID, videoInfo.service));
|
||||
redis.delAsync(skipSegmentsHashKey(videoInfo.hashedVideoID, videoInfo.service));
|
||||
@@ -33,4 +33,4 @@ function clearVideoCache(videoInfo: { videoID: VideoID; hashedVideoID: VideoIDHa
|
||||
export const QueryCacher = {
|
||||
get,
|
||||
clearVideoCache
|
||||
}
|
||||
};
|
||||
@@ -12,12 +12,12 @@ interface RedisSB {
|
||||
|
||||
let exportObject: RedisSB = {
|
||||
get: (key, callback?) => callback(null, undefined),
|
||||
getAsync: (key) =>
|
||||
getAsync: () =>
|
||||
new Promise((resolve) => resolve({err: null, reply: undefined})),
|
||||
set: (key, value, callback) => callback(null, undefined),
|
||||
setAsync: (key, value) =>
|
||||
setAsync: () =>
|
||||
new Promise((resolve) => resolve({err: null, reply: undefined})),
|
||||
delAsync: (...keys) =>
|
||||
delAsync: () =>
|
||||
new Promise((resolve) => resolve(null)),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {config} from '../config';
|
||||
import {Logger} from '../utils/logger';
|
||||
import fetch from 'node-fetch';
|
||||
import AbortController from "abort-controller";
|
||||
|
||||
function getVoteAuthorRaw(submissionCount: number, isVIP: boolean, isOwnSubmission: boolean): string {
|
||||
if (isOwnSubmission) {
|
||||
@@ -27,15 +26,15 @@ function getVoteAuthor(submissionCount: number, isVIP: boolean, isOwnSubmission:
|
||||
return "";
|
||||
}
|
||||
|
||||
function dispatchEvent(scope: string, data: any): void {
|
||||
let webhooks = config.webhooks;
|
||||
function dispatchEvent(scope: string, data: Record<string, unknown>): void {
|
||||
const webhooks = config.webhooks;
|
||||
if (webhooks === undefined || webhooks.length === 0) return;
|
||||
Logger.debug("Dispatching webhooks");
|
||||
|
||||
for (const webhook of webhooks) {
|
||||
let webhookURL = webhook.url;
|
||||
let authKey = webhook.key;
|
||||
let scopes = webhook.scopes || [];
|
||||
const webhookURL = webhook.url;
|
||||
const authKey = webhook.key;
|
||||
const scopes = webhook.scopes || [];
|
||||
if (!scopes.includes(scope.toLowerCase())) return;
|
||||
|
||||
fetch(webhookURL, {
|
||||
|
||||
@@ -17,10 +17,10 @@ export class YouTubeAPI {
|
||||
|
||||
if (data) {
|
||||
Logger.debug("YouTube API: cache used for video information: " + videoID);
|
||||
return { err: null, data: JSON.parse(data) }
|
||||
return { err: null, data: JSON.parse(data) };
|
||||
}
|
||||
} catch (err) {
|
||||
return { err }
|
||||
return { err };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export class YouTubeAPI {
|
||||
if (result.ok) {
|
||||
const data = await result.json();
|
||||
if (data.error) {
|
||||
Logger.warn("NewLeaf API Error for " + videoID + ": " + data.error)
|
||||
Logger.warn("NewLeaf API Error for " + videoID + ": " + data.error);
|
||||
return { err: data.error, data: null };
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export class YouTubeAPI {
|
||||
return { err: result.statusText, data: null };
|
||||
}
|
||||
} catch (err) {
|
||||
return {err, data: null}
|
||||
return {err, data: null};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user