mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-06 11:36:58 +03:00
migrate to typescript
This commit is contained in:
committed by
Dainius Dauksevicius
parent
c462323dd5
commit
08d27265fc
17
.editorconfig
Normal file
17
.editorconfig
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{js,json,ts,tsx}]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[package.json]
|
||||||
|
indent_size = 2
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -103,3 +103,4 @@ config.json
|
|||||||
|
|
||||||
# Mac files
|
# Mac files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/.idea/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ FROM node:12
|
|||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
RUN npm install
|
RUN npm install
|
||||||
COPY index.js .
|
COPY index.ts .
|
||||||
COPY src src
|
COPY src src
|
||||||
RUN mkdir databases
|
RUN mkdir databases
|
||||||
COPY databases/*.sql databases/
|
COPY databases/*.sql databases/
|
||||||
|
|||||||
@@ -23,4 +23,4 @@ cp /etc/sponsorblock/config.json . || cat <<EOF > config.json
|
|||||||
"readOnly": false
|
"readOnly": false
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
node index.js
|
ts-node index.ts
|
||||||
|
|||||||
6
index.js
6
index.js
@@ -1,6 +0,0 @@
|
|||||||
var config = require('./src/config.js');
|
|
||||||
var createServer = require('./src/app.js');
|
|
||||||
const logger = require('./src/utils/logger.js');
|
|
||||||
var server = createServer(() => {
|
|
||||||
logger.info("Server started on port " + config.port + ".");
|
|
||||||
});
|
|
||||||
9
index.ts
Normal file
9
index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import {config} from "./src/config";
|
||||||
|
import {initDb} from './src/databases/databases';
|
||||||
|
import {createServer} from "./src/app";
|
||||||
|
import {Logger} from "./src/utils/logger";
|
||||||
|
|
||||||
|
initDb();
|
||||||
|
createServer(() => {
|
||||||
|
Logger.info("Server started on port " + config.port + ".");
|
||||||
|
});
|
||||||
1082
package-lock.json
generated
1082
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -2,12 +2,12 @@
|
|||||||
"name": "sponsor_block_server",
|
"name": "sponsor_block_server",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Server that holds the SponsorBlock database",
|
"description": "Server that holds the SponsorBlock database",
|
||||||
"main": "index.js",
|
"main": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node test.js",
|
"test": "ts-node test.ts",
|
||||||
"dev": "nodemon -x \"(npm test || echo test failed) && npm start\"",
|
"dev": "nodemon -x \"(npm test || echo test failed) && npm start\"",
|
||||||
"dev:bash": "nodemon -x 'npm test ; npm start'",
|
"dev:bash": "nodemon -x 'npm test ; npm start'",
|
||||||
"start": "node index.js"
|
"start": "ts-node index.ts"
|
||||||
},
|
},
|
||||||
"author": "Ajay Ramachandran",
|
"author": "Ajay Ramachandran",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -19,12 +19,23 @@
|
|||||||
"iso8601-duration": "^1.2.0",
|
"iso8601-duration": "^1.2.0",
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
"redis": "^3.0.2",
|
"redis": "^3.0.2",
|
||||||
|
"request": "^2.88.2",
|
||||||
"sync-mysql": "^3.0.1",
|
"sync-mysql": "^3.0.1",
|
||||||
"uuid": "^3.3.2",
|
"uuid": "^3.3.2",
|
||||||
"youtube-api": "^2.0.10"
|
"youtube-api": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/better-sqlite3": "^5.4.0",
|
||||||
|
"@types/express": "^4.17.8",
|
||||||
|
"@types/express-rate-limit": "^5.1.0",
|
||||||
|
"@types/mocha": "^8.0.3",
|
||||||
|
"@types/node": "^14.11.9",
|
||||||
|
"@types/node-fetch": "^2.5.7",
|
||||||
|
"@types/redis": "^2.8.28",
|
||||||
|
"@types/request": "^2.48.5",
|
||||||
"mocha": "^7.1.1",
|
"mocha": "^7.1.1",
|
||||||
"nodemon": "^2.0.2"
|
"nodemon": "^2.0.2",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,38 @@
|
|||||||
var express = require('express');
|
import express, {Request, RequestHandler, Response} from 'express';
|
||||||
|
import {config} from './config';
|
||||||
|
import {oldSubmitSponsorTimes} from './routes/oldSubmitSponsorTimes';
|
||||||
|
import {oldGetVideoSponsorTimes} from './routes/oldGetVideoSponsorTimes';
|
||||||
|
import {postSegmentShift} from './routes/postSegmentShift';
|
||||||
|
import {postWarning} from './routes/postWarning';
|
||||||
|
import {getIsUserVIP} from './routes/getIsUserVIP';
|
||||||
|
import {deleteNoSegments} from './routes/deleteNoSegments';
|
||||||
|
import {postNoSegments} from './routes/postNoSegments';
|
||||||
|
import {getUserInfo} from './routes/getUserInfo';
|
||||||
|
import {getDaysSavedFormatted} from './routes/getDaysSavedFormatted';
|
||||||
|
import {getTotalStats} from './routes/getTotalStats';
|
||||||
|
import {getTopUsers} from './routes/getTopUsers';
|
||||||
|
import {getViewsForUser} from './routes/getViewsForUser';
|
||||||
|
import {getSavedTimeForUser} from './routes/getSavedTimeForUser';
|
||||||
|
import {addUserAsVIP} from './routes/addUserAsVIP';
|
||||||
|
import {shadowBanUser} from './routes/shadowBanUser';
|
||||||
|
import {getUsername} from './routes/getUsername';
|
||||||
|
import {setUsername} from './routes/setUsername';
|
||||||
|
import {viewedVideoSponsorTime} from './routes/viewedVideoSponsorTime';
|
||||||
|
import {voteOnSponsorTime} from './routes/voteOnSponsorTime';
|
||||||
|
import {getSkipSegmentsByHash} from './routes/getSkipSegmentsByHash';
|
||||||
|
import {postSkipSegments} from './routes/postSkipSegments';
|
||||||
|
import {endpoint as getSkipSegments} from './routes/getSkipSegments';
|
||||||
|
|
||||||
|
import {userCounter} from './middleware/userCounter';
|
||||||
|
import {loggerMiddleware} from './middleware/logger';
|
||||||
|
import {corsMiddleware} from './middleware/cors';
|
||||||
|
import {rateLimitMiddleware} from './middleware/requestRateLimit';
|
||||||
|
|
||||||
// Create a service (the app object is just a callback).
|
// Create a service (the app object is just a callback).
|
||||||
var app = express();
|
const app = express();
|
||||||
var config = require('./config.js');
|
|
||||||
var redis = require('./utils/redis.js');
|
|
||||||
const getIP = require('./utils/getIP.js');
|
|
||||||
const getHash = require('./utils/getHash.js');
|
|
||||||
|
|
||||||
// Middleware
|
|
||||||
const rateLimitMiddleware = require('./middleware/requestRateLimit.js');
|
|
||||||
var corsMiddleware = require('./middleware/cors.js');
|
|
||||||
var loggerMiddleware = require('./middleware/logger.js');
|
|
||||||
const userCounter = require('./middleware/userCounter.js');
|
|
||||||
|
|
||||||
// Routes
|
|
||||||
var getSkipSegments = require('./routes/getSkipSegments.js').endpoint;
|
|
||||||
var postSkipSegments = require('./routes/postSkipSegments.js');
|
|
||||||
var getSkipSegmentsByHash = require('./routes/getSkipSegmentsByHash.js');
|
|
||||||
var voteOnSponsorTime = require('./routes/voteOnSponsorTime.js');
|
|
||||||
var viewedVideoSponsorTime = require('./routes/viewedVideoSponsorTime.js');
|
|
||||||
var setUsername = require('./routes/setUsername.js');
|
|
||||||
var getUsername = require('./routes/getUsername.js');
|
|
||||||
var shadowBanUser = require('./routes/shadowBanUser.js');
|
|
||||||
var addUserAsVIP = require('./routes/addUserAsVIP.js');
|
|
||||||
var getSavedTimeForUser = require('./routes/getSavedTimeForUser.js');
|
|
||||||
var getViewsForUser = require('./routes/getViewsForUser.js');
|
|
||||||
var getTopUsers = require('./routes/getTopUsers.js');
|
|
||||||
var getTotalStats = require('./routes/getTotalStats.js');
|
|
||||||
var getDaysSavedFormatted = require('./routes/getDaysSavedFormatted.js');
|
|
||||||
var getUserInfo = require('./routes/getUserInfo.js');
|
|
||||||
var postNoSegments = require('./routes/postNoSegments.js');
|
|
||||||
var deleteNoSegments = require('./routes/deleteNoSegments.js');
|
|
||||||
var getIsUserVIP = require('./routes/getIsUserVIP.js');
|
|
||||||
var warnUser = require('./routes/postWarning.js');
|
|
||||||
var postSegmentShift = require('./routes/postSegmentShift.js');
|
|
||||||
|
|
||||||
// Old Routes
|
|
||||||
var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js');
|
|
||||||
var oldSubmitSponsorTimes = require('./routes/oldSubmitSponsorTimes.js');
|
|
||||||
|
|
||||||
// Rate limit endpoint lists
|
// Rate limit endpoint lists
|
||||||
let voteEndpoints = [voteOnSponsorTime.endpoint];
|
const voteEndpoints: RequestHandler[] = [voteOnSponsorTime];
|
||||||
let viewEndpoints = [viewedVideoSponsorTime];
|
const viewEndpoints: RequestHandler[] = [viewedVideoSponsorTime];
|
||||||
if (config.rateLimit) {
|
if (config.rateLimit) {
|
||||||
if (config.rateLimit.vote) voteEndpoints.unshift(rateLimitMiddleware(config.rateLimit.vote));
|
if (config.rateLimit.vote) voteEndpoints.unshift(rateLimitMiddleware(config.rateLimit.vote));
|
||||||
if (config.rateLimit.view) viewEndpoints.unshift(rateLimitMiddleware(config.rateLimit.view));
|
if (config.rateLimit.view) viewEndpoints.unshift(rateLimitMiddleware(config.rateLimit.view));
|
||||||
@@ -49,7 +41,7 @@ if (config.rateLimit) {
|
|||||||
//setup CORS correctly
|
//setup CORS correctly
|
||||||
app.use(corsMiddleware);
|
app.use(corsMiddleware);
|
||||||
app.use(loggerMiddleware);
|
app.use(loggerMiddleware);
|
||||||
app.use(express.json())
|
app.use(express.json());
|
||||||
|
|
||||||
if (config.userCounterURL) app.use(userCounter);
|
if (config.userCounterURL) app.use(userCounter);
|
||||||
|
|
||||||
@@ -122,16 +114,16 @@ app.delete('/api/noSegments', deleteNoSegments);
|
|||||||
app.get('/api/isUserVIP', getIsUserVIP);
|
app.get('/api/isUserVIP', getIsUserVIP);
|
||||||
|
|
||||||
//sent user a warning
|
//sent user a warning
|
||||||
app.post('/api/warnUser', warnUser);
|
app.post('/api/warnUser', postWarning);
|
||||||
|
|
||||||
//get if user is a vip
|
//get if user is a vip
|
||||||
app.post('/api/segmentShift', postSegmentShift);
|
app.post('/api/segmentShift', postSegmentShift);
|
||||||
|
|
||||||
app.get('/database.db', function (req, res) {
|
app.get('/database.db', function (req: Request, res: Response) {
|
||||||
res.sendFile("./databases/sponsorTimes.db", { root: "./" });
|
res.sendFile("./databases/sponsorTimes.db", {root: "./"});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create an HTTP service.
|
// Create an HTTP service.
|
||||||
module.exports = function createServer (callback) {
|
export function createServer(callback: () => void) {
|
||||||
return app.listen(config.port, callback);
|
return app.listen(config.port, callback);
|
||||||
}
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
let config = {};
|
|
||||||
|
|
||||||
// Check to see if launched in test mode
|
|
||||||
if (process.env.npm_lifecycle_script === 'node test.js') {
|
|
||||||
config = JSON.parse(fs.readFileSync('test.json'));
|
|
||||||
} else {
|
|
||||||
config = JSON.parse(fs.readFileSync('config.json'));
|
|
||||||
}
|
|
||||||
|
|
||||||
addDefaults(config, {
|
|
||||||
"port": 80,
|
|
||||||
"behindProxy": "X-Forwarded-For",
|
|
||||||
"db": "./databases/sponsorTimes.db",
|
|
||||||
"privateDB": "./databases/private.db",
|
|
||||||
"createDatabaseIfNotExist": true,
|
|
||||||
"schemaFolder": "./databases",
|
|
||||||
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
|
||||||
"privateDBSchema": "./databases/_private.db.sql",
|
|
||||||
"readOnly": false,
|
|
||||||
"webhooks": [],
|
|
||||||
"categoryList": ["sponsor", "intro", "outro", "interaction", "selfpromo", "music_offtopic"],
|
|
||||||
"maxNumberOfActiveWarnings": 3,
|
|
||||||
"hoursAfterWarningExpires": 24
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = config;
|
|
||||||
|
|
||||||
// Add defaults
|
|
||||||
function addDefaults(config, defaults) {
|
|
||||||
for (const key in defaults) {
|
|
||||||
if(!config.hasOwnProperty(key)) {
|
|
||||||
config[key] = defaults[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
58
src/config.ts
Normal file
58
src/config.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import {SBSConfig} from "./types/config.model";
|
||||||
|
|
||||||
|
const isTestMode = process.env.npm_lifecycle_script === 'ts-node test.ts';
|
||||||
|
const configFile = isTestMode ? 'test.json' : 'config.json';
|
||||||
|
export const config: SBSConfig = JSON.parse(fs.readFileSync(configFile).toString('utf8'));
|
||||||
|
|
||||||
|
addDefaults(config, {
|
||||||
|
port: 80,
|
||||||
|
behindProxy: "X-Forwarded-For",
|
||||||
|
db: "./databases/sponsorTimes.db",
|
||||||
|
privateDB: "./databases/private.db",
|
||||||
|
createDatabaseIfNotExist: true,
|
||||||
|
schemaFolder: "./databases",
|
||||||
|
dbSchema: "./databases/_sponsorTimes.db.sql",
|
||||||
|
privateDBSchema: "./databases/_private.db.sql",
|
||||||
|
readOnly: false,
|
||||||
|
webhooks: [],
|
||||||
|
categoryList: ["sponsor", "intro", "outro", "interaction", "selfpromo", "music_offtopic"],
|
||||||
|
maxNumberOfActiveWarnings: 3,
|
||||||
|
hoursAfterWarningExpires: 24,
|
||||||
|
adminUserID: "",
|
||||||
|
discordCompletelyIncorrectReportWebhookURL: "",
|
||||||
|
discordFirstTimeSubmissionsWebhookURL: "",
|
||||||
|
discordNeuralBlockRejectWebhookURL: "",
|
||||||
|
discordReportChannelWebhookURL: "",
|
||||||
|
getTopUsersCacheTimeMinutes: 0,
|
||||||
|
globalSalt: null,
|
||||||
|
mode: "",
|
||||||
|
neuralBlockURL: null,
|
||||||
|
proxySubmission: null,
|
||||||
|
rateLimit: {
|
||||||
|
vote: {
|
||||||
|
windowMs: 900000,
|
||||||
|
max: 20,
|
||||||
|
message: "Too many votes, please try again later",
|
||||||
|
statusCode: 429,
|
||||||
|
},
|
||||||
|
view: {
|
||||||
|
windowMs: 900000,
|
||||||
|
max: 20,
|
||||||
|
statusCode: 200,
|
||||||
|
message: "", // TODO TYPESCRIPT whats the msg?
|
||||||
|
},
|
||||||
|
},
|
||||||
|
userCounterURL: null,
|
||||||
|
youtubeAPIKey: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add defaults
|
||||||
|
function addDefaults(config: SBSConfig, defaults: SBSConfig) {
|
||||||
|
for (const key in defaults) {
|
||||||
|
if (!config.hasOwnProperty(key)) {
|
||||||
|
// @ts-ignore
|
||||||
|
config[key] = defaults[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/databases/IDatabase.ts
Normal file
13
src/databases/IDatabase.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export interface IDatabase {
|
||||||
|
init(): void;
|
||||||
|
|
||||||
|
prepare(type: QueryType, query: string, params: any[]): any;
|
||||||
|
|
||||||
|
get<TModel>(query: string, params: any[]): TModel;
|
||||||
|
getAll<TModel>(query: string, params: any[]): TModel[];
|
||||||
|
run(query: string, params: any[]): void;
|
||||||
|
|
||||||
|
exec(query: string): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QueryType = 'get' | 'all' | 'run';
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
var MysqlInterface = require('sync-mysql');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
|
|
||||||
class Mysql {
|
|
||||||
constructor(msConfig) {
|
|
||||||
this.connection = new MysqlInterface(msConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
exec(query) {
|
|
||||||
this.prepare('run', query, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
prepare (type, query, params) {
|
|
||||||
logger.debug("prepare (mysql): type: " + type + ", query: " + query + ", params: " + params);
|
|
||||||
if (type === 'get') {
|
|
||||||
return this.connection.query(query, params)[0];
|
|
||||||
} else if (type === 'run') {
|
|
||||||
this.connection.query(query, params);
|
|
||||||
} else if (type === 'all') {
|
|
||||||
return this.connection.query(query, params);
|
|
||||||
} else {
|
|
||||||
logger.warn('returning undefined...');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Mysql;
|
|
||||||
49
src/databases/Mysql.ts
Normal file
49
src/databases/Mysql.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {IDatabase, QueryType} from './IDatabase';
|
||||||
|
// @ts-ignore
|
||||||
|
import MysqlInterface from 'sync-mysql';
|
||||||
|
|
||||||
|
export class Mysql implements IDatabase {
|
||||||
|
private connection: any;
|
||||||
|
|
||||||
|
constructor(private config: any) {
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.connection = new MysqlInterface(this.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(query: string) {
|
||||||
|
this.prepare('run', query, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(type: QueryType, query: string, params: any[]) {
|
||||||
|
Logger.debug(`prepare (mysql): type: ${type}, query: ${query}, params: ${params}`);
|
||||||
|
const queryResult = this.connection.query(query, params);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'get': {
|
||||||
|
return queryResult[0];
|
||||||
|
}
|
||||||
|
case 'all': {
|
||||||
|
return queryResult;
|
||||||
|
}
|
||||||
|
case 'run': {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get<TModel>(query: string, params: any[]): TModel {
|
||||||
|
return this.prepare('get', query, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAll<TModel>(query: string, params: any[]): TModel[] {
|
||||||
|
return this.prepare('all', query, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public run(query: string, params: any[]): void {
|
||||||
|
this.prepare('run', query, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
const { db } = require("./databases");
|
|
||||||
var config = require('../config.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
|
|
||||||
class Sqlite {
|
|
||||||
constructor(connection) {
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
getConnection() {
|
|
||||||
return this.connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepare(type, query, params) {
|
|
||||||
if (type === 'get') {
|
|
||||||
return this.connection.prepare(query).get(...params);
|
|
||||||
} else if (type === 'run') {
|
|
||||||
this.connection.prepare(query).run(...params);
|
|
||||||
} else if (type === 'all') {
|
|
||||||
return this.connection.prepare(query).all(...params);
|
|
||||||
} else {
|
|
||||||
logger.debug('sqlite query: returning undefined')
|
|
||||||
logger.debug("prepare: type: " + type + ", query: " + query + ", params: " + params);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exec(query) {
|
|
||||||
return this.connection.exec(query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Sqlite;
|
|
||||||
107
src/databases/Sqlite.ts
Normal file
107
src/databases/Sqlite.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import {IDatabase, QueryType} from './IDatabase';
|
||||||
|
import Sqlite3, {Database, Database as SQLiteDatabase} from 'better-sqlite3';
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
|
||||||
|
export class Sqlite implements IDatabase {
|
||||||
|
private db: SQLiteDatabase;
|
||||||
|
|
||||||
|
constructor(private config: SqliteConfig)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare(type: QueryType, query: string, params: any[]) {
|
||||||
|
const preparedQuery = this.db.prepare(query);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'get': {
|
||||||
|
return preparedQuery.get(...params);
|
||||||
|
}
|
||||||
|
case 'all': {
|
||||||
|
return preparedQuery.all(...params);
|
||||||
|
}
|
||||||
|
case 'run': {
|
||||||
|
preparedQuery.run(...params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get<TModel>(query: string, params: any[]): TModel {
|
||||||
|
return this.prepare('get', query, params);
|
||||||
|
}
|
||||||
|
getAll<TModel>(query: string, params: any[]): TModel[] {
|
||||||
|
return this.prepare('all', query, params);
|
||||||
|
}
|
||||||
|
run(query: string, params: any[]): void {
|
||||||
|
this.prepare('run', query, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(query: string) {
|
||||||
|
return this.db.exec(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// Make dirs if required
|
||||||
|
if (!fs.existsSync(path.join(this.config.dbPath, "../"))) {
|
||||||
|
fs.mkdirSync(path.join(this.config.dbPath, "../"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.db = new Sqlite3(this.config.dbPath, {readonly: this.config.readOnly, fileMustExist: !this.config.createDbIfNotExists});
|
||||||
|
|
||||||
|
if (this.config.createDbIfNotExists && !this.config.readOnly && fs.existsSync(this.config.dbSchemaFileName)) {
|
||||||
|
this.db.exec(fs.readFileSync(this.config.dbSchemaFileName).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.config.readOnly) {
|
||||||
|
this.db.function("sha256", (str: string) => {
|
||||||
|
return getHash(str, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Upgrade database if required
|
||||||
|
Sqlite.upgradeDB(this.db, this.config.fileNamePrefix, this.config.dbSchemaFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable WAL mode checkpoint number
|
||||||
|
if (this.config.enableWalCheckpointNumber) {
|
||||||
|
this.db.exec("PRAGMA journal_mode=WAL;");
|
||||||
|
this.db.exec("PRAGMA wal_autocheckpoint=1;");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable Memory-Mapped IO
|
||||||
|
this.db.exec("pragma mmap_size= 500000000;");
|
||||||
|
}
|
||||||
|
|
||||||
|
attachDatabase(database: string, attachAs: string) {
|
||||||
|
this.db.prepare(`ATTACH ? as ${attachAs}`).run(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static upgradeDB(db: Database, fileNamePrefix: string, schemaFolder: string) {
|
||||||
|
const versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version");
|
||||||
|
let versionCode = versionCodeInfo ? versionCodeInfo.value : 0;
|
||||||
|
|
||||||
|
let path = schemaFolder + "/_upgrade_" + fileNamePrefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
||||||
|
Logger.debug('db update: trying ' + path);
|
||||||
|
while (fs.existsSync(path)) {
|
||||||
|
Logger.debug('db update: updating ' + path);
|
||||||
|
db.exec(fs.readFileSync(path).toString());
|
||||||
|
|
||||||
|
versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value;
|
||||||
|
path = schemaFolder + "/_upgrade_" + fileNamePrefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
||||||
|
Logger.debug('db update: trying ' + path);
|
||||||
|
}
|
||||||
|
Logger.debug('db update: no file ' + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SqliteConfig {
|
||||||
|
dbPath: string;
|
||||||
|
dbSchemaFileName: string;
|
||||||
|
dbSchemaFolder: string;
|
||||||
|
fileNamePrefix: string;
|
||||||
|
readOnly: boolean;
|
||||||
|
createDbIfNotExists: boolean;
|
||||||
|
enableWalCheckpointNumber: boolean
|
||||||
|
}
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
var config = require('../config.js');
|
|
||||||
var Sqlite3 = require('better-sqlite3');
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var Sqlite = require('./Sqlite.js')
|
|
||||||
var Mysql = require('./Mysql.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
const getHash = require('../utils/getHash.js');
|
|
||||||
|
|
||||||
let options = {
|
|
||||||
readonly: config.readOnly,
|
|
||||||
fileMustExist: !config.createDatabaseIfNotExist
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.mysql) {
|
|
||||||
module.exports = {
|
|
||||||
db: new Mysql(config.mysql),
|
|
||||||
privateDB: new Mysql(config.privateMysql)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Make dirs if required
|
|
||||||
if (!fs.existsSync(path.join(config.db, "../"))) {
|
|
||||||
fs.mkdirSync(path.join(config.db, "../"));
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(path.join(config.privateDB, "../"))) {
|
|
||||||
fs.mkdirSync(path.join(config.privateDB, "../"));
|
|
||||||
}
|
|
||||||
|
|
||||||
var db = new Sqlite3(config.db, options);
|
|
||||||
var privateDB = new Sqlite3(config.privateDB, options);
|
|
||||||
|
|
||||||
if (config.createDatabaseIfNotExist && !config.readOnly) {
|
|
||||||
if (fs.existsSync(config.dbSchema)) db.exec(fs.readFileSync(config.dbSchema).toString());
|
|
||||||
if (fs.existsSync(config.privateDBSchema)) privateDB.exec(fs.readFileSync(config.privateDBSchema).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.readOnly) {
|
|
||||||
db.function("sha256", (string) => {
|
|
||||||
return getHash(string, 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Upgrade database if required
|
|
||||||
ugradeDB(db, "sponsorTimes");
|
|
||||||
ugradeDB(privateDB, "private")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach private db to main db
|
|
||||||
db.prepare("ATTACH ? as privateDB").run(config.privateDB);
|
|
||||||
|
|
||||||
// Enable WAL mode checkpoint number
|
|
||||||
if (!config.readOnly && config.mode === "production") {
|
|
||||||
db.exec("PRAGMA journal_mode=WAL;");
|
|
||||||
db.exec("PRAGMA wal_autocheckpoint=1;");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable Memory-Mapped IO
|
|
||||||
db.exec("pragma mmap_size= 500000000;");
|
|
||||||
privateDB.exec("pragma mmap_size= 500000000;");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
db: new Sqlite(db),
|
|
||||||
privateDB: new Sqlite(privateDB)
|
|
||||||
};
|
|
||||||
|
|
||||||
function ugradeDB(db, prefix) {
|
|
||||||
let versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version");
|
|
||||||
let versionCode = versionCodeInfo ? versionCodeInfo.value : 0;
|
|
||||||
|
|
||||||
let path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
|
||||||
logger.debug('db update: trying ' + path);
|
|
||||||
while (fs.existsSync(path)) {
|
|
||||||
logger.debug('db update: updating ' + path);
|
|
||||||
db.exec(fs.readFileSync(path).toString());
|
|
||||||
|
|
||||||
versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value;
|
|
||||||
path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
|
||||||
logger.debug('db update: trying ' + path);
|
|
||||||
}
|
|
||||||
logger.debug('db update: no file ' + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
47
src/databases/databases.ts
Normal file
47
src/databases/databases.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
import {Sqlite} from './Sqlite';
|
||||||
|
import {Mysql} from './Mysql';
|
||||||
|
import {IDatabase} from './IDatabase';
|
||||||
|
|
||||||
|
|
||||||
|
let db: IDatabase;
|
||||||
|
let privateDB: IDatabase;
|
||||||
|
if (config.mysql) {
|
||||||
|
db = new Mysql(config.mysql);
|
||||||
|
privateDB = new Mysql(config.privateMysql);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
db = new Sqlite({
|
||||||
|
dbPath: config.db,
|
||||||
|
dbSchemaFileName: config.dbSchema,
|
||||||
|
dbSchemaFolder: config.schemaFolder,
|
||||||
|
fileNamePrefix: 'sponsorTimes',
|
||||||
|
readOnly: config.readOnly,
|
||||||
|
createDbIfNotExists: config.createDatabaseIfNotExist,
|
||||||
|
enableWalCheckpointNumber: !config.readOnly && config.mode === "production"
|
||||||
|
});
|
||||||
|
privateDB = new Sqlite({
|
||||||
|
dbPath: config.privateDB,
|
||||||
|
dbSchemaFileName: config.privateDBSchema,
|
||||||
|
dbSchemaFolder: config.schemaFolder,
|
||||||
|
fileNamePrefix: 'private',
|
||||||
|
readOnly: config.readOnly,
|
||||||
|
createDbIfNotExists: config.createDatabaseIfNotExist,
|
||||||
|
enableWalCheckpointNumber: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function initDb() {
|
||||||
|
db.init();
|
||||||
|
privateDB.init();
|
||||||
|
|
||||||
|
if (db instanceof Sqlite) {
|
||||||
|
// Attach private db to main db
|
||||||
|
(db as Sqlite).attachDatabase(config.privateDB, 'privateDB');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
db,
|
||||||
|
privateDB,
|
||||||
|
initDb,
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = function corsMiddleware(req, res, next) {
|
|
||||||
res.header("Access-Control-Allow-Origin", "*");
|
|
||||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
7
src/middleware/cors.ts
Normal file
7
src/middleware/cors.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import {NextFunction, Request, Response} from 'express';
|
||||||
|
|
||||||
|
export function corsMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||||
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
|
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
|
||||||
|
next();
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
const log = require('../utils/logger.js'); // log not logger to not interfere with function name
|
|
||||||
|
|
||||||
module.exports = function logger (req, res, next) {
|
|
||||||
log.info("Request recieved: " + req.method + " " + req.url);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
7
src/middleware/logger.ts
Normal file
7
src/middleware/logger.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {NextFunction, Request, Response} from 'express';
|
||||||
|
|
||||||
|
export function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
|
||||||
|
Logger.info(`Request received: ${req.method} ${req.url}`);
|
||||||
|
next();
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
const getIP = require('../utils/getIP.js');
|
|
||||||
const getHash = require('../utils/getHash.js');
|
|
||||||
const rateLimit = require('express-rate-limit');
|
|
||||||
|
|
||||||
module.exports = (limitConfig) => rateLimit({
|
|
||||||
windowMs: limitConfig.windowMs,
|
|
||||||
max: limitConfig.max,
|
|
||||||
message: limitConfig.message,
|
|
||||||
statusCode: limitConfig.statusCode,
|
|
||||||
headers: false,
|
|
||||||
keyGenerator: (req /*, res*/) => {
|
|
||||||
return getHash(getIP(req), 1);
|
|
||||||
},
|
|
||||||
skip: (/*req, res*/) => {
|
|
||||||
// skip rate limit if running in test mode
|
|
||||||
return process.env.npm_lifecycle_script === 'node test.js';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
21
src/middleware/requestRateLimit.ts
Normal file
21
src/middleware/requestRateLimit.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import {getIP} from '../utils/getIP';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import rateLimit from 'express-rate-limit';
|
||||||
|
import {RateLimitConfig} from '../types/config.model';
|
||||||
|
|
||||||
|
export function rateLimitMiddleware(limitConfig: RateLimitConfig): rateLimit.RateLimit {
|
||||||
|
return rateLimit({
|
||||||
|
windowMs: limitConfig.windowMs,
|
||||||
|
max: limitConfig.max,
|
||||||
|
message: limitConfig.message,
|
||||||
|
statusCode: limitConfig.statusCode,
|
||||||
|
headers: false,
|
||||||
|
keyGenerator: (req) => {
|
||||||
|
return getHash(getIP(req), 1);
|
||||||
|
},
|
||||||
|
skip: (/*req, res*/) => {
|
||||||
|
// skip rate limit if running in test mode
|
||||||
|
return process.env.npm_lifecycle_script === 'ts-node test.ts';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
const fetch = require('node-fetch');
|
|
||||||
|
|
||||||
const config = require('../config.js');
|
|
||||||
const getIP = require('../utils/getIP.js');
|
|
||||||
const getHash = require('../utils/getHash.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
|
|
||||||
module.exports = function userCounter(req, res, next) {
|
|
||||||
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))
|
|
||||||
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
13
src/middleware/userCounter.ts
Normal file
13
src/middleware/userCounter.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import fetch from 'node-fetch';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {config} from '../config';
|
||||||
|
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) {
|
||||||
|
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));
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
@@ -1,19 +1,15 @@
|
|||||||
var fs = require('fs');
|
import {getHash} from '../utils/getHash';
|
||||||
var config = require('../config.js');
|
import {db} from '../databases/databases';
|
||||||
|
import {config} from '../config';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
var db = require('../databases/databases.js').db;
|
export async function addUserAsVIP(req: Request, res: Response) {
|
||||||
var getHash = require('../utils/getHash.js');
|
const userID = req.query.userID as string;
|
||||||
|
let adminUserIDInput = req.query.adminUserID as string;
|
||||||
|
|
||||||
module.exports = async function addUserAsVIP (req, res) {
|
const enabled = req.query.enabled === undefined
|
||||||
let userID = req.query.userID;
|
? false
|
||||||
let adminUserIDInput = req.query.adminUserID;
|
: req.query.enabled === 'true';
|
||||||
|
|
||||||
let enabled = req.query.enabled;
|
|
||||||
if (enabled === undefined){
|
|
||||||
enabled = true;
|
|
||||||
} else {
|
|
||||||
enabled = enabled === "true";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userID == undefined || adminUserIDInput == undefined) {
|
if (userID == undefined || adminUserIDInput == undefined) {
|
||||||
//invalid request
|
//invalid request
|
||||||
@@ -31,7 +27,7 @@ module.exports = async function addUserAsVIP (req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check to see if this user is already a vip
|
//check to see if this user is already a vip
|
||||||
let row = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]);
|
const row = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
if (enabled && row.userCount == 0) {
|
if (enabled && row.userCount == 0) {
|
||||||
//add them to the vip list
|
//add them to the vip list
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
const db = require('../databases/databases.js').db;
|
import {Request, Response} from 'express';
|
||||||
const getHash = require('../utils/getHash.js');
|
import {isUserVIP} from '../utils/isUserVIP';
|
||||||
const isUserVIP = require('../utils/isUserVIP.js');
|
import {getHash} from '../utils/getHash';
|
||||||
const logger = require('../utils/logger.js');
|
import {db} from '../databases/databases';
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
export function deleteNoSegments(req: Request, res: Response) {
|
||||||
// Collect user input data
|
// Collect user input data
|
||||||
let videoID = req.body.videoID;
|
const videoID = req.body.videoID;
|
||||||
let userID = req.body.userID;
|
let userID = req.body.userID;
|
||||||
let categories = req.body.categories;
|
const categories = req.body.categories;
|
||||||
|
|
||||||
// Check input data is valid
|
// Check input data is valid
|
||||||
if (!videoID
|
if (!videoID
|
||||||
@@ -17,27 +17,27 @@ module.exports = (req, res) => {
|
|||||||
|| categories.length === 0
|
|| categories.length === 0
|
||||||
) {
|
) {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
message: 'Bad Format'
|
message: 'Bad Format',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user is VIP
|
// Check if user is VIP
|
||||||
userID = getHash(userID);
|
userID = getHash(userID);
|
||||||
let userIsVIP = isUserVIP(userID);
|
const userIsVIP = isUserVIP(userID);
|
||||||
|
|
||||||
if (!userIsVIP) {
|
if (!userIsVIP) {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
message: 'Must be a VIP to mark videos.'
|
message: 'Must be a VIP to mark videos.',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.prepare("all", 'SELECT * FROM noSegments WHERE videoID = ?', [videoID]).filter((entry) => {
|
db.prepare("all", 'SELECT * FROM noSegments WHERE videoID = ?', [videoID]).filter((entry: any) => {
|
||||||
return (categories.indexOf(entry.category) !== -1);
|
return (categories.indexOf(entry.category) !== -1);
|
||||||
}).forEach((entry) => {
|
}).forEach((entry: any) => {
|
||||||
db.prepare('run', 'DELETE FROM noSegments WHERE videoID = ? AND category = ?', [videoID, entry.category]);
|
db.prepare('run', 'DELETE FROM noSegments WHERE videoID = ? AND category = ?', [videoID, entry.category]);
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({message: 'Removed no segments entrys for video ' + videoID});
|
res.status(200).json({message: 'Removed no segments entrys for video ' + videoID});
|
||||||
};
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
|
||||||
|
|
||||||
module.exports = function getDaysSavedFormatted (req, res) {
|
|
||||||
let row = db.prepare('get', "SELECT SUM((endTime - startTime) / 60 / 60 / 24 * views) as daysSaved from sponsorTimes where shadowHidden != 1", []);
|
|
||||||
|
|
||||||
if (row !== undefined) {
|
|
||||||
//send this result
|
|
||||||
res.send({
|
|
||||||
daysSaved: row.daysSaved.toFixed(2)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
src/routes/getDaysSavedFormatted.ts
Normal file
13
src/routes/getDaysSavedFormatted.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
export function getDaysSavedFormatted(req: Request, res: Response) {
|
||||||
|
let row = db.prepare('get', "SELECT SUM((endTime - startTime) / 60 / 60 / 24 * views) as daysSaved from sponsorTimes where shadowHidden != 1", []);
|
||||||
|
|
||||||
|
if (row !== undefined) {
|
||||||
|
//send this result
|
||||||
|
res.send({
|
||||||
|
daysSaved: row.daysSaved.toFixed(2),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
import {Logger} from '../utils/logger';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {isUserVIP} from '../utils/isUserVIP';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
var getHash = require('../utils/getHash.js');
|
export function getIsUserVIP(req: Request, res: Response): void {
|
||||||
const logger = require('../utils/logger.js');
|
let userID = req.query.userID as string;
|
||||||
const isUserVIP = require('../utils/isUserVIP.js');
|
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
|
|
||||||
if (userID == undefined) {
|
if (userID == undefined) {
|
||||||
//invalid request
|
//invalid request
|
||||||
@@ -20,10 +19,10 @@ module.exports = (req, res) => {
|
|||||||
let vipState = isUserVIP(userID);
|
let vipState = isUserVIP(userID);
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
hashedUserID: userID,
|
hashedUserID: userID,
|
||||||
vip: vipState
|
vip: vipState,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
Logger.error(err);
|
||||||
res.sendStatus(500);
|
res.sendStatus(500);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
|
||||||
var getHash = require('../utils/getHash.js');
|
|
||||||
|
|
||||||
module.exports = function getSavedTimeForUser (req, res) {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
|
|
||||||
if (userID == undefined) {
|
|
||||||
//invalid request
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//hash the userID
|
|
||||||
userID = getHash(userID);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let row = db.prepare("get", "SELECT SUM((endTime - startTime) / 60 * views) as minutesSaved FROM sponsorTimes WHERE userID = ? AND votes > -1 AND shadowHidden != 1 ", [userID]);
|
|
||||||
|
|
||||||
if (row.minutesSaved != null) {
|
|
||||||
res.send({
|
|
||||||
timeSaved: row.minutesSaved
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
res.sendStatus(500);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
33
src/routes/getSavedTimeForUser.ts
Normal file
33
src/routes/getSavedTimeForUser.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
|
||||||
|
export function getSavedTimeForUser(req: Request, res: Response) {
|
||||||
|
let userID = req.query.userID as string;
|
||||||
|
|
||||||
|
if (userID == undefined) {
|
||||||
|
//invalid request
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//hash the userID
|
||||||
|
userID = getHash(userID);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let row = db.prepare("get", "SELECT SUM((endTime - startTime) / 60 * views) as minutesSaved FROM sponsorTimes WHERE userID = ? AND votes > -1 AND shadowHidden != 1 ", [userID]);
|
||||||
|
|
||||||
|
if (row.minutesSaved != null) {
|
||||||
|
res.send({
|
||||||
|
timeSaved: row.minutesSaved,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
var config = require('../config.js');
|
import {config} from '../config';
|
||||||
|
import {db, privateDB} from '../databases/databases';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {getIP} from '../utils/getIP';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
var databases = require('../databases/databases.js');
|
|
||||||
var db = databases.db;
|
|
||||||
var privateDB = databases.privateDB;
|
|
||||||
|
|
||||||
var logger = require('../utils/logger.js');
|
function cleanGetSegments(req: Request, videoID: string, categories: any[]) {
|
||||||
var getHash = require('../utils/getHash.js');
|
let userHashedIP: any;
|
||||||
var getIP = require('../utils/getIP.js');
|
let shadowHiddenSegments: any[];
|
||||||
|
|
||||||
function cleanGetSegments(req, videoID, categories) {
|
let segments: { category: any; segment: any[]; UUID: any; }[] = [];
|
||||||
let userHashedIP, shadowHiddenSegments;
|
|
||||||
|
|
||||||
let segments = [];
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
const categorySegments = db
|
const categorySegments: any[] = db
|
||||||
.prepare(
|
.prepare(
|
||||||
'all',
|
'all',
|
||||||
'SELECT startTime, endTime, votes, UUID, shadowHidden FROM sponsorTimes WHERE videoID = ? and category = ? ORDER BY startTime',
|
'SELECT startTime, endTime, votes, UUID, shadowHidden FROM sponsorTimes WHERE videoID = ? and category = ? ORDER BY startTime',
|
||||||
[videoID, category]
|
[videoID, category],
|
||||||
)
|
)
|
||||||
.filter(segment => {
|
.filter((segment: any) => {
|
||||||
if (segment.votes < -1) {
|
if (segment.votes < -1) {
|
||||||
return false; //too untrustworthy, just ignore it
|
return false; //too untrustworthy, just ignore it
|
||||||
}
|
}
|
||||||
@@ -46,7 +45,7 @@ function cleanGetSegments(req, videoID, categories) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
chooseSegments(categorySegments).forEach(chosenSegment => {
|
chooseSegments(categorySegments).forEach((chosenSegment: any) => {
|
||||||
segments.push({
|
segments.push({
|
||||||
category,
|
category,
|
||||||
segment: [chosenSegment.startTime, chosenSegment.endTime],
|
segment: [chosenSegment.startTime, chosenSegment.endTime],
|
||||||
@@ -58,7 +57,7 @@ function cleanGetSegments(req, videoID, categories) {
|
|||||||
return segments;
|
return segments;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
Logger.error(err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,7 +66,7 @@ function cleanGetSegments(req, videoID, categories) {
|
|||||||
//gets a weighted random choice from the choices array based on their `votes` property.
|
//gets a weighted random choice from the choices array based on their `votes` property.
|
||||||
//amountOfChoices specifies the maximum amount of choices to return, 1 or more.
|
//amountOfChoices specifies the maximum amount of choices to return, 1 or more.
|
||||||
//choices are unique
|
//choices are unique
|
||||||
function getWeightedRandomChoice(choices, amountOfChoices) {
|
function getWeightedRandomChoice(choices: any[], amountOfChoices: number) {
|
||||||
//trivial case: no need to go through the whole process
|
//trivial case: no need to go through the whole process
|
||||||
if (amountOfChoices >= choices.length) {
|
if (amountOfChoices >= choices.length) {
|
||||||
return choices;
|
return choices;
|
||||||
@@ -79,10 +78,10 @@ function getWeightedRandomChoice(choices, amountOfChoices) {
|
|||||||
//The 3 makes -2 the minimum votes before being ignored completely
|
//The 3 makes -2 the minimum votes before being ignored completely
|
||||||
//https://www.desmos.com/calculator/c1duhfrmts
|
//https://www.desmos.com/calculator/c1duhfrmts
|
||||||
//this can be changed if this system increases in popularity.
|
//this can be changed if this system increases in popularity.
|
||||||
const weight = Math.exp((choice.votes + 3), 0.85);
|
const weight = Math.exp((choice.votes + 3)); // TODO TYPESCRIPT what was this 0.85?
|
||||||
totalWeight += weight;
|
totalWeight += weight;
|
||||||
|
|
||||||
return { ...choice, weight };
|
return {...choice, weight};
|
||||||
});
|
});
|
||||||
|
|
||||||
//iterate and find amountOfChoices choices
|
//iterate and find amountOfChoices choices
|
||||||
@@ -109,18 +108,18 @@ function getWeightedRandomChoice(choices, amountOfChoices) {
|
|||||||
//Only one similar time will be returned, randomly generated based on the sqrt of votes.
|
//Only one similar time will be returned, randomly generated based on the sqrt of votes.
|
||||||
//This allows new less voted items to still sometimes appear to give them a chance at getting votes.
|
//This allows new less voted items to still sometimes appear to give them a chance at getting votes.
|
||||||
//Segments with less than -1 votes are already ignored before this function is called
|
//Segments with less than -1 votes are already ignored before this function is called
|
||||||
function chooseSegments(segments) {
|
function chooseSegments(segments: any[]) {
|
||||||
//Create groups of segments that are similar to eachother
|
//Create groups of segments that are similar to eachother
|
||||||
//Segments must be sorted by their startTime so that we can build groups chronologically:
|
//Segments must be sorted by their startTime so that we can build groups chronologically:
|
||||||
//1. As long as the segments' startTime fall inside the currentGroup, we keep adding them to that group
|
//1. As long as the segments' startTime fall inside the currentGroup, we keep adding them to that group
|
||||||
//2. If a segment starts after the end of the currentGroup (> cursor), no other segment will ever fall
|
//2. If a segment starts after the end of the currentGroup (> cursor), no other segment will ever fall
|
||||||
// inside that group (because they're sorted) so we can create a new one
|
// inside that group (because they're sorted) so we can create a new one
|
||||||
const similarSegmentsGroups = [];
|
const similarSegmentsGroups: any[] = [];
|
||||||
let currentGroup;
|
let currentGroup: any;
|
||||||
let cursor = -1; //-1 to make sure that, even if the 1st segment starts at 0, a new group is created
|
let cursor = -1; //-1 to make sure that, even if the 1st segment starts at 0, a new group is created
|
||||||
segments.forEach(segment => {
|
segments.forEach(segment => {
|
||||||
if (segment.startTime > cursor) {
|
if (segment.startTime > cursor) {
|
||||||
currentGroup = { segments: [], votes: 0 };
|
currentGroup = {segments: [], votes: 0};
|
||||||
similarSegmentsGroups.push(currentGroup);
|
similarSegmentsGroups.push(currentGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +135,7 @@ function chooseSegments(segments) {
|
|||||||
//if there are too many groups, find the best 8
|
//if there are too many groups, find the best 8
|
||||||
return getWeightedRandomChoice(similarSegmentsGroups, 8).map(
|
return getWeightedRandomChoice(similarSegmentsGroups, 8).map(
|
||||||
//randomly choose 1 good segment per group and return them
|
//randomly choose 1 good segment per group and return them
|
||||||
group => getWeightedRandomChoice(group.segments, 1)[0]
|
group => getWeightedRandomChoice(group.segments, 1)[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,15 +149,15 @@ function chooseSegments(segments) {
|
|||||||
*
|
*
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function handleGetSegments(req, res) {
|
function handleGetSegments(req: Request, res: Response) {
|
||||||
const videoID = req.query.videoID;
|
const videoID = req.query.videoID as string;
|
||||||
// Default to sponsor
|
// Default to sponsor
|
||||||
// If using params instead of JSON, only one category can be pulled
|
// If using params instead of JSON, only one category can be pulled
|
||||||
const categories = req.query.categories
|
const categories = req.query.categories
|
||||||
? JSON.parse(req.query.categories)
|
? JSON.parse(req.query.categories as string)
|
||||||
: req.query.category
|
: req.query.category
|
||||||
? [req.query.category]
|
? [req.query.category]
|
||||||
: ['sponsor'];
|
: ['sponsor'];
|
||||||
|
|
||||||
let segments = cleanGetSegments(req, videoID, categories);
|
let segments = cleanGetSegments(req, videoID, categories);
|
||||||
|
|
||||||
@@ -175,15 +174,17 @@ function handleGetSegments(req, res) {
|
|||||||
return segments;
|
return segments;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
function endpoint(req: Request, res: Response) {
|
||||||
|
const segments = handleGetSegments(req, res);
|
||||||
|
|
||||||
|
if (segments) {
|
||||||
|
//send result
|
||||||
|
res.send(segments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
handleGetSegments,
|
handleGetSegments,
|
||||||
cleanGetSegments,
|
cleanGetSegments,
|
||||||
endpoint: function (req, res) {
|
endpoint,
|
||||||
let segments = handleGetSegments(req, res);
|
|
||||||
|
|
||||||
if (segments) {
|
|
||||||
//send result
|
|
||||||
res.send(segments);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
const hashPrefixTester = require('../utils/hashPrefixTester.js');
|
import {hashPrefixTester} from '../utils/hashPrefixTester';
|
||||||
const getSegments = require('./getSkipSegments.js').cleanGetSegments;
|
import {cleanGetSegments} from './getSkipSegments';
|
||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
const databases = require('../databases/databases.js');
|
export async function getSkipSegmentsByHash(req: Request, res: Response) {
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
const db = databases.db;
|
|
||||||
|
|
||||||
module.exports = async function (req, res) {
|
|
||||||
let hashPrefix = req.params.prefix;
|
let hashPrefix = req.params.prefix;
|
||||||
if (!hashPrefixTester(req.params.prefix)) {
|
if (!hashPrefixTester(req.params.prefix)) {
|
||||||
res.status(400).send("Hash prefix does not match format requirements."); // Exit early on faulty prefix
|
res.status(400).send("Hash prefix does not match format requirements."); // Exit early on faulty prefix
|
||||||
@@ -13,19 +11,19 @@ module.exports = async function (req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const categories = req.query.categories
|
const categories = req.query.categories
|
||||||
? JSON.parse(req.query.categories)
|
? JSON.parse(req.query.categories as string)
|
||||||
: req.query.category
|
: req.query.category
|
||||||
? [req.query.category]
|
? [req.query.category]
|
||||||
: ['sponsor'];
|
: ['sponsor'];
|
||||||
|
|
||||||
// Get all video id's that match hash prefix
|
// Get all video id's that match hash prefix
|
||||||
const videoIds = db.prepare('all', 'SELECT DISTINCT videoId, hashedVideoID from sponsorTimes WHERE hashedVideoID LIKE ?', [hashPrefix+'%']);
|
const videoIds = db.prepare('all', 'SELECT DISTINCT videoId, hashedVideoID from sponsorTimes WHERE hashedVideoID LIKE ?', [hashPrefix + '%']);
|
||||||
|
|
||||||
let segments = videoIds.map((video) => {
|
let segments = videoIds.map((video: any) => {
|
||||||
return {
|
return {
|
||||||
videoID: video.videoID,
|
videoID: video.videoID,
|
||||||
hash: video.hashedVideoID,
|
hash: video.hashedVideoID,
|
||||||
segments: getSegments(req, video.videoID, categories)
|
segments: cleanGetSegments(req, video.videoID, categories),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
const createMemoryCache = require('../utils/createMemoryCache.js');
|
|
||||||
const config = require('../config.js');
|
|
||||||
|
|
||||||
const MILLISECONDS_IN_MINUTE = 60000;
|
|
||||||
const getTopUsersWithCache = createMemoryCache(generateTopUsersStats, config.getTopUsersCacheTimeMinutes * MILLISECONDS_IN_MINUTE);
|
|
||||||
|
|
||||||
function generateTopUsersStats(sortBy, categoryStatsEnabled = false) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const userNames = [];
|
|
||||||
const viewCounts = [];
|
|
||||||
const totalSubmissions = [];
|
|
||||||
const minutesSaved = [];
|
|
||||||
const categoryStats = categoryStatsEnabled ? [] : undefined;
|
|
||||||
|
|
||||||
let additionalFields = '';
|
|
||||||
if (categoryStatsEnabled) {
|
|
||||||
additionalFields += "SUM(CASE WHEN category = 'sponsor' THEN 1 ELSE 0 END) as categorySponsor, " +
|
|
||||||
"SUM(CASE WHEN category = 'intro' THEN 1 ELSE 0 END) as categorySumIntro, " +
|
|
||||||
"SUM(CASE WHEN category = 'outro' THEN 1 ELSE 0 END) as categorySumOutro, " +
|
|
||||||
"SUM(CASE WHEN category = 'interaction' THEN 1 ELSE 0 END) as categorySumInteraction, " +
|
|
||||||
"SUM(CASE WHEN category = 'selfpromo' THEN 1 ELSE 0 END) as categorySelfpromo, " +
|
|
||||||
"SUM(CASE WHEN category = 'music_offtopic' THEN 1 ELSE 0 END) as categoryMusicOfftopic, ";
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = db.prepare('all', "SELECT COUNT(*) as totalSubmissions, SUM(views) as viewCount," +
|
|
||||||
"SUM((sponsorTimes.endTime - sponsorTimes.startTime) / 60 * sponsorTimes.views) as minutesSaved, " +
|
|
||||||
"SUM(votes) as userVotes, " +
|
|
||||||
additionalFields +
|
|
||||||
"IFNULL(userNames.userName, sponsorTimes.userID) as userName FROM sponsorTimes LEFT JOIN userNames ON sponsorTimes.userID=userNames.userID " +
|
|
||||||
"LEFT JOIN privateDB.shadowBannedUsers ON sponsorTimes.userID=privateDB.shadowBannedUsers.userID " +
|
|
||||||
"WHERE sponsorTimes.votes > -1 AND sponsorTimes.shadowHidden != 1 AND privateDB.shadowBannedUsers.userID IS NULL " +
|
|
||||||
"GROUP BY IFNULL(userName, sponsorTimes.userID) HAVING userVotes > 20 " +
|
|
||||||
"ORDER BY " + sortBy + " DESC LIMIT 100", []);
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
|
||||||
userNames[i] = rows[i].userName;
|
|
||||||
|
|
||||||
viewCounts[i] = rows[i].viewCount;
|
|
||||||
totalSubmissions[i] = rows[i].totalSubmissions;
|
|
||||||
minutesSaved[i] = rows[i].minutesSaved;
|
|
||||||
if (categoryStatsEnabled) {
|
|
||||||
categoryStats[i] = [
|
|
||||||
rows[i].categorySponsor,
|
|
||||||
rows[i].categorySumIntro,
|
|
||||||
rows[i].categorySumOutro,
|
|
||||||
rows[i].categorySumInteraction,
|
|
||||||
rows[i].categorySelfpromo,
|
|
||||||
rows[i].categoryMusicOfftopic,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
userNames,
|
|
||||||
viewCounts,
|
|
||||||
totalSubmissions,
|
|
||||||
minutesSaved,
|
|
||||||
categoryStats
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async function getTopUsers (req, res) {
|
|
||||||
let sortType = req.query.sortType;
|
|
||||||
let categoryStatsEnabled = req.query.categoryStats;
|
|
||||||
|
|
||||||
if (sortType == undefined) {
|
|
||||||
//invalid request
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//setup which sort type to use
|
|
||||||
let sortBy = '';
|
|
||||||
if (sortType == 0) {
|
|
||||||
sortBy = 'minutesSaved';
|
|
||||||
} else if (sortType == 1) {
|
|
||||||
sortBy = 'viewCount';
|
|
||||||
} else if (sortType == 2) {
|
|
||||||
sortBy = 'totalSubmissions';
|
|
||||||
} else {
|
|
||||||
//invalid request
|
|
||||||
return res.sendStatus(400);
|
|
||||||
}
|
|
||||||
|
|
||||||
const stats = await getTopUsersWithCache(sortBy, categoryStatsEnabled);
|
|
||||||
|
|
||||||
//send this result
|
|
||||||
res.send(stats);
|
|
||||||
}
|
|
||||||
92
src/routes/getTopUsers.ts
Normal file
92
src/routes/getTopUsers.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {createMemoryCache} from '../utils/createMemoryCache';
|
||||||
|
import {config} from '../config';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
const MILLISECONDS_IN_MINUTE = 60000;
|
||||||
|
const getTopUsersWithCache = createMemoryCache(generateTopUsersStats, config.getTopUsersCacheTimeMinutes * MILLISECONDS_IN_MINUTE);
|
||||||
|
|
||||||
|
function generateTopUsersStats(sortBy: string, categoryStatsEnabled: boolean = false) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const userNames = [];
|
||||||
|
const viewCounts = [];
|
||||||
|
const totalSubmissions = [];
|
||||||
|
const minutesSaved = [];
|
||||||
|
const categoryStats: any[] = categoryStatsEnabled ? [] : undefined;
|
||||||
|
|
||||||
|
let additionalFields = '';
|
||||||
|
if (categoryStatsEnabled) {
|
||||||
|
additionalFields += "SUM(CASE WHEN category = 'sponsor' THEN 1 ELSE 0 END) as categorySponsor, " +
|
||||||
|
"SUM(CASE WHEN category = 'intro' THEN 1 ELSE 0 END) as categorySumIntro, " +
|
||||||
|
"SUM(CASE WHEN category = 'outro' THEN 1 ELSE 0 END) as categorySumOutro, " +
|
||||||
|
"SUM(CASE WHEN category = 'interaction' THEN 1 ELSE 0 END) as categorySumInteraction, " +
|
||||||
|
"SUM(CASE WHEN category = 'selfpromo' THEN 1 ELSE 0 END) as categorySelfpromo, " +
|
||||||
|
"SUM(CASE WHEN category = 'music_offtopic' THEN 1 ELSE 0 END) as categoryMusicOfftopic, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = db.prepare('all', "SELECT COUNT(*) as totalSubmissions, SUM(views) as viewCount," +
|
||||||
|
"SUM((sponsorTimes.endTime - sponsorTimes.startTime) / 60 * sponsorTimes.views) as minutesSaved, " +
|
||||||
|
"SUM(votes) as userVotes, " +
|
||||||
|
additionalFields +
|
||||||
|
"IFNULL(userNames.userName, sponsorTimes.userID) as userName FROM sponsorTimes LEFT JOIN userNames ON sponsorTimes.userID=userNames.userID " +
|
||||||
|
"LEFT JOIN privateDB.shadowBannedUsers ON sponsorTimes.userID=privateDB.shadowBannedUsers.userID " +
|
||||||
|
"WHERE sponsorTimes.votes > -1 AND sponsorTimes.shadowHidden != 1 AND privateDB.shadowBannedUsers.userID IS NULL " +
|
||||||
|
"GROUP BY IFNULL(userName, sponsorTimes.userID) HAVING userVotes > 20 " +
|
||||||
|
"ORDER BY " + sortBy + " DESC LIMIT 100", []);
|
||||||
|
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
userNames[i] = rows[i].userName;
|
||||||
|
|
||||||
|
viewCounts[i] = rows[i].viewCount;
|
||||||
|
totalSubmissions[i] = rows[i].totalSubmissions;
|
||||||
|
minutesSaved[i] = rows[i].minutesSaved;
|
||||||
|
if (categoryStatsEnabled) {
|
||||||
|
categoryStats[i] = [
|
||||||
|
rows[i].categorySponsor,
|
||||||
|
rows[i].categorySumIntro,
|
||||||
|
rows[i].categorySumOutro,
|
||||||
|
rows[i].categorySumInteraction,
|
||||||
|
rows[i].categorySelfpromo,
|
||||||
|
rows[i].categoryMusicOfftopic,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
userNames,
|
||||||
|
viewCounts,
|
||||||
|
totalSubmissions,
|
||||||
|
minutesSaved,
|
||||||
|
categoryStats,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getTopUsers(req: Request, res: Response) {
|
||||||
|
const sortType = parseInt(req.query.sortType as string);
|
||||||
|
const categoryStatsEnabled = req.query.categoryStats;
|
||||||
|
|
||||||
|
if (sortType == undefined) {
|
||||||
|
//invalid request
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//setup which sort type to use
|
||||||
|
let sortBy = '';
|
||||||
|
if (sortType == 0) {
|
||||||
|
sortBy = 'minutesSaved';
|
||||||
|
} else if (sortType == 1) {
|
||||||
|
sortBy = 'viewCount';
|
||||||
|
} else if (sortType == 2) {
|
||||||
|
sortBy = 'totalSubmissions';
|
||||||
|
} else {
|
||||||
|
//invalid request
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = await getTopUsersWithCache(sortBy, categoryStatsEnabled);
|
||||||
|
|
||||||
|
//send this result
|
||||||
|
res.send(stats);
|
||||||
|
}
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
const db = require('../databases/databases.js').db;
|
import {db} from '../databases/databases';
|
||||||
const request = require('request');
|
import request from 'request';
|
||||||
const config = require('../config.js');
|
import {config} from '../config';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
// A cache of the number of chrome web store users
|
// A cache of the number of chrome web store users
|
||||||
let chromeUsersCache = null;
|
let chromeUsersCache = 0;
|
||||||
let firefoxUsersCache = null;
|
let firefoxUsersCache = 0;
|
||||||
|
|
||||||
// By the privacy friendly user counter
|
// By the privacy friendly user counter
|
||||||
let apiUsersCache = null;
|
let apiUsersCache = 0;
|
||||||
|
|
||||||
let lastUserCountCheck = 0;
|
let lastUserCountCheck = 0;
|
||||||
|
|
||||||
module.exports = function getTotalStats (req, res) {
|
export function getTotalStats(req: Request, res: Response) {
|
||||||
let row = db.prepare('get', "SELECT COUNT(DISTINCT userID) as userCount, COUNT(*) as totalSubmissions, " +
|
let row = db.prepare('get', "SELECT COUNT(DISTINCT userID) as userCount, COUNT(*) as totalSubmissions, " +
|
||||||
"SUM(views) as viewCount, SUM((endTime - startTime) / 60 * views) as minutesSaved FROM sponsorTimes WHERE shadowHidden != 1 AND votes >= 0", []);
|
"SUM(views) as viewCount, SUM((endTime - startTime) / 60 * views) as minutesSaved FROM sponsorTimes WHERE shadowHidden != 1 AND votes >= 0", []);
|
||||||
|
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
let extensionUsers = chromeUsersCache + firefoxUsersCache;
|
let extensionUsers = chromeUsersCache + firefoxUsersCache;
|
||||||
@@ -25,7 +26,7 @@ module.exports = function getTotalStats (req, res) {
|
|||||||
apiUsers: Math.max(apiUsersCache, extensionUsers),
|
apiUsers: Math.max(apiUsersCache, extensionUsers),
|
||||||
viewCount: row.viewCount,
|
viewCount: row.viewCount,
|
||||||
totalSubmissions: row.totalSubmissions,
|
totalSubmissions: row.totalSubmissions,
|
||||||
minutesSaved: row.minutesSaved
|
minutesSaved: row.minutesSaved,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if the cache should be updated (every ~14 hours)
|
// Check if the cache should be updated (every ~14 hours)
|
||||||
@@ -49,7 +50,7 @@ function updateExtensionUsers() {
|
|||||||
try {
|
try {
|
||||||
firefoxUsersCache = parseInt(JSON.parse(body).average_daily_users);
|
firefoxUsersCache = parseInt(JSON.parse(body).average_daily_users);
|
||||||
|
|
||||||
request.get("https://chrome.google.com/webstore/detail/sponsorblock-for-youtube/mnjggcdmjocbbbhaepdhchncahnbgone", function(err, chromeResponse, body) {
|
request.get("https://chrome.google.com/webstore/detail/sponsorblock-for-youtube/mnjggcdmjocbbbhaepdhchncahnbgone", function (err, chromeResponse, body) {
|
||||||
if (body !== undefined) {
|
if (body !== undefined) {
|
||||||
try {
|
try {
|
||||||
chromeUsersCache = parseInt(body.match(/(?<=\<span class=\"e-f-ih\" title=\").*?(?= users\">)/)[0].replace(",", ""));
|
chromeUsersCache = parseInt(body.match(/(?<=\<span class=\"e-f-ih\" title=\").*?(?= users\">)/)[0].replace(",", ""));
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
const db = require('../databases/databases.js').db;
|
|
||||||
const getHash = require('../utils/getHash.js');
|
|
||||||
|
|
||||||
function dbGetSubmittedSegmentSummary (userID) {
|
|
||||||
try {
|
|
||||||
let row = 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) {
|
|
||||||
return {
|
|
||||||
minutesSaved: row.minutesSaved,
|
|
||||||
segmentCount: row.segmentCount,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
minutesSaved: 0,
|
|
||||||
segmentCount: 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dbGetUsername (userID) {
|
|
||||||
try {
|
|
||||||
let row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]);
|
|
||||||
if (row !== undefined) {
|
|
||||||
return row.userName;
|
|
||||||
} else {
|
|
||||||
//no username yet, just send back the userID
|
|
||||||
return userID;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dbGetViewsForUser (userID) {
|
|
||||||
try {
|
|
||||||
let row = db.prepare('get', "SELECT SUM(views) as viewCount FROM sponsorTimes WHERE userID = ? AND votes > -2 AND shadowHidden != 1", [userID]);
|
|
||||||
//increase the view count by one
|
|
||||||
if (row.viewCount != null) {
|
|
||||||
return row.viewCount;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function dbGetWarningsForUser (userID) {
|
|
||||||
try {
|
|
||||||
let rows = db.prepare('all', "SELECT * FROM warnings WHERE userID = ?", [userID]);
|
|
||||||
return rows.length;
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Couldn\'t get warnings for user ' + userID + '. returning 0') ;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function getUserInfo (req, res) {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
|
|
||||||
if (userID == undefined) {
|
|
||||||
//invalid request
|
|
||||||
res.status(400).send('Parameters are not valid');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//hash the userID
|
|
||||||
userID = getHash(userID);
|
|
||||||
|
|
||||||
const segmentsSummary = dbGetSubmittedSegmentSummary(userID);
|
|
||||||
res.send({
|
|
||||||
userID,
|
|
||||||
userName: dbGetUsername(userID),
|
|
||||||
minutesSaved: segmentsSummary.minutesSaved,
|
|
||||||
segmentCount: segmentsSummary.segmentCount,
|
|
||||||
viewCount: dbGetViewsForUser(userID),
|
|
||||||
warnings: dbGetWarningsForUser(userID)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
84
src/routes/getUserInfo.ts
Normal file
84
src/routes/getUserInfo.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
import {Logger} from '../utils/logger'
|
||||||
|
|
||||||
|
function dbGetSubmittedSegmentSummary(userID: string): any {
|
||||||
|
try {
|
||||||
|
let row = 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) {
|
||||||
|
return {
|
||||||
|
minutesSaved: row.minutesSaved,
|
||||||
|
segmentCount: row.segmentCount,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
minutesSaved: 0,
|
||||||
|
segmentCount: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbGetUsername(userID: string) {
|
||||||
|
try {
|
||||||
|
let row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]);
|
||||||
|
if (row !== undefined) {
|
||||||
|
return row.userName;
|
||||||
|
} else {
|
||||||
|
//no username yet, just send back the userID
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbGetViewsForUser(userID: string) {
|
||||||
|
try {
|
||||||
|
let row = db.prepare('get', "SELECT SUM(views) as viewCount FROM sponsorTimes WHERE userID = ? AND votes > -2 AND shadowHidden != 1", [userID]);
|
||||||
|
//increase the view count by one
|
||||||
|
if (row.viewCount != null) {
|
||||||
|
return row.viewCount;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbGetWarningsForUser(userID: string): number {
|
||||||
|
try {
|
||||||
|
let rows = db.prepare('all', "SELECT * FROM warnings WHERE userID = ?", [userID]);
|
||||||
|
return rows.length;
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error('Couldn\'t get warnings for user ' + userID + '. returning 0');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserInfo(req: Request, res: Response) {
|
||||||
|
let userID = req.query.userID as string;
|
||||||
|
|
||||||
|
if (userID == undefined) {
|
||||||
|
//invalid request
|
||||||
|
res.status(400).send('Parameters are not valid');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//hash the userID
|
||||||
|
userID = getHash(userID);
|
||||||
|
|
||||||
|
const segmentsSummary = dbGetSubmittedSegmentSummary(userID);
|
||||||
|
res.send({
|
||||||
|
userID,
|
||||||
|
userName: dbGetUsername(userID),
|
||||||
|
minutesSaved: segmentsSummary.minutesSaved,
|
||||||
|
segmentCount: segmentsSummary.segmentCount,
|
||||||
|
viewCount: dbGetViewsForUser(userID),
|
||||||
|
warnings: dbGetWarningsForUser(userID),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
import {db} from '../databases/databases';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
var getHash = require('../utils/getHash.js');
|
export function getUsername(req: Request, res: Response) {
|
||||||
const logger = require('../utils/logger.js');
|
let userID = req.query.userID as string;
|
||||||
|
|
||||||
module.exports = function getUsername (req, res) {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
|
|
||||||
if (userID == undefined) {
|
if (userID == undefined) {
|
||||||
//invalid request
|
//invalid request
|
||||||
@@ -20,16 +20,16 @@ module.exports = function getUsername (req, res) {
|
|||||||
|
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
res.send({
|
res.send({
|
||||||
userName: row.userName
|
userName: row.userName,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//no username yet, just send back the userID
|
//no username yet, just send back the userID
|
||||||
res.send({
|
res.send({
|
||||||
userName: userID
|
userName: userID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
Logger.error(err);
|
||||||
res.sendStatus(500);
|
res.sendStatus(500);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
|
||||||
var getHash = require('../utils/getHash.js');
|
|
||||||
var logger = require('../utils/logger.js');
|
|
||||||
module.exports = function getViewsForUser(req, res) {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
|
|
||||||
if (userID == undefined) {
|
|
||||||
//invalid request
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//hash the userID
|
|
||||||
userID = getHash(userID);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let row = db.prepare('get', "SELECT SUM(views) as viewCount FROM sponsorTimes WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
//increase the view count by one
|
|
||||||
if (row.viewCount != null) {
|
|
||||||
res.send({
|
|
||||||
viewCount: row.viewCount
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err);
|
|
||||||
res.sendStatus(500);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
35
src/routes/getViewsForUser.ts
Normal file
35
src/routes/getViewsForUser.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
|
||||||
|
export function getViewsForUser(req: Request, res: Response) {
|
||||||
|
let userID = req.query.userID as string;
|
||||||
|
|
||||||
|
if (userID == undefined) {
|
||||||
|
//invalid request
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//hash the userID
|
||||||
|
userID = getHash(userID);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let row = db.prepare('get', "SELECT SUM(views) as viewCount FROM sponsorTimes WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
|
//increase the view count by one
|
||||||
|
if (row.viewCount != null) {
|
||||||
|
res.send({
|
||||||
|
viewCount: row.viewCount,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
var getSkipSegments = require("./getSkipSegments.js")
|
import {handleGetSegments} from './getSkipSegments';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
export function oldGetVideoSponsorTimes(req: Request, res: Response) {
|
||||||
module.exports = function (req, res) {
|
let segments = handleGetSegments(req, res);
|
||||||
let videoID = req.query.videoID;
|
|
||||||
|
|
||||||
let segments = getSkipSegments.handleGetSegments(req, res);
|
|
||||||
|
|
||||||
if (segments) {
|
if (segments) {
|
||||||
// Convert to old outputs
|
// Convert to old outputs
|
||||||
@@ -18,8 +16,8 @@ module.exports = function (req, res) {
|
|||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
sponsorTimes,
|
sponsorTimes,
|
||||||
UUIDs
|
UUIDs,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error has already been handled in the other method
|
// Error has already been handled in the other method
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
var postSkipSegments = require('./postSkipSegments.js');
|
|
||||||
|
|
||||||
module.exports = async function submitSponsorTimes(req, res) {
|
|
||||||
req.query.category = "sponsor";
|
|
||||||
|
|
||||||
return postSkipSegments(req, res);
|
|
||||||
}
|
|
||||||
8
src/routes/oldSubmitSponsorTimes.ts
Normal file
8
src/routes/oldSubmitSponsorTimes.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import {postSkipSegments} from './postSkipSegments';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
export async function oldSubmitSponsorTimes(req: Request, res: Response) {
|
||||||
|
req.query.category = "sponsor";
|
||||||
|
|
||||||
|
return postSkipSegments(req, res);
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
const db = require('../databases/databases.js').db;
|
import {Logger} from '../utils/logger';
|
||||||
const getHash = require('../utils/getHash.js');
|
import {getHash} from '../utils/getHash';
|
||||||
const isUserVIP = require('../utils/isUserVIP.js');
|
import {isUserVIP} from '../utils/isUserVIP';
|
||||||
const logger = require('../utils/logger.js');
|
import {db} from '../databases/databases';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
export function postNoSegments(req: Request, res: Response) {
|
||||||
// Collect user input data
|
// Collect user input data
|
||||||
let videoID = req.body.videoID;
|
let videoID = req.body.videoID;
|
||||||
let userID = req.body.userID;
|
let userID = req.body.userID;
|
||||||
@@ -17,7 +18,7 @@ module.exports = (req, res) => {
|
|||||||
|| categories.length === 0
|
|| categories.length === 0
|
||||||
) {
|
) {
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
message: 'Bad Format'
|
message: 'Bad Format',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -28,7 +29,7 @@ module.exports = (req, res) => {
|
|||||||
|
|
||||||
if (!userIsVIP) {
|
if (!userIsVIP) {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
message: 'Must be a VIP to mark videos.'
|
message: 'Must be a VIP to mark videos.',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,7 @@ module.exports = (req, res) => {
|
|||||||
if (!noSegmentList || noSegmentList.length === 0) {
|
if (!noSegmentList || noSegmentList.length === 0) {
|
||||||
noSegmentList = [];
|
noSegmentList = [];
|
||||||
} else {
|
} else {
|
||||||
noSegmentList = noSegmentList.map((obj) => {
|
noSegmentList = noSegmentList.map((obj: any) => {
|
||||||
return obj.category;
|
return obj.category;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -60,15 +61,15 @@ module.exports = (req, res) => {
|
|||||||
try {
|
try {
|
||||||
db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]);
|
db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Error submitting 'noSegment' marker for category '" + category + "' for video '" + videoID + "'");
|
Logger.error("Error submitting 'noSegment' marker for category '" + category + "' for video '" + videoID + "'");
|
||||||
logger.error(err);
|
Logger.error(err);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
message: "Internal Server Error: Could not write marker to the database."
|
message: "Internal Server Error: Could not write marker to the database.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
submitted: categoriesToMark
|
submitted: categoriesToMark,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
const db = require('../databases/databases.js').db;
|
|
||||||
const getHash = require('../utils/getHash.js');
|
|
||||||
const isUserVIP = require('../utils/isUserVIP.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
|
|
||||||
const ACTION_NONE = Symbol('none');
|
|
||||||
const ACTION_UPDATE = Symbol('update');
|
|
||||||
const ACTION_REMOVE = Symbol('remove');
|
|
||||||
|
|
||||||
function shiftSegment(segment, shift) {
|
|
||||||
if (segment.startTime >= segment.endTime) return {action: ACTION_NONE, segment};
|
|
||||||
if (shift.startTime >= shift.endTime) return {action: ACTION_NONE, segment};
|
|
||||||
const duration = shift.endTime - shift.startTime;
|
|
||||||
if (shift.endTime < segment.startTime) {
|
|
||||||
// Scenario #1 cut before segment
|
|
||||||
segment.startTime -= duration;
|
|
||||||
segment.endTime -= duration;
|
|
||||||
return {action: ACTION_UPDATE, segment};
|
|
||||||
}
|
|
||||||
if (shift.startTime > segment.endTime) {
|
|
||||||
// Scenario #2 cut after segment
|
|
||||||
return {action: ACTION_NONE, segment};
|
|
||||||
}
|
|
||||||
if (segment.startTime < shift.startTime && segment.endTime > shift.endTime) {
|
|
||||||
// Scenario #3 cut inside segment
|
|
||||||
segment.endTime -= duration;
|
|
||||||
return {action: ACTION_UPDATE, segment};
|
|
||||||
}
|
|
||||||
if (segment.startTime >= shift.startTime && segment.endTime > shift.endTime) {
|
|
||||||
// Scenario #4 cut overlap startTime
|
|
||||||
segment.startTime = shift.startTime;
|
|
||||||
segment.endTime -= duration;
|
|
||||||
return {action: ACTION_UPDATE, segment};
|
|
||||||
}
|
|
||||||
if (segment.startTime < shift.startTime && segment.endTime <= shift.endTime) {
|
|
||||||
// Scenario #5 cut overlap endTime
|
|
||||||
segment.endTime = shift.startTime;
|
|
||||||
return {action: ACTION_UPDATE, segment};
|
|
||||||
}
|
|
||||||
if (segment.startTime >= shift.startTime && segment.endTime <= shift.endTime) {
|
|
||||||
// Scenario #6 cut overlap startTime and endTime
|
|
||||||
return {action: ACTION_REMOVE, segment};
|
|
||||||
}
|
|
||||||
return {action: ACTION_NONE, segment};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
|
||||||
// Collect user input data
|
|
||||||
const videoID = req.body.videoID;
|
|
||||||
const startTime = req.body.startTime;
|
|
||||||
const endTime = req.body.endTime;
|
|
||||||
let userID = req.body.userID;
|
|
||||||
|
|
||||||
// Check input data is valid
|
|
||||||
if (!videoID
|
|
||||||
|| !userID
|
|
||||||
|| !startTime
|
|
||||||
|| !endTime
|
|
||||||
) {
|
|
||||||
res.status(400).json({
|
|
||||||
message: 'Bad Format'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user is VIP
|
|
||||||
userID = getHash(userID);
|
|
||||||
const userIsVIP = isUserVIP(userID);
|
|
||||||
|
|
||||||
if (!userIsVIP) {
|
|
||||||
res.status(403).json({
|
|
||||||
message: 'Must be a VIP to perform this action.'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const segments = db.prepare('all', 'SELECT startTime, endTime, UUID FROM sponsorTimes WHERE videoID = ?', [videoID]);
|
|
||||||
const shift = {
|
|
||||||
startTime,
|
|
||||||
endTime,
|
|
||||||
};
|
|
||||||
segments.forEach(segment => {
|
|
||||||
const result = shiftSegment(segment, shift);
|
|
||||||
switch (result.action) {
|
|
||||||
case ACTION_UPDATE:
|
|
||||||
db.prepare('run', 'UPDATE sponsorTimes SET startTime = ?, endTime = ? WHERE UUID = ?', [result.segment.startTime, result.segment.endTime, result.segment.UUID]);
|
|
||||||
break;
|
|
||||||
case ACTION_REMOVE:
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
};
|
|
||||||
101
src/routes/postSegmentShift.ts
Normal file
101
src/routes/postSegmentShift.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import {Request, Response} from 'express';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {isUserVIP} from '../utils/isUserVIP';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {db} from '../databases/databases';
|
||||||
|
|
||||||
|
const ACTION_NONE = Symbol('none');
|
||||||
|
const ACTION_UPDATE = Symbol('update');
|
||||||
|
const ACTION_REMOVE = Symbol('remove');
|
||||||
|
|
||||||
|
function shiftSegment(segment: any, shift: { startTime: any; endTime: any }) {
|
||||||
|
if (segment.startTime >= segment.endTime) return {action: ACTION_NONE, segment};
|
||||||
|
if (shift.startTime >= shift.endTime) return {action: ACTION_NONE, segment};
|
||||||
|
const duration = shift.endTime - shift.startTime;
|
||||||
|
if (shift.endTime < segment.startTime) {
|
||||||
|
// Scenario #1 cut before segment
|
||||||
|
segment.startTime -= duration;
|
||||||
|
segment.endTime -= duration;
|
||||||
|
return {action: ACTION_UPDATE, segment};
|
||||||
|
}
|
||||||
|
if (shift.startTime > segment.endTime) {
|
||||||
|
// Scenario #2 cut after segment
|
||||||
|
return {action: ACTION_NONE, segment};
|
||||||
|
}
|
||||||
|
if (segment.startTime < shift.startTime && segment.endTime > shift.endTime) {
|
||||||
|
// Scenario #3 cut inside segment
|
||||||
|
segment.endTime -= duration;
|
||||||
|
return {action: ACTION_UPDATE, segment};
|
||||||
|
}
|
||||||
|
if (segment.startTime >= shift.startTime && segment.endTime > shift.endTime) {
|
||||||
|
// Scenario #4 cut overlap startTime
|
||||||
|
segment.startTime = shift.startTime;
|
||||||
|
segment.endTime -= duration;
|
||||||
|
return {action: ACTION_UPDATE, segment};
|
||||||
|
}
|
||||||
|
if (segment.startTime < shift.startTime && segment.endTime <= shift.endTime) {
|
||||||
|
// Scenario #5 cut overlap endTime
|
||||||
|
segment.endTime = shift.startTime;
|
||||||
|
return {action: ACTION_UPDATE, segment};
|
||||||
|
}
|
||||||
|
if (segment.startTime >= shift.startTime && segment.endTime <= shift.endTime) {
|
||||||
|
// Scenario #6 cut overlap startTime and endTime
|
||||||
|
return {action: ACTION_REMOVE, segment};
|
||||||
|
}
|
||||||
|
return {action: ACTION_NONE, segment};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postSegmentShift(req: Request, res: Response): Response {
|
||||||
|
// Collect user input data
|
||||||
|
const videoID = req.body.videoID;
|
||||||
|
const startTime = req.body.startTime;
|
||||||
|
const endTime = req.body.endTime;
|
||||||
|
let userID = req.body.userID;
|
||||||
|
|
||||||
|
// Check input data is valid
|
||||||
|
if (!videoID
|
||||||
|
|| !userID
|
||||||
|
|| !startTime
|
||||||
|
|| !endTime
|
||||||
|
) {
|
||||||
|
res.status(400).json({
|
||||||
|
message: 'Bad Format',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is VIP
|
||||||
|
userID = getHash(userID);
|
||||||
|
const userIsVIP = isUserVIP(userID);
|
||||||
|
|
||||||
|
if (!userIsVIP) {
|
||||||
|
res.status(403).json({
|
||||||
|
message: 'Must be a VIP to perform this action.',
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const segments = db.prepare('all', 'SELECT startTime, endTime, UUID FROM sponsorTimes WHERE videoID = ?', [videoID]);
|
||||||
|
const shift = {
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
};
|
||||||
|
segments.forEach((segment: any) => {
|
||||||
|
const result = shiftSegment(segment, shift);
|
||||||
|
switch (result.action) {
|
||||||
|
case ACTION_UPDATE:
|
||||||
|
db.prepare('run', 'UPDATE sponsorTimes SET startTime = ?, endTime = ? WHERE UUID = ?', [result.segment.startTime, result.segment.endTime, result.segment.UUID]);
|
||||||
|
break;
|
||||||
|
case ACTION_REMOVE:
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
@@ -1,25 +1,23 @@
|
|||||||
const config = require('../config.js');
|
import {config} from '../config';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {db, privateDB} from '../databases/databases';
|
||||||
|
import {YouTubeAPI} from '../utils/youtubeApi';
|
||||||
|
import {getSubmissionUUID} from '../utils/getSubmissionUUID';
|
||||||
|
import request from 'request';
|
||||||
|
import isoDurations from 'iso8601-duration';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
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';
|
||||||
|
|
||||||
const databases = require('../databases/databases.js');
|
|
||||||
const db = databases.db;
|
|
||||||
const privateDB = databases.privateDB;
|
|
||||||
const YouTubeAPI = require('../utils/youtubeAPI.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
const getSubmissionUUID = require('../utils/getSubmissionUUID.js');
|
|
||||||
const request = require('request');
|
|
||||||
const isoDurations = require('iso8601-duration');
|
|
||||||
const fetch = require('node-fetch');
|
|
||||||
|
|
||||||
const getHash = require('../utils/getHash.js');
|
function sendWebhookNotification(userID: string, videoID: string, UUID: string, submissionCount: number, youtubeData: any, {submissionStart, submissionEnd}: { submissionStart: number; submissionEnd: number; }, segmentInfo: any) {
|
||||||
const getIP = require('../utils/getIP.js');
|
const row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]);
|
||||||
const getFormattedTime = require('../utils/getFormattedTime.js');
|
const userName = row !== undefined ? row.userName : null;
|
||||||
const isUserTrustworthy = require('../utils/isUserTrustworthy.js')
|
const video = youtubeData.items[0];
|
||||||
const { dispatchEvent } = require('../utils/webhookUtils.js');
|
|
||||||
|
|
||||||
function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtubeData, {submissionStart, submissionEnd}, segmentInfo) {
|
|
||||||
let row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]);
|
|
||||||
let userName = row !== undefined ? row.userName : null;
|
|
||||||
let video = youtubeData.items[0];
|
|
||||||
|
|
||||||
let scopeName = "submissions.other";
|
let scopeName = "submissions.other";
|
||||||
if (submissionCount <= 1) {
|
if (submissionCount <= 1) {
|
||||||
@@ -31,7 +29,7 @@ function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtube
|
|||||||
"id": videoID,
|
"id": videoID,
|
||||||
"title": video.snippet.title,
|
"title": video.snippet.title,
|
||||||
"thumbnail": video.snippet.thumbnails.maxres ? video.snippet.thumbnails.maxres : null,
|
"thumbnail": video.snippet.thumbnails.maxres ? video.snippet.thumbnails.maxres : null,
|
||||||
"url": "https://www.youtube.com/watch?v=" + videoID
|
"url": "https://www.youtube.com/watch?v=" + videoID,
|
||||||
},
|
},
|
||||||
"submission": {
|
"submission": {
|
||||||
"UUID": UUID,
|
"UUID": UUID,
|
||||||
@@ -40,25 +38,28 @@ function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtube
|
|||||||
"endTime": submissionEnd,
|
"endTime": submissionEnd,
|
||||||
"user": {
|
"user": {
|
||||||
"UUID": userID,
|
"UUID": userID,
|
||||||
"username": userName
|
"username": userName,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
function sendWebhooks(userID: string, videoID: string, UUID: string, segmentInfo: any) {
|
||||||
if (config.youtubeAPIKey !== null) {
|
if (config.youtubeAPIKey !== null) {
|
||||||
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [userID]);
|
const userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
YouTubeAPI.listVideos(videoID, (err, data) => {
|
YouTubeAPI.listVideos(videoID, (err: any, data: any) => {
|
||||||
if (err || data.items.length === 0) {
|
if (err || data.items.length === 0) {
|
||||||
err && logger.error(err);
|
err && Logger.error(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = parseFloat(segmentInfo.segment[0]);
|
const startTime = parseFloat(segmentInfo.segment[0]);
|
||||||
let endTime = parseFloat(segmentInfo.segment[1]);
|
const endTime = parseFloat(segmentInfo.segment[1]);
|
||||||
sendWebhookNotification(userID, videoID, UUID, userSubmissionCountRow.submissionCount, data, {submissionStart: startTime, submissionEnd: endTime}, segmentInfo);
|
sendWebhookNotification(userID, videoID, UUID, userSubmissionCountRow.submissionCount, data, {
|
||||||
|
submissionStart: startTime,
|
||||||
|
submissionEnd: endTime,
|
||||||
|
}, segmentInfo);
|
||||||
|
|
||||||
// If it is a first time submission
|
// If it is a first time submission
|
||||||
// Then send a notification to discord
|
// Then send a notification to discord
|
||||||
@@ -67,45 +68,45 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
|||||||
json: {
|
json: {
|
||||||
"embeds": [{
|
"embeds": [{
|
||||||
"title": data.items[0].snippet.title,
|
"title": data.items[0].snippet.title,
|
||||||
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2),
|
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (parseInt(startTime.toFixed(0)) - 2),
|
||||||
"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": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
}
|
},
|
||||||
}, (err, res) => {
|
}, (err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error("Failed to send first time submission Discord hook.");
|
Logger.error("Failed to send first time submission Discord hook.");
|
||||||
logger.error(JSON.stringify(err));
|
Logger.error(JSON.stringify(err));
|
||||||
logger.error("\n");
|
Logger.error("\n");
|
||||||
} else if (res && res.statusCode >= 400) {
|
} else if (res && res.statusCode >= 400) {
|
||||||
logger.error("Error sending first time submission Discord hook");
|
Logger.error("Error sending first time submission Discord hook");
|
||||||
logger.error(JSON.stringify(res));
|
Logger.error(JSON.stringify(res));
|
||||||
logger.error("\n");
|
Logger.error("\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendWebhooksNB(userID, videoID, UUID, startTime, endTime, category, probability, ytData) {
|
function sendWebhooksNB(userID: string, videoID: string, UUID: string, startTime: number, endTime: number, category: string, probability: number, ytData: any) {
|
||||||
let submissionInfoRow = db.prepare('get', "SELECT " +
|
const submissionInfoRow = db.prepare('get', "SELECT " +
|
||||||
"(select count(1) from sponsorTimes where userID = ?) count, " +
|
"(select count(1) from sponsorTimes where userID = ?) count, " +
|
||||||
"(select count(1) from sponsorTimes where userID = ? and votes <= -2) disregarded, " +
|
"(select count(1) from sponsorTimes where userID = ? and votes <= -2) disregarded, " +
|
||||||
"coalesce((select userName FROM userNames WHERE userID = ?), ?) userName",
|
"coalesce((select userName FROM userNames WHERE userID = ?), ?) userName",
|
||||||
[userID, userID, userID, userID]);
|
[userID, userID, userID, userID]);
|
||||||
|
|
||||||
let submittedBy = "";
|
let submittedBy: string;
|
||||||
// If a userName was created then show both
|
// If a userName was created then show both
|
||||||
if (submissionInfoRow.userName !== userID){
|
if (submissionInfoRow.userName !== userID) {
|
||||||
submittedBy = submissionInfoRow.userName + "\n " + userID;
|
submittedBy = submissionInfoRow.userName + "\n " + userID;
|
||||||
} else {
|
} else {
|
||||||
submittedBy = userID;
|
submittedBy = userID;
|
||||||
@@ -117,30 +118,30 @@ function sendWebhooksNB(userID, videoID, UUID, startTime, endTime, category, pro
|
|||||||
json: {
|
json: {
|
||||||
"embeds": [{
|
"embeds": [{
|
||||||
"title": ytData.items[0].snippet.title,
|
"title": ytData.items[0].snippet.title,
|
||||||
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2),
|
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (parseFloat(startTime.toFixed(0)) - 2),
|
||||||
"description": "**Submission ID:** " + UUID +
|
"description": "**Submission ID:** " + UUID +
|
||||||
"\n**Timestamp:** " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
"\n**Timestamp:** " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||||
"\n**Predicted Probability:** " + probability +
|
"\n**Predicted Probability:** " + probability +
|
||||||
"\n**Category:** " + category +
|
"\n**Category:** " + category +
|
||||||
"\n**Submitted by:** "+ submittedBy +
|
"\n**Submitted by:** " + submittedBy +
|
||||||
"\n**Total User Submissions:** "+submissionInfoRow.count +
|
"\n**Total User Submissions:** " + submissionInfoRow.count +
|
||||||
"\n**Ignored User Submissions:** "+submissionInfoRow.disregarded,
|
"\n**Ignored User Submissions:** " + submissionInfoRow.disregarded,
|
||||||
"color": 10813440,
|
"color": 10813440,
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"url": ytData.items[0].snippet.thumbnails.maxres ? ytData.items[0].snippet.thumbnails.maxres.url : "",
|
"url": ytData.items[0].snippet.thumbnails.maxres ? ytData.items[0].snippet.thumbnails.maxres.url : "",
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
}
|
},
|
||||||
}, (err, res) => {
|
}, (err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error("Failed to send NeuralBlock Discord hook.");
|
Logger.error("Failed to send NeuralBlock Discord hook.");
|
||||||
logger.error(JSON.stringify(err));
|
Logger.error(JSON.stringify(err));
|
||||||
logger.error("\n");
|
Logger.error("\n");
|
||||||
} else if (res && res.statusCode >= 400) {
|
} else if (res && res.statusCode >= 400) {
|
||||||
logger.error("Error sending NeuralBlock Discord hook");
|
Logger.error("Error sending NeuralBlock Discord hook");
|
||||||
logger.error(JSON.stringify(res));
|
Logger.error(JSON.stringify(res));
|
||||||
logger.error("\n");
|
Logger.error("\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,11 +151,11 @@ function sendWebhooksNB(userID, videoID, UUID, startTime, endTime, category, pro
|
|||||||
// Looks like this was broken for no defined youtube key - fixed but IMO we shouldn't return
|
// Looks like this was broken for no defined youtube key - fixed but IMO we shouldn't return
|
||||||
// false for a pass - it was confusing and lead to this bug - any use of this function in
|
// false for a pass - it was confusing and lead to this bug - any use of this function in
|
||||||
// the future could have the same problem.
|
// the future could have the same problem.
|
||||||
async function autoModerateSubmission(submission) {
|
async function autoModerateSubmission(submission: { videoID: any; userID: any; segments: any }) {
|
||||||
// Get the video information from the youtube API
|
// Get the video information from the youtube API
|
||||||
if (config.youtubeAPIKey !== null) {
|
if (config.youtubeAPIKey !== null) {
|
||||||
let {err, data} = await new Promise((resolve, reject) => {
|
const {err, data} = await new Promise((resolve) => {
|
||||||
YouTubeAPI.listVideos(submission.videoID, (err, data) => resolve({err, data}));
|
YouTubeAPI.listVideos(submission.videoID, (err: any, data: any) => resolve({err, data}));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -164,54 +165,54 @@ async function autoModerateSubmission(submission) {
|
|||||||
if (data.pageInfo.totalResults === 0) {
|
if (data.pageInfo.totalResults === 0) {
|
||||||
return "No video exists with id " + submission.videoID;
|
return "No video exists with id " + submission.videoID;
|
||||||
} else {
|
} else {
|
||||||
let segments = submission.segments;
|
const segments = submission.segments;
|
||||||
let nbString = "";
|
let nbString = "";
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
let startTime = parseFloat(segments[i].segment[0]);
|
const startTime = parseFloat(segments[i].segment[0]);
|
||||||
let endTime = parseFloat(segments[i].segment[1]);
|
const endTime = parseFloat(segments[i].segment[1]);
|
||||||
|
|
||||||
let duration = data.items[0].contentDetails.duration;
|
let duration = data.items[0].contentDetails.duration;
|
||||||
duration = isoDurations.toSeconds(isoDurations.parse(duration));
|
duration = isoDurations.toSeconds(isoDurations.parse(duration));
|
||||||
if (duration == 0) {
|
if (duration == 0) {
|
||||||
// Allow submission if the duration is 0 (bug in youtube api)
|
// Allow submission if the duration is 0 (bug in youtube api)
|
||||||
return false;
|
return false;
|
||||||
} else if ((endTime - startTime) > (duration/100)*80) {
|
} else if ((endTime - startTime) > (duration / 100) * 80) {
|
||||||
// Reject submission if over 80% of the video
|
// Reject submission if over 80% of the video
|
||||||
return "One of your submitted segments is over 80% of the video.";
|
return "One of your submitted segments is over 80% of the video.";
|
||||||
} else {
|
} else {
|
||||||
if (segments[i].category === "sponsor") {
|
if (segments[i].category === "sponsor") {
|
||||||
//Prepare timestamps to send to NB all at once
|
//Prepare timestamps to send to NB all at once
|
||||||
nbString = nbString + segments[i].segment[0] + "," + segments[i].segment[1] + ";";
|
nbString = nbString + segments[i].segment[0] + "," + segments[i].segment[1] + ";";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check NeuralBlock
|
// Check NeuralBlock
|
||||||
let neuralBlockURL = config.neuralBlockURL;
|
const neuralBlockURL = config.neuralBlockURL;
|
||||||
if (!neuralBlockURL) return false;
|
if (!neuralBlockURL) return false;
|
||||||
let response = await fetch(neuralBlockURL + "/api/checkSponsorSegments?vid=" + submission.videoID +
|
const response = await fetch(neuralBlockURL + "/api/checkSponsorSegments?vid=" + submission.videoID +
|
||||||
"&segments=" + nbString.substring(0,nbString.length-1));
|
"&segments=" + nbString.substring(0, nbString.length - 1));
|
||||||
if (!response.ok) return false;
|
if (!response.ok) return false;
|
||||||
|
|
||||||
let nbPredictions = await response.json();
|
const nbPredictions = await response.json();
|
||||||
nbDecision = false;
|
let nbDecision = false;
|
||||||
let predictionIdx = 0; //Keep track because only sponsor categories were submitted
|
let predictionIdx = 0; //Keep track because only sponsor categories were submitted
|
||||||
for (let i = 0; i < segments.length; i++){
|
for (let i = 0; i < segments.length; i++) {
|
||||||
if (segments[i].category === "sponsor"){
|
if (segments[i].category === "sponsor") {
|
||||||
if (nbPredictions.probabilities[predictionIdx] < 0.70){
|
if (nbPredictions.probabilities[predictionIdx] < 0.70) {
|
||||||
nbDecision = true; // At least one bad entry
|
nbDecision = true; // At least one bad entry
|
||||||
startTime = parseFloat(segments[i].segment[0]);
|
const startTime = parseFloat(segments[i].segment[0]);
|
||||||
endTime = parseFloat(segments[i].segment[1]);
|
const endTime = parseFloat(segments[i].segment[1]);
|
||||||
|
|
||||||
const UUID = getSubmissionUUID(submission.videoID, segments[i].category, submission.userID, startTime, endTime);
|
const UUID = getSubmissionUUID(submission.videoID, segments[i].category, submission.userID, startTime, endTime);
|
||||||
// Send to Discord
|
// Send to Discord
|
||||||
// Note, if this is too spammy. Consider sending all the segments as one Webhook
|
// Note, if this is too spammy. Consider sending all the segments as one Webhook
|
||||||
sendWebhooksNB(submission.userID, submission.videoID, UUID, startTime, endTime, segments[i].category, nbPredictions.probabilities[predictionIdx], data);
|
sendWebhooksNB(submission.userID, submission.videoID, UUID, startTime, endTime, segments[i].category, nbPredictions.probabilities[predictionIdx], data);
|
||||||
}
|
}
|
||||||
predictionIdx++;
|
predictionIdx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (nbDecision){
|
if (nbDecision) {
|
||||||
return "Rejected based on NeuralBlock predictions.";
|
return "Rejected based on NeuralBlock predictions.";
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -219,7 +220,7 @@ async function autoModerateSubmission(submission) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Skipped YouTube API");
|
Logger.debug("Skipped YouTube API");
|
||||||
|
|
||||||
// Can't moderate the submission without calling the youtube API
|
// Can't moderate the submission without calling the youtube API
|
||||||
// so allow by default.
|
// so allow by default.
|
||||||
@@ -227,33 +228,33 @@ async function autoModerateSubmission(submission) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function proxySubmission(req) {
|
function proxySubmission(req: Request) {
|
||||||
request.post(config.proxySubmission + '/api/skipSegments?userID='+req.query.userID+'&videoID='+req.query.videoID, {json: req.body}, (err, result) => {
|
request.post(config.proxySubmission + '/api/skipSegments?userID=' + req.query.userID + '&videoID=' + req.query.videoID, {json: req.body}, (err, result) => {
|
||||||
if (config.mode === 'development') {
|
if (config.mode === 'development') {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
logger.debug('Proxy Submission: ' + result.statusCode + ' ('+result.body+')');
|
Logger.debug('Proxy Submission: ' + result.statusCode + ' (' + result.body + ')');
|
||||||
} else {
|
} else {
|
||||||
logger.error("Proxy Submission: Failed to make call");
|
Logger.error("Proxy Submission: Failed to make call");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = async function postSkipSegments(req, res) {
|
export async function postSkipSegments(req: Request, res: Response) {
|
||||||
if (config.proxySubmission) {
|
if (config.proxySubmission) {
|
||||||
proxySubmission(req);
|
proxySubmission(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
let videoID = req.query.videoID || req.body.videoID;
|
const videoID = req.query.videoID || req.body.videoID;
|
||||||
let userID = req.query.userID || req.body.userID;
|
let userID = req.query.userID || req.body.userID;
|
||||||
|
|
||||||
let segments = req.body.segments;
|
|
||||||
|
|
||||||
|
let segments = req.body.segments;
|
||||||
if (segments === undefined) {
|
if (segments === undefined) {
|
||||||
// Use query instead
|
// Use query instead
|
||||||
segments = [{
|
segments = [{
|
||||||
segment: [req.query.startTime, req.query.endTime],
|
segment: [req.query.startTime, req.query.endTime],
|
||||||
category: req.query.category
|
category: req.query.category,
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,24 +280,26 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
userID = getHash(userID);
|
userID = getHash(userID);
|
||||||
|
|
||||||
//hash the ip 5000 times so no one can get it from the database
|
//hash the ip 5000 times so no one can get it from the database
|
||||||
let hashedIP = getHash(getIP(req) + config.globalSalt);
|
const hashedIP = getHash(getIP(req) + config.globalSalt);
|
||||||
|
|
||||||
const MILLISECONDS_IN_HOUR = 3600000;
|
const MILLISECONDS_IN_HOUR = 3600000;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let warningsCount = db.prepare('get', "SELECT count(1) as count FROM warnings WHERE userID = ? AND issueTime > ?",
|
const warningsCount = db.prepare('get', "SELECT count(1) as count FROM warnings WHERE userID = ? AND issueTime > ?",
|
||||||
[userID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))]
|
[userID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))],
|
||||||
).count;
|
).count;
|
||||||
|
|
||||||
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
||||||
return res.status(403).send('Submission blocked. Too many active warnings!');
|
return res.status(403).send('Submission blocked. Too many active warnings!');
|
||||||
}
|
}
|
||||||
|
|
||||||
let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]).map((list) => { return list.category });
|
const noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]).map((list: any) => {
|
||||||
|
return list.category;
|
||||||
|
});
|
||||||
|
|
||||||
//check if this user is on the vip list
|
//check if this user is on the vip list
|
||||||
let isVIP = db.prepare("get", "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
const isVIP = db.prepare("get", "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
||||||
|
|
||||||
let decreaseVotes = 0;
|
const decreaseVotes = 0;
|
||||||
|
|
||||||
// Check if all submissions are correct
|
// Check if all submissions are correct
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
@@ -307,20 +310,20 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!config.categoryList.includes(segments[i].category)) {
|
if (!config.categoryList.includes(segments[i].category)) {
|
||||||
res.status("400").send("Category doesn't exist.");
|
res.status(400).send("Category doesn't exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject segemnt if it's in the no segments list
|
// Reject segemnt if it's in the no segments list
|
||||||
if (!isVIP && noSegmentList.indexOf(segments[i].category) !== -1) {
|
if (!isVIP && noSegmentList.indexOf(segments[i].category) !== -1) {
|
||||||
// TODO: Do something about the fradulent submission
|
// TODO: Do something about the fradulent submission
|
||||||
logger.warn("Caught a no-segment submission. userID: '" + userID + "', videoID: '" + videoID + "', category: '" + segments[i].category + "'");
|
Logger.warn("Caught a no-segment submission. userID: '" + userID + "', videoID: '" + videoID + "', category: '" + segments[i].category + "'");
|
||||||
res.status(403).send(
|
res.status(403).send(
|
||||||
"Request rejected by auto moderator: New submissions are not allowed for the following category: '"
|
"Request rejected by auto moderator: New submissions are not allowed for the following category: '"
|
||||||
+ segments[i].category + "'. A moderator has decided that no new segments are needed and that all current segments of this category are timed perfectly.\n\n "
|
+ segments[i].category + "'. A moderator has decided that no new segments are needed and that all current segments of this category are timed perfectly.\n\n "
|
||||||
+ (segments[i].category === "sponsor" ? "Maybe the segment you are submitting is a different category that you have not enabled and is not a sponsor. " +
|
+ (segments[i].category === "sponsor" ? "Maybe the segment you are submitting is a different category that you have not enabled and is not a sponsor. " +
|
||||||
"Categories that aren't sponsor, such as self-promotion can be enabled in the options.\n\n " : "")
|
"Categories that aren't sponsor, such as self-promotion can be enabled in the options.\n\n " : "")
|
||||||
+ "If you believe this is incorrect, please contact someone on Discord."
|
+ "If you believe this is incorrect, please contact someone on Discord.",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -330,7 +333,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
let endTime = parseFloat(segments[i].segment[1]);
|
let endTime = parseFloat(segments[i].segment[1]);
|
||||||
|
|
||||||
if (isNaN(startTime) || isNaN(endTime)
|
if (isNaN(startTime) || isNaN(endTime)
|
||||||
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime >= endTime) {
|
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime >= endTime) {
|
||||||
//invalid request
|
//invalid request
|
||||||
res.status(400).send("One of your segments times are invalid (too short, startTime before endTime, etc.)");
|
res.status(400).send("One of your segments times are invalid (too short, startTime before endTime, etc.)");
|
||||||
return;
|
return;
|
||||||
@@ -343,7 +346,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check if this info has already been submitted before
|
//check if this info has already been submitted before
|
||||||
let duplicateCheck2Row = db.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE startTime = ? " +
|
const duplicateCheck2Row = db.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE startTime = ? " +
|
||||||
"and endTime = ? and category = ? and videoID = ?", [startTime, endTime, segments[i].category, videoID]);
|
"and endTime = ? and category = ? and videoID = ?", [startTime, endTime, segments[i].category, videoID]);
|
||||||
if (duplicateCheck2Row.count > 0) {
|
if (duplicateCheck2Row.count > 0) {
|
||||||
res.sendStatus(409);
|
res.sendStatus(409);
|
||||||
@@ -353,8 +356,8 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
|
|
||||||
// Auto moderator check
|
// Auto moderator check
|
||||||
if (!isVIP) {
|
if (!isVIP) {
|
||||||
let autoModerateResult = await autoModerateSubmission({userID, videoID, segments});//startTime, endTime, category: segments[i].category});
|
const autoModerateResult = await autoModerateSubmission({userID, videoID, segments});//startTime, endTime, category: segments[i].category});
|
||||||
if (autoModerateResult == "Rejected based on NeuralBlock predictions."){
|
if (autoModerateResult == "Rejected based on NeuralBlock predictions.") {
|
||||||
// If NB automod rejects, the submission will start with -2 votes.
|
// If NB automod rejects, the submission will start with -2 votes.
|
||||||
// Note, if one submission is bad all submissions will be affected.
|
// Note, if one submission is bad all submissions will be affected.
|
||||||
// However, this behavior is consistent with other automod functions
|
// However, this behavior is consistent with other automod functions
|
||||||
@@ -367,18 +370,18 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Will be filled when submitting
|
// Will be filled when submitting
|
||||||
let UUIDs = [];
|
const UUIDs = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//get current time
|
//get current time
|
||||||
let timeSubmitted = Date.now();
|
const timeSubmitted = Date.now();
|
||||||
|
|
||||||
let yesterday = timeSubmitted - 86400000;
|
const yesterday = timeSubmitted - 86400000;
|
||||||
|
|
||||||
// Disable IP ratelimiting for now
|
// Disable IP ratelimiting for now
|
||||||
if (false) {
|
if (false) {
|
||||||
//check to see if this ip has submitted too many sponsors today
|
//check to see if this ip has submitted too many sponsors today
|
||||||
let rateLimitCheckRow = privateDB.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?", [hashedIP, videoID, yesterday]);
|
const rateLimitCheckRow = privateDB.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?", [hashedIP, videoID, yesterday]);
|
||||||
|
|
||||||
if (rateLimitCheckRow.count >= 10) {
|
if (rateLimitCheckRow.count >= 10) {
|
||||||
//too many sponsors for the same video from the same ip address
|
//too many sponsors for the same video from the same ip address
|
||||||
@@ -391,7 +394,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
// Disable max submissions for now
|
// Disable max submissions for now
|
||||||
if (false) {
|
if (false) {
|
||||||
//check to see if the user has already submitted sponsors for this video
|
//check to see if the user has already submitted sponsors for this video
|
||||||
let duplicateCheckRow = db.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?", [userID, videoID]);
|
const duplicateCheckRow = db.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?", [userID, videoID]);
|
||||||
|
|
||||||
if (duplicateCheckRow.count >= 16) {
|
if (duplicateCheckRow.count >= 16) {
|
||||||
//too many sponsors for the same video from the same user
|
//too many sponsors for the same video from the same user
|
||||||
@@ -402,7 +405,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check to see if this user is shadowbanned
|
//check to see if this user is shadowbanned
|
||||||
let shadowBanRow = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
const shadowBanRow = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
let shadowBanned = shadowBanRow.userCount;
|
let shadowBanned = shadowBanRow.userCount;
|
||||||
|
|
||||||
@@ -418,20 +421,20 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.youtubeAPIKey !== null) {
|
if (config.youtubeAPIKey !== null) {
|
||||||
let {err, data} = await new Promise((resolve, reject) => {
|
let {err, data} = await new Promise((resolve) => {
|
||||||
YouTubeAPI.listVideos(videoID, (err, data) => resolve({err, data}));
|
YouTubeAPI.listVideos(videoID, (err: any, data: any) => resolve({err, data}));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error("Error while submitting when connecting to YouTube API: " + err);
|
Logger.error("Error while submitting when connecting to YouTube API: " + err);
|
||||||
} else {
|
} else {
|
||||||
//get all segments for this video and user
|
//get all segments for this video and user
|
||||||
let allSubmittedByUser = db.prepare('all', "SELECT startTime, endTime FROM sponsorTimes WHERE userID = ? and videoID = ? and votes > -1", [userID, videoID]);
|
const allSubmittedByUser = db.prepare('all', "SELECT startTime, endTime FROM sponsorTimes WHERE userID = ? and videoID = ? and votes > -1", [userID, videoID]);
|
||||||
let allSegmentTimes = [];
|
const allSegmentTimes = [];
|
||||||
if (allSubmittedByUser !== undefined) {
|
if (allSubmittedByUser !== undefined) {
|
||||||
//add segments the user has previously submitted
|
//add segments the user has previously submitted
|
||||||
for (const segmentInfo of allSubmittedByUser) {
|
for (const segmentInfo of allSubmittedByUser) {
|
||||||
allSegmentTimes.push([parseFloat(segmentInfo.startTime), parseFloat(segmentInfo.endTime)])
|
allSegmentTimes.push([parseFloat(segmentInfo.startTime), parseFloat(segmentInfo.endTime)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +446,9 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//merge all the times into non-overlapping arrays
|
//merge all the times into non-overlapping arrays
|
||||||
const allSegmentsSorted = mergeTimeSegments(allSegmentTimes.sort(function(a, b) { return a[0]-b[0] || a[1]-b[1] }));
|
const allSegmentsSorted = mergeTimeSegments(allSegmentTimes.sort(function (a, b) {
|
||||||
|
return a[0] - b[0] || a[1] - b[1];
|
||||||
|
}));
|
||||||
|
|
||||||
let videoDuration = data.items[0].contentDetails.duration;
|
let videoDuration = data.items[0].contentDetails.duration;
|
||||||
videoDuration = isoDurations.toSeconds(isoDurations.parse(videoDuration));
|
videoDuration = isoDurations.toSeconds(isoDurations.parse(videoDuration));
|
||||||
@@ -451,7 +456,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
let allSegmentDuration = 0;
|
let allSegmentDuration = 0;
|
||||||
//sum all segment times together
|
//sum all segment times together
|
||||||
allSegmentsSorted.forEach(segmentInfo => allSegmentDuration += segmentInfo[1] - segmentInfo[0]);
|
allSegmentsSorted.forEach(segmentInfo => allSegmentDuration += segmentInfo[1] - segmentInfo[0]);
|
||||||
if (allSegmentDuration > (videoDuration/100)*80) {
|
if (allSegmentDuration > (videoDuration / 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
|
||||||
res.status(400).send("Total length of your submitted segments are over 80% of the video.");
|
res.status(400).send("Total length of your submitted segments are over 80% of the video.");
|
||||||
return;
|
return;
|
||||||
@@ -470,8 +475,8 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
db.prepare('run', "INSERT INTO sponsorTimes " +
|
db.prepare('run', "INSERT INTO sponsorTimes " +
|
||||||
"(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID)" +
|
"(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID)" +
|
||||||
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [
|
||||||
videoID, segmentInfo.segment[0], segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned, getHash(videoID, 1)
|
videoID, segmentInfo.segment[0], segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned, getHash(videoID, 1),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
//add to private db as well
|
//add to private db as well
|
||||||
@@ -479,7 +484,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
//a DB change probably occurred
|
//a DB change probably occurred
|
||||||
res.sendStatus(502);
|
res.sendStatus(502);
|
||||||
logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
Logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
||||||
segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err);
|
segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -488,7 +493,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
UUIDs.push(UUID);
|
UUIDs.push(UUID);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
Logger.error(err);
|
||||||
|
|
||||||
res.sendStatus(500);
|
res.sendStatus(500);
|
||||||
|
|
||||||
@@ -516,8 +521,9 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
// [50, 80],
|
// [50, 80],
|
||||||
// [100, 150]
|
// [100, 150]
|
||||||
// ]
|
// ]
|
||||||
function mergeTimeSegments(ranges) {
|
function mergeTimeSegments(ranges: number[][]) {
|
||||||
var result = [], last;
|
const result: number[][] = [];
|
||||||
|
let last: number[];
|
||||||
|
|
||||||
ranges.forEach(function (r) {
|
ranges.forEach(function (r) {
|
||||||
if (!last || r[0] > last[1])
|
if (!last || r[0] > last[1])
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
const db = require('../databases/databases.js').db;
|
import {Request, Response} from 'express';
|
||||||
const getHash = require('../utils/getHash.js');
|
import {Logger} from '../utils/logger';
|
||||||
const isUserVIP = require('../utils/isUserVIP.js');
|
import {db} from '../databases/databases';
|
||||||
const logger = require('../utils/logger.js');
|
import {isUserVIP} from '../utils/isUserVIP';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
|
||||||
module.exports = (req, res) => {
|
export function postWarning(req: Request, res: Response) {
|
||||||
// Collect user input data
|
// Collect user input data
|
||||||
let issuerUserID = getHash(req.body.issuerUserID);
|
let issuerUserID = getHash(req.body.issuerUserID);
|
||||||
let userID = getHash(req.body.userID);
|
let userID = getHash(req.body.userID);
|
||||||
@@ -11,14 +12,14 @@ module.exports = (req, res) => {
|
|||||||
|
|
||||||
// Ensure user is a VIP
|
// Ensure user is a VIP
|
||||||
if (!isUserVIP(issuerUserID)) {
|
if (!isUserVIP(issuerUserID)) {
|
||||||
logger.debug("Permission violation: User " + issuerUserID + " attempted to warn user " + userID + "."); // maybe warn?
|
Logger.debug("Permission violation: User " + issuerUserID + " attempted to warn user " + userID + "."); // maybe warn?
|
||||||
res.status(403).json({"message": "Not a VIP"});
|
res.status(403).json({"message": "Not a VIP"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.prepare('run', 'INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES (?, ?, ?)', [userID, issueTime, issuerUserID]);
|
db.prepare('run', 'INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES (?, ?, ?)', [userID, issueTime, issuerUserID]);
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
message: "Warning issued to user '" + userID + "'."
|
message: "Warning issued to user '" + userID + "'.",
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
}
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
|
|
||||||
var config = require('../config.js');
|
|
||||||
|
|
||||||
var db = require('../databases/databases.js').db;
|
|
||||||
var getHash = require('../utils/getHash.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function setUsername(req, res) {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
let userName = req.query.username;
|
|
||||||
|
|
||||||
let adminUserIDInput = req.query.adminUserID;
|
|
||||||
|
|
||||||
if (userID == undefined || userName == undefined || userID === "undefined" || userName.length > 64) {
|
|
||||||
//invalid request
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userName.includes("discord")) {
|
|
||||||
// Don't allow
|
|
||||||
res.sendStatus(200);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adminUserIDInput != undefined) {
|
|
||||||
//this is the admin controlling the other users account, don't hash the controling account's ID
|
|
||||||
adminUserIDInput = getHash(adminUserIDInput);
|
|
||||||
|
|
||||||
if (adminUserIDInput != config.adminUserID) {
|
|
||||||
//they aren't the admin
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//hash the userID
|
|
||||||
userID = getHash(userID);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
//check if username is already set
|
|
||||||
let row = db.prepare('get', "SELECT count(*) as count FROM userNames WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
if (row.count > 0) {
|
|
||||||
//already exists, update this row
|
|
||||||
db.prepare('run', "UPDATE userNames SET userName = ? WHERE userID = ?", [userName, userID]);
|
|
||||||
} else {
|
|
||||||
//add to the db
|
|
||||||
db.prepare('run', "INSERT INTO userNames VALUES(?, ?)", [userID, userName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err);
|
|
||||||
res.sendStatus(500);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
58
src/routes/setUsername.ts
Normal file
58
src/routes/setUsername.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
export function setUsername(req: Request, res: Response) {
|
||||||
|
let userID = req.query.userID as string;
|
||||||
|
let userName = req.query.username as string;
|
||||||
|
|
||||||
|
let adminUserIDInput = req.query.adminUserID as string;
|
||||||
|
|
||||||
|
if (userID == undefined || userName == undefined || userID === "undefined" || userName.length > 64) {
|
||||||
|
//invalid request
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userName.includes("discord")) {
|
||||||
|
// Don't allow
|
||||||
|
res.sendStatus(200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adminUserIDInput != undefined) {
|
||||||
|
//this is the admin controlling the other users account, don't hash the controling account's ID
|
||||||
|
adminUserIDInput = getHash(adminUserIDInput);
|
||||||
|
|
||||||
|
if (adminUserIDInput != config.adminUserID) {
|
||||||
|
//they aren't the admin
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//hash the userID
|
||||||
|
userID = getHash(userID);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
//check if username is already set
|
||||||
|
let row = db.prepare('get', "SELECT count(*) as count FROM userNames WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
|
if (row.count > 0) {
|
||||||
|
//already exists, update this row
|
||||||
|
db.prepare('run', "UPDATE userNames SET userName = ? WHERE userID = ?", [userName, userID]);
|
||||||
|
} else {
|
||||||
|
//add to the db
|
||||||
|
db.prepare('run', "INSERT INTO userNames VALUES(?, ?)", [userID, userName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
} catch (err) {
|
||||||
|
Logger.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
var databases = require('../databases/databases.js');
|
|
||||||
var db = databases.db;
|
|
||||||
var privateDB = databases.privateDB;
|
|
||||||
|
|
||||||
var getHash = require('../utils/getHash.js');
|
|
||||||
|
|
||||||
module.exports = async function shadowBanUser(req, res) {
|
|
||||||
let userID = req.query.userID;
|
|
||||||
let hashedIP = req.query.hashedIP;
|
|
||||||
let adminUserIDInput = req.query.adminUserID;
|
|
||||||
|
|
||||||
let enabled = req.query.enabled;
|
|
||||||
if (enabled === undefined){
|
|
||||||
enabled = true;
|
|
||||||
} else {
|
|
||||||
enabled = enabled === "true";
|
|
||||||
}
|
|
||||||
|
|
||||||
//if enabled is false and the old submissions should be made visible again
|
|
||||||
let unHideOldSubmissions = req.query.unHideOldSubmissions !== "false";
|
|
||||||
|
|
||||||
if (adminUserIDInput == undefined || (userID == undefined && hashedIP == undefined)) {
|
|
||||||
//invalid request
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//hash the userID
|
|
||||||
adminUserIDInput = getHash(adminUserIDInput);
|
|
||||||
|
|
||||||
let isVIP = db.prepare("get", "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [adminUserIDInput]).userCount > 0;
|
|
||||||
if (!isVIP) {
|
|
||||||
//not authorized
|
|
||||||
res.sendStatus(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userID) {
|
|
||||||
//check to see if this user is already shadowbanned
|
|
||||||
let row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
if (enabled && row.userCount == 0) {
|
|
||||||
//add them to the shadow ban list
|
|
||||||
|
|
||||||
//add it to the table
|
|
||||||
privateDB.prepare('run', "INSERT INTO shadowBannedUsers VALUES(?)", [userID]);
|
|
||||||
|
|
||||||
//find all previous submissions and hide them
|
|
||||||
if (unHideOldSubmissions) {
|
|
||||||
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 1 WHERE userID = ?", [userID]);
|
|
||||||
}
|
|
||||||
} else if (!enabled && row.userCount > 0) {
|
|
||||||
//remove them from the shadow ban list
|
|
||||||
privateDB.prepare('run', "DELETE FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
//find all previous submissions and unhide them
|
|
||||||
if (unHideOldSubmissions) {
|
|
||||||
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 0 WHERE userID = ?", [userID]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (hashedIP) {
|
|
||||||
//check to see if this user is already shadowbanned
|
|
||||||
// let row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedIPs WHERE hashedIP = ?", [hashedIP]);
|
|
||||||
|
|
||||||
// if (enabled && row.userCount == 0) {
|
|
||||||
if (enabled) {
|
|
||||||
//add them to the shadow ban list
|
|
||||||
|
|
||||||
//add it to the table
|
|
||||||
// privateDB.prepare('run', "INSERT INTO shadowBannedIPs VALUES(?)", [hashedIP]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//find all previous submissions and hide them
|
|
||||||
if (unHideOldSubmissions) {
|
|
||||||
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 1 WHERE timeSubmitted IN " +
|
|
||||||
"(SELECT privateDB.timeSubmitted FROM sponsorTimes LEFT JOIN privateDB.sponsorTimes as privateDB ON sponsorTimes.timeSubmitted=privateDB.timeSubmitted " +
|
|
||||||
"WHERE privateDB.hashedIP = ?)", [hashedIP]);
|
|
||||||
}
|
|
||||||
} else if (!enabled && row.userCount > 0) {
|
|
||||||
// //remove them from the shadow ban list
|
|
||||||
// privateDB.prepare('run', "DELETE FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
// //find all previous submissions and unhide them
|
|
||||||
// if (unHideOldSubmissions) {
|
|
||||||
// db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 0 WHERE userID = ?", [userID]);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
}
|
|
||||||
89
src/routes/shadowBanUser.ts
Normal file
89
src/routes/shadowBanUser.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import {db, privateDB} from '../databases/databases';
|
||||||
|
import {getHash} from '../utils/getHash';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
|
||||||
|
export async function shadowBanUser(req: Request, res: Response) {
|
||||||
|
const userID = req.query.userID as string;
|
||||||
|
const hashedIP = req.query.hashedIP as string;
|
||||||
|
let adminUserIDInput = req.query.adminUserID as string;
|
||||||
|
|
||||||
|
const enabled = req.query.enabled === undefined
|
||||||
|
? false
|
||||||
|
: req.query.enabled === 'true';
|
||||||
|
|
||||||
|
//if enabled is false and the old submissions should be made visible again
|
||||||
|
const unHideOldSubmissions = req.query.unHideOldSubmissions !== "false";
|
||||||
|
|
||||||
|
if (adminUserIDInput == undefined || (userID == undefined && hashedIP == undefined)) {
|
||||||
|
//invalid request
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//hash the userID
|
||||||
|
adminUserIDInput = getHash(adminUserIDInput);
|
||||||
|
|
||||||
|
const isVIP = db.prepare("get", "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [adminUserIDInput]).userCount > 0;
|
||||||
|
if (!isVIP) {
|
||||||
|
//not authorized
|
||||||
|
res.sendStatus(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userID) {
|
||||||
|
//check to see if this user is already shadowbanned
|
||||||
|
const row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
|
if (enabled && row.userCount == 0) {
|
||||||
|
//add them to the shadow ban list
|
||||||
|
|
||||||
|
//add it to the table
|
||||||
|
privateDB.prepare('run', "INSERT INTO shadowBannedUsers VALUES(?)", [userID]);
|
||||||
|
|
||||||
|
//find all previous submissions and hide them
|
||||||
|
if (unHideOldSubmissions) {
|
||||||
|
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 1 WHERE userID = ?", [userID]);
|
||||||
|
}
|
||||||
|
} else if (!enabled && row.userCount > 0) {
|
||||||
|
//remove them from the shadow ban list
|
||||||
|
privateDB.prepare('run', "DELETE FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
|
//find all previous submissions and unhide them
|
||||||
|
if (unHideOldSubmissions) {
|
||||||
|
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 0 WHERE userID = ?", [userID]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO TYPESCRIPT a lof unused code, wtf is happening
|
||||||
|
else if (hashedIP) {
|
||||||
|
//check to see if this user is already shadowbanned
|
||||||
|
// let row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedIPs WHERE hashedIP = ?", [hashedIP]);
|
||||||
|
|
||||||
|
// if (enabled && row.userCount == 0) {
|
||||||
|
if (enabled) {
|
||||||
|
//add them to the shadow ban list
|
||||||
|
|
||||||
|
//add it to the table
|
||||||
|
// privateDB.prepare('run', "INSERT INTO shadowBannedIPs VALUES(?)", [hashedIP]);
|
||||||
|
|
||||||
|
|
||||||
|
//find all previous submissions and hide them
|
||||||
|
if (unHideOldSubmissions) {
|
||||||
|
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 1 WHERE timeSubmitted IN " +
|
||||||
|
"(SELECT privateDB.timeSubmitted FROM sponsorTimes LEFT JOIN privateDB.sponsorTimes as privateDB ON sponsorTimes.timeSubmitted=privateDB.timeSubmitted " +
|
||||||
|
"WHERE privateDB.hashedIP = ?)", [hashedIP]);
|
||||||
|
}
|
||||||
|
} /*else if (!enabled && row.userCount > 0) {
|
||||||
|
// //remove them from the shadow ban list
|
||||||
|
// privateDB.prepare('run', "DELETE FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
|
// //find all previous submissions and unhide them
|
||||||
|
// if (unHideOldSubmissions) {
|
||||||
|
// db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 0 WHERE userID = ?", [userID]);
|
||||||
|
// }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
|
||||||
|
|
||||||
module.exports = function viewedVideoSponsorTime(req, res) {
|
|
||||||
let UUID = req.query.UUID;
|
|
||||||
|
|
||||||
if (UUID == undefined) {
|
|
||||||
//invalid request
|
|
||||||
res.sendStatus(400);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//up the view count by one
|
|
||||||
db.prepare('run', "UPDATE sponsorTimes SET views = views + 1 WHERE UUID = ?", [UUID]);
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
}
|
|
||||||
16
src/routes/viewedVideoSponsorTime.ts
Normal file
16
src/routes/viewedVideoSponsorTime.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
import {Request, Response} from 'express';
|
||||||
|
|
||||||
|
export function viewedVideoSponsorTime(req: Request, res: Response): Response {
|
||||||
|
let UUID = req.query.UUID;
|
||||||
|
|
||||||
|
if (UUID == undefined) {
|
||||||
|
//invalid request
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
//up the view count by one
|
||||||
|
db.prepare('run', "UPDATE sponsorTimes SET views = views + 1 WHERE UUID = ?", [UUID]);
|
||||||
|
|
||||||
|
return res.sendStatus(200);
|
||||||
|
}
|
||||||
@@ -1,47 +1,47 @@
|
|||||||
var config = require('../config.js');
|
import {Request, Response} from 'express';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
var getHash = require('../utils/getHash.js');
|
import {isUserVIP} from '../utils/isUserVIP';
|
||||||
var getIP = require('../utils/getIP.js');
|
import request from 'request';
|
||||||
var getFormattedTime = require('../utils/getFormattedTime.js');
|
import {YouTubeAPI} from '../utils/youtubeApi';
|
||||||
var isUserTrustworthy = require('../utils/isUserTrustworthy.js');
|
import {db, privateDB} from '../databases/databases';
|
||||||
const { getVoteAuthor, getVoteAuthorRaw, dispatchEvent } = require('../utils/webhookUtils.js');
|
import {dispatchEvent, getVoteAuthor, getVoteAuthorRaw} from '../utils/webhookUtils';
|
||||||
|
import {isUserTrustworthy} from '../utils/isUserTrustworthy';
|
||||||
var databases = require('../databases/databases.js');
|
import {getFormattedTime} from '../utils/getFormattedTime';
|
||||||
var db = databases.db;
|
import {getIP} from '../utils/getIP';
|
||||||
var privateDB = databases.privateDB;
|
import {getHash} from '../utils/getHash';
|
||||||
var YouTubeAPI = require('../utils/youtubeAPI.js');
|
import {config} from '../config';
|
||||||
var request = require('request');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
const isUserVIP = require('../utils/isUserVIP.js');
|
|
||||||
|
|
||||||
const voteTypes = {
|
const voteTypes = {
|
||||||
normal: 0,
|
normal: 0,
|
||||||
incorrect: 1
|
incorrect: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface VoteData {
|
||||||
|
UUID: string;
|
||||||
|
nonAnonUserID: string;
|
||||||
|
voteTypeEnum: number;
|
||||||
|
isVIP: boolean;
|
||||||
|
isOwnSubmission: boolean;
|
||||||
|
row: {
|
||||||
|
votes: number;
|
||||||
|
views: number;
|
||||||
|
};
|
||||||
|
category: string;
|
||||||
|
incrementAmount: number;
|
||||||
|
oldIncrementAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function sendWebhooks(voteData: VoteData) {
|
||||||
* @param {Object} voteData
|
const submissionInfoRow = db.prepare('get', "SELECT s.videoID, s.userID, s.startTime, s.endTime, s.category, u.userName, " +
|
||||||
* @param {string} voteData.UUID
|
|
||||||
* @param {string} voteData.nonAnonUserID
|
|
||||||
* @param {number} voteData.voteTypeEnum
|
|
||||||
* @param {boolean} voteData.isVIP
|
|
||||||
* @param {boolean} voteData.isOwnSubmission
|
|
||||||
* @param voteData.row
|
|
||||||
* @param {string} voteData.category
|
|
||||||
* @param {number} voteData.incrementAmount
|
|
||||||
* @param {number} voteData.oldIncrementAmount
|
|
||||||
*/
|
|
||||||
function sendWebhooks(voteData) {
|
|
||||||
let submissionInfoRow = db.prepare('get', "SELECT s.videoID, s.userID, s.startTime, s.endTime, s.category, u.userName, " +
|
|
||||||
"(select count(1) from sponsorTimes where userID = s.userID) count, " +
|
"(select count(1) from sponsorTimes where userID = s.userID) count, " +
|
||||||
"(select count(1) from sponsorTimes where userID = s.userID and votes <= -2) disregarded " +
|
"(select count(1) from sponsorTimes where userID = s.userID and votes <= -2) disregarded " +
|
||||||
"FROM sponsorTimes s left join userNames u on s.userID = u.userID where s.UUID=?",
|
"FROM sponsorTimes s left join userNames u on s.userID = u.userID where s.UUID=?",
|
||||||
[voteData.UUID]);
|
[voteData.UUID]);
|
||||||
|
|
||||||
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [voteData.nonAnonUserID]);
|
const userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [voteData.nonAnonUserID]);
|
||||||
|
|
||||||
if (submissionInfoRow !== undefined && userSubmissionCountRow != undefined) {
|
if (submissionInfoRow !== undefined && userSubmissionCountRow != undefined) {
|
||||||
let webhookURL = null;
|
let webhookURL: string = null;
|
||||||
if (voteData.voteTypeEnum === voteTypes.normal) {
|
if (voteData.voteTypeEnum === voteTypes.normal) {
|
||||||
webhookURL = config.discordReportChannelWebhookURL;
|
webhookURL = config.discordReportChannelWebhookURL;
|
||||||
} else if (voteData.voteTypeEnum === voteTypes.incorrect) {
|
} else if (voteData.voteTypeEnum === voteTypes.incorrect) {
|
||||||
@@ -51,20 +51,20 @@ function sendWebhooks(voteData) {
|
|||||||
if (config.youtubeAPIKey !== null) {
|
if (config.youtubeAPIKey !== null) {
|
||||||
YouTubeAPI.listVideos(submissionInfoRow.videoID, (err, data) => {
|
YouTubeAPI.listVideos(submissionInfoRow.videoID, (err, data) => {
|
||||||
if (err || data.items.length === 0) {
|
if (err || data.items.length === 0) {
|
||||||
err && logger.error(err);
|
err && Logger.error(err.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let isUpvote = voteData.incrementAmount > 0;
|
const isUpvote = voteData.incrementAmount > 0;
|
||||||
// Send custom webhooks
|
// Send custom webhooks
|
||||||
dispatchEvent(isUpvote ? "vote.up" : "vote.down", {
|
dispatchEvent(isUpvote ? "vote.up" : "vote.down", {
|
||||||
"user": {
|
"user": {
|
||||||
"status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission)
|
"status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission),
|
||||||
},
|
},
|
||||||
"video": {
|
"video": {
|
||||||
"id": submissionInfoRow.videoID,
|
"id": submissionInfoRow.videoID,
|
||||||
"title": data.items[0].snippet.title,
|
"title": data.items[0].snippet.title,
|
||||||
"url": "https://www.youtube.com/watch?v=" + submissionInfoRow.videoID,
|
"url": "https://www.youtube.com/watch?v=" + submissionInfoRow.videoID,
|
||||||
"thumbnail": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : ""
|
"thumbnail": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
||||||
},
|
},
|
||||||
"submission": {
|
"submission": {
|
||||||
"UUID": voteData.UUID,
|
"UUID": voteData.UUID,
|
||||||
@@ -77,14 +77,14 @@ function sendWebhooks(voteData) {
|
|||||||
"username": submissionInfoRow.userName,
|
"username": submissionInfoRow.userName,
|
||||||
"submissions": {
|
"submissions": {
|
||||||
"total": submissionInfoRow.count,
|
"total": submissionInfoRow.count,
|
||||||
"ignored": submissionInfoRow.disregarded
|
"ignored": submissionInfoRow.disregarded,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"votes": {
|
"votes": {
|
||||||
"before": voteData.row.votes,
|
"before": voteData.row.votes,
|
||||||
"after": (voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount)
|
"after": (voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send discord message
|
// Send discord message
|
||||||
@@ -99,29 +99,29 @@ function sendWebhooks(voteData) {
|
|||||||
(voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount) + " Votes Now | " + voteData.row.views
|
(voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount) + " Votes Now | " + voteData.row.views
|
||||||
+ " Views**\n\n**Submission ID:** " + voteData.UUID
|
+ " Views**\n\n**Submission ID:** " + voteData.UUID
|
||||||
+ "\n**Category:** " + submissionInfoRow.category
|
+ "\n**Category:** " + submissionInfoRow.category
|
||||||
+ "\n\n**Submitted by:** "+submissionInfoRow.userName+"\n " + submissionInfoRow.userID
|
+ "\n\n**Submitted by:** " + submissionInfoRow.userName + "\n " + submissionInfoRow.userID
|
||||||
+ "\n\n**Total User Submissions:** "+submissionInfoRow.count
|
+ "\n\n**Total User Submissions:** " + submissionInfoRow.count
|
||||||
+ "\n**Ignored User Submissions:** "+submissionInfoRow.disregarded
|
+ "\n**Ignored User Submissions:** " + submissionInfoRow.disregarded
|
||||||
+"\n\n**Timestamp:** " +
|
+ "\n\n**Timestamp:** " +
|
||||||
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
|
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
|
||||||
"color": 10813440,
|
"color": 10813440,
|
||||||
"author": {
|
"author": {
|
||||||
"name": getVoteAuthor(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission)
|
"name": getVoteAuthor(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission),
|
||||||
},
|
},
|
||||||
"thumbnail": {
|
"thumbnail": {
|
||||||
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
||||||
}
|
},
|
||||||
}]
|
}],
|
||||||
}
|
},
|
||||||
}, (err, res) => {
|
}, (err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error("Failed to send reported submission Discord hook.");
|
Logger.error("Failed to send reported submission Discord hook.");
|
||||||
logger.error(JSON.stringify(err));
|
Logger.error(JSON.stringify(err));
|
||||||
logger.error("\n");
|
Logger.error("\n");
|
||||||
} else if (res && res.statusCode >= 400) {
|
} else if (res && res.statusCode >= 400) {
|
||||||
logger.error("Error sending reported submission Discord hook");
|
Logger.error("Error sending reported submission Discord hook");
|
||||||
logger.error(JSON.stringify(res));
|
Logger.error(JSON.stringify(res));
|
||||||
logger.error("\n");
|
Logger.error("\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -131,9 +131,9 @@ function sendWebhooks(voteData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
function categoryVote(UUID: string, userID: string, isVIP: boolean, category: any, hashedIP: string, res: Response) {
|
||||||
// Check if they've already made a vote
|
// Check if they've already made a vote
|
||||||
let previousVoteInfo = privateDB.prepare('get', "select count(*) as votes, category from categoryVotes where UUID = ? and userID = ?", [UUID, userID]);
|
const previousVoteInfo = privateDB.prepare('get', "select count(*) as votes, category from categoryVotes where UUID = ? and userID = ?", [UUID, userID]);
|
||||||
|
|
||||||
if (previousVoteInfo !== undefined && previousVoteInfo.category === category) {
|
if (previousVoteInfo !== undefined && previousVoteInfo.category === category) {
|
||||||
// Double vote, ignore
|
// Double vote, ignore
|
||||||
@@ -141,21 +141,21 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentCategory = db.prepare('get', "select category from sponsorTimes where UUID = ?", [UUID]);
|
const currentCategory = db.prepare('get', "select category from sponsorTimes where UUID = ?", [UUID]);
|
||||||
if (!currentCategory) {
|
if (!currentCategory) {
|
||||||
// Submission doesn't exist
|
// Submission doesn't exist
|
||||||
res.status("400").send("Submission doesn't exist.");
|
res.status(400).send("Submission doesn't exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.categoryList.includes(category)) {
|
if (!config.categoryList.includes(category)) {
|
||||||
res.status("400").send("Category doesn't exist.");
|
res.status(400).send("Category doesn't exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeSubmitted = Date.now();
|
const timeSubmitted = Date.now();
|
||||||
|
|
||||||
let voteAmount = isVIP ? 500 : 1;
|
const voteAmount = isVIP ? 500 : 1;
|
||||||
|
|
||||||
// Add the vote
|
// Add the vote
|
||||||
if (db.prepare('get', "select count(*) as count from categoryVotes where UUID = ? and category = ?", [UUID, category]).count > 0) {
|
if (db.prepare('get', "select count(*) as count from categoryVotes where UUID = ? and category = ?", [UUID, category]).count > 0) {
|
||||||
@@ -167,7 +167,7 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the info into the private db
|
// Add the info into the private db
|
||||||
if (previousVoteInfo !== undefined) {
|
if (previousVoteInfo !== undefined) {
|
||||||
// Reverse the previous vote
|
// Reverse the previous vote
|
||||||
db.prepare('run', "update categoryVotes set votes = votes - ? where UUID = ? and category = ?", [voteAmount, UUID, previousVoteInfo.category]);
|
db.prepare('run', "update categoryVotes set votes = votes - ? where UUID = ? and category = ?", [voteAmount, UUID, previousVoteInfo.category]);
|
||||||
|
|
||||||
@@ -177,15 +177,15 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See if the submissions category is ready to change
|
// See if the submissions category is ready to change
|
||||||
let currentCategoryInfo = db.prepare("get", "select votes from categoryVotes where UUID = ? and category = ?", [UUID, currentCategory.category]);
|
const currentCategoryInfo = db.prepare("get", "select votes from categoryVotes where UUID = ? and category = ?", [UUID, currentCategory.category]);
|
||||||
|
|
||||||
let submissionInfo = db.prepare("get", "SELECT userID, timeSubmitted FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
const submissionInfo = db.prepare("get", "SELECT userID, timeSubmitted FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||||
let isSubmissionVIP = submissionInfo && isUserVIP(submissionInfo.userID);
|
const isSubmissionVIP = submissionInfo && isUserVIP(submissionInfo.userID);
|
||||||
let startingVotes = isSubmissionVIP ? 10000 : 1;
|
const startingVotes = isSubmissionVIP ? 10000 : 1;
|
||||||
|
|
||||||
// Change this value from 1 in the future to make it harder to change categories
|
// Change this value from 1 in the future to make it harder to change categories
|
||||||
// Done this way without ORs incase the value is zero
|
// Done this way without ORs incase the value is zero
|
||||||
let currentCategoryCount = (currentCategoryInfo === undefined || currentCategoryInfo === null) ? startingVotes : currentCategoryInfo.votes;
|
const currentCategoryCount = (currentCategoryInfo === undefined || currentCategoryInfo === null) ? startingVotes : currentCategoryInfo.votes;
|
||||||
|
|
||||||
// Add submission as vote
|
// Add submission as vote
|
||||||
if (!currentCategoryInfo && submissionInfo) {
|
if (!currentCategoryInfo && submissionInfo) {
|
||||||
@@ -194,7 +194,7 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||||||
privateDB.prepare("run", "insert into categoryVotes (UUID, userID, hashedIP, category, timeSubmitted) values (?, ?, ?, ?, ?)", [UUID, submissionInfo.userID, "unknown", currentCategory.category, submissionInfo.timeSubmitted]);
|
privateDB.prepare("run", "insert into categoryVotes (UUID, userID, hashedIP, category, timeSubmitted) values (?, ?, ?, ?, ?)", [UUID, submissionInfo.userID, "unknown", currentCategory.category, submissionInfo.timeSubmitted]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextCategoryCount = (previousVoteInfo.votes || 0) + voteAmount;
|
const nextCategoryCount = (previousVoteInfo.votes || 0) + voteAmount;
|
||||||
|
|
||||||
//TODO: In the future, raise this number from zero to make it harder to change categories
|
//TODO: In the future, raise this number from zero to make it harder to change categories
|
||||||
// VIPs change it every time
|
// VIPs change it every time
|
||||||
@@ -206,11 +206,11 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function voteOnSponsorTime(req, res) {
|
async function voteOnSponsorTime(req: Request, res: Response) {
|
||||||
let UUID = req.query.UUID;
|
const UUID = req.query.UUID as string;
|
||||||
let userID = req.query.userID;
|
let userID = req.query.userID as string;
|
||||||
let type = req.query.type;
|
let type = req.query.type !== undefined ? parseInt(req.query.type as string) : undefined;
|
||||||
let category = req.query.category;
|
const category = req.query.category as string;
|
||||||
|
|
||||||
if (UUID === undefined || userID === undefined || (type === undefined && category === undefined)) {
|
if (UUID === undefined || userID === undefined || (type === undefined && category === undefined)) {
|
||||||
//invalid request
|
//invalid request
|
||||||
@@ -219,20 +219,20 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//hash the userID
|
//hash the userID
|
||||||
let nonAnonUserID = getHash(userID);
|
const nonAnonUserID = getHash(userID);
|
||||||
userID = getHash(userID + UUID);
|
userID = getHash(userID + UUID);
|
||||||
|
|
||||||
//x-forwarded-for if this server is behind a proxy
|
//x-forwarded-for if this server is behind a proxy
|
||||||
let ip = getIP(req);
|
const ip = getIP(req);
|
||||||
|
|
||||||
//hash the ip 5000 times so no one can get it from the database
|
//hash the ip 5000 times so no one can get it from the database
|
||||||
let hashedIP = getHash(ip + config.globalSalt);
|
const hashedIP = getHash(ip + config.globalSalt);
|
||||||
|
|
||||||
//check if this user is on the vip list
|
//check if this user is on the vip list
|
||||||
let isVIP = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [nonAnonUserID]).userCount > 0;
|
const isVIP = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [nonAnonUserID]).userCount > 0;
|
||||||
|
|
||||||
//check if user voting on own submission
|
//check if user voting on own submission
|
||||||
let isOwnSubmission = db.prepare("get", "SELECT UUID as submissionCount FROM sponsorTimes where userID = ? AND UUID = ?", [nonAnonUserID, UUID]) !== undefined;
|
const isOwnSubmission = db.prepare("get", "SELECT UUID as submissionCount FROM sponsorTimes where userID = ? AND UUID = ?", [nonAnonUserID, UUID]) !== undefined;
|
||||||
|
|
||||||
if (type === undefined && category !== undefined) {
|
if (type === undefined && category !== undefined) {
|
||||||
return categoryVote(UUID, nonAnonUserID, isVIP, category, hashedIP, res);
|
return categoryVote(UUID, nonAnonUserID, isVIP, category, hashedIP, res);
|
||||||
@@ -240,29 +240,29 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
|
|
||||||
if (type == 1 && !isVIP && !isOwnSubmission) {
|
if (type == 1 && !isVIP && !isOwnSubmission) {
|
||||||
// Check if upvoting hidden segment
|
// Check if upvoting hidden segment
|
||||||
let voteInfo = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
const voteInfo = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||||
|
|
||||||
if (voteInfo && voteInfo.votes <= -2) {
|
if (voteInfo && voteInfo.votes <= -2) {
|
||||||
res.status(403).send("Not allowed to upvote segment with too many downvotes unless you are VIP.")
|
res.status(403).send("Not allowed to upvote segment with too many downvotes unless you are VIP.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MILLISECONDS_IN_HOUR = 3600000;
|
const MILLISECONDS_IN_HOUR = 3600000;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let warningsCount = db.prepare('get', "SELECT count(1) as count FROM warnings WHERE userID = ? AND issueTime > ?",
|
const warningsCount = db.prepare('get', "SELECT count(1) as count FROM warnings WHERE userID = ? AND issueTime > ?",
|
||||||
[nonAnonUserID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))]
|
[nonAnonUserID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))],
|
||||||
).count;
|
).count;
|
||||||
|
|
||||||
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
||||||
return res.status(403).send('Vote blocked. Too many active warnings!');
|
return res.status(403).send('Vote blocked. Too many active warnings!');
|
||||||
}
|
}
|
||||||
|
|
||||||
let voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect;
|
const voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//check if vote has already happened
|
//check if vote has already happened
|
||||||
let votesRow = privateDB.prepare('get', "SELECT type FROM votes WHERE userID = ? AND UUID = ?", [userID, UUID]);
|
const votesRow = privateDB.prepare('get', "SELECT type FROM votes WHERE userID = ? AND UUID = ?", [userID, UUID]);
|
||||||
|
|
||||||
//-1 for downvote, 1 for upvote. Maybe more depending on reputation in the future
|
//-1 for downvote, 1 for upvote. Maybe more depending on reputation in the future
|
||||||
let incrementAmount = 0;
|
let incrementAmount = 0;
|
||||||
@@ -308,12 +308,12 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check if the increment amount should be multiplied (downvotes have more power if there have been many views)
|
//check if the increment amount should be multiplied (downvotes have more power if there have been many views)
|
||||||
let row = db.prepare('get', "SELECT votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
const row = db.prepare('get', "SELECT votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||||
|
|
||||||
if (voteTypeEnum === voteTypes.normal) {
|
if (voteTypeEnum === voteTypes.normal) {
|
||||||
if ((isVIP || isOwnSubmission) && incrementAmount < 0) {
|
if ((isVIP || isOwnSubmission) && incrementAmount < 0) {
|
||||||
//this user is a vip and a downvote
|
//this user is a vip and a downvote
|
||||||
incrementAmount = - (row.votes + 2 - oldIncrementAmount);
|
incrementAmount = -(row.votes + 2 - oldIncrementAmount);
|
||||||
type = incrementAmount;
|
type = incrementAmount;
|
||||||
}
|
}
|
||||||
} else if (voteTypeEnum == voteTypes.incorrect) {
|
} else if (voteTypeEnum == voteTypes.incorrect) {
|
||||||
@@ -325,10 +325,10 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only change the database if they have made a submission before and haven't voted recently
|
// Only change the database if they have made a submission before and haven't voted recently
|
||||||
let ableToVote = isVIP
|
const ableToVote = isVIP
|
||||||
|| (db.prepare("get", "SELECT userID FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]) !== undefined
|
|| (db.prepare("get", "SELECT userID FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]) !== undefined
|
||||||
&& privateDB.prepare("get", "SELECT userID FROM shadowBannedUsers WHERE userID = ?", [nonAnonUserID]) === undefined
|
&& privateDB.prepare("get", "SELECT userID FROM shadowBannedUsers WHERE userID = ?", [nonAnonUserID]) === undefined
|
||||||
&& privateDB.prepare("get", "SELECT UUID FROM votes WHERE UUID = ? AND hashedIP = ? AND userID != ?", [UUID, hashedIP, userID]) === undefined);
|
&& privateDB.prepare("get", "SELECT UUID FROM votes WHERE UUID = ? AND hashedIP = ? AND userID != ?", [UUID, hashedIP, userID]) === undefined);
|
||||||
|
|
||||||
if (ableToVote) {
|
if (ableToVote) {
|
||||||
//update the votes table
|
//update the votes table
|
||||||
@@ -352,17 +352,17 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
//for each positive vote, see if a hidden submission can be shown again
|
//for each positive vote, see if a hidden submission can be shown again
|
||||||
if (incrementAmount > 0 && voteTypeEnum === voteTypes.normal) {
|
if (incrementAmount > 0 && voteTypeEnum === voteTypes.normal) {
|
||||||
//find the UUID that submitted the submission that was voted on
|
//find the UUID that submitted the submission that was voted on
|
||||||
let submissionUserIDInfo = db.prepare('get', "SELECT userID FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
const submissionUserIDInfo = db.prepare('get', "SELECT userID FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||||
if (!submissionUserIDInfo) {
|
if (!submissionUserIDInfo) {
|
||||||
// They are voting on a non-existent submission
|
// They are voting on a non-existent submission
|
||||||
res.status(400).send("Voting on a non-existent submission");
|
res.status(400).send("Voting on a non-existent submission");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let submissionUserID = submissionUserIDInfo.userID;
|
const submissionUserID = submissionUserIDInfo.userID;
|
||||||
|
|
||||||
//check if any submissions are hidden
|
//check if any submissions are hidden
|
||||||
let hiddenSubmissionsRow = db.prepare('get', "SELECT count(*) as hiddenSubmissions FROM sponsorTimes WHERE userID = ? AND shadowHidden > 0", [submissionUserID]);
|
const hiddenSubmissionsRow = db.prepare('get', "SELECT count(*) as hiddenSubmissions FROM sponsorTimes WHERE userID = ? AND shadowHidden > 0", [submissionUserID]);
|
||||||
|
|
||||||
if (hiddenSubmissionsRow.hiddenSubmissions > 0) {
|
if (hiddenSubmissionsRow.hiddenSubmissions > 0) {
|
||||||
//see if some of this users submissions should be visible again
|
//see if some of this users submissions should be visible again
|
||||||
@@ -387,17 +387,16 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
row,
|
row,
|
||||||
category,
|
category,
|
||||||
incrementAmount,
|
incrementAmount,
|
||||||
oldIncrementAmount
|
oldIncrementAmount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
Logger.error(err);
|
||||||
|
|
||||||
res.status(500).json({error: 'Internal error creating segment vote'});
|
res.status(500).json({error: 'Internal error creating segment vote'});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
export {
|
||||||
voteOnSponsorTime,
|
voteOnSponsorTime,
|
||||||
endpoint: voteOnSponsorTime
|
|
||||||
};
|
};
|
||||||
52
src/types/config.model.ts
Normal file
52
src/types/config.model.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import * as redis from 'redis';
|
||||||
|
|
||||||
|
export interface SBSConfig {
|
||||||
|
port: number;
|
||||||
|
mockPort?: number;
|
||||||
|
globalSalt: string;
|
||||||
|
adminUserID: string;
|
||||||
|
youtubeAPIKey?: string;
|
||||||
|
discordReportChannelWebhookURL?: string;
|
||||||
|
discordFirstTimeSubmissionsWebhookURL?: string;
|
||||||
|
discordCompletelyIncorrectReportWebhookURL?: string;
|
||||||
|
neuralBlockURL?: string;
|
||||||
|
discordNeuralBlockRejectWebhookURL?: string;
|
||||||
|
userCounterURL?: string;
|
||||||
|
proxySubmission?: string;
|
||||||
|
behindProxy: string | boolean;
|
||||||
|
db: string;
|
||||||
|
privateDB: string;
|
||||||
|
createDatabaseIfNotExist: boolean;
|
||||||
|
schemaFolder: string;
|
||||||
|
dbSchema: string;
|
||||||
|
privateDBSchema: string;
|
||||||
|
mode: string;
|
||||||
|
readOnly: boolean;
|
||||||
|
webhooks: WebhookConfig[];
|
||||||
|
categoryList: string[];
|
||||||
|
getTopUsersCacheTimeMinutes: number;
|
||||||
|
maxNumberOfActiveWarnings: number;
|
||||||
|
hoursAfterWarningExpires: number;
|
||||||
|
rateLimit: {
|
||||||
|
vote: RateLimitConfig;
|
||||||
|
view: RateLimitConfig;
|
||||||
|
};
|
||||||
|
mysql?: any;
|
||||||
|
privateMysql?: any;
|
||||||
|
minimumPrefix?: string;
|
||||||
|
maximumPrefix?: string;
|
||||||
|
redis?: redis.ClientOpts;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebhookConfig {
|
||||||
|
url: string;
|
||||||
|
key: string;
|
||||||
|
scopes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RateLimitConfig {
|
||||||
|
windowMs: number;
|
||||||
|
max: number;
|
||||||
|
message: string;
|
||||||
|
statusCode: number;
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
module.exports = function createMemoryCache(memoryFn, cacheTimeMs) {
|
|
||||||
if (isNaN(cacheTimeMs)) cacheTimeMs = 0;
|
|
||||||
|
|
||||||
// holds the promise results
|
|
||||||
const cache = new Map();
|
|
||||||
// holds the promises that are not fulfilled
|
|
||||||
const promiseMemory = new Map();
|
|
||||||
return (...args) => {
|
|
||||||
// create cacheKey by joining arguments as string
|
|
||||||
const cacheKey = args.join('.');
|
|
||||||
// check if promising is already running
|
|
||||||
if (promiseMemory.has(cacheKey)) {
|
|
||||||
return promiseMemory.get(cacheKey);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// check if result is in cache
|
|
||||||
if (cache.has(cacheKey)) {
|
|
||||||
const cacheItem = cache.get(cacheKey);
|
|
||||||
const now = Date.now();
|
|
||||||
// check if cache is valid
|
|
||||||
if (!(cacheItem.cacheTime + cacheTimeMs < now)) {
|
|
||||||
return Promise.resolve(cacheItem.result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// create new promise
|
|
||||||
const promise = new Promise(async (resolve, reject) => {
|
|
||||||
resolve((await memoryFn(...args)));
|
|
||||||
});
|
|
||||||
// store promise reference until fulfilled
|
|
||||||
promiseMemory.set(cacheKey, promise);
|
|
||||||
return promise.then(result => {
|
|
||||||
// store promise result in cache
|
|
||||||
cache.set(cacheKey, {
|
|
||||||
result,
|
|
||||||
cacheTime: Date.now(),
|
|
||||||
});
|
|
||||||
// remove fulfilled promise from memory
|
|
||||||
promiseMemory.delete(cacheKey);
|
|
||||||
// return promise result
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
43
src/utils/createMemoryCache.ts
Normal file
43
src/utils/createMemoryCache.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
export function createMemoryCache(memoryFn: (...args: any[]) => void, cacheTimeMs: number) {
|
||||||
|
if (isNaN(cacheTimeMs)) cacheTimeMs = 0;
|
||||||
|
|
||||||
|
// holds the promise results
|
||||||
|
const cache = new Map();
|
||||||
|
// holds the promises that are not fulfilled
|
||||||
|
const promiseMemory = new Map();
|
||||||
|
return (...args: any[]) => {
|
||||||
|
// create cacheKey by joining arguments as string
|
||||||
|
const cacheKey = args.join('.');
|
||||||
|
// check if promising is already running
|
||||||
|
if (promiseMemory.has(cacheKey)) {
|
||||||
|
return promiseMemory.get(cacheKey);
|
||||||
|
} else {
|
||||||
|
// check if result is in cache
|
||||||
|
if (cache.has(cacheKey)) {
|
||||||
|
const cacheItem = cache.get(cacheKey);
|
||||||
|
const now = Date.now();
|
||||||
|
// check if cache is valid
|
||||||
|
if (!(cacheItem.cacheTime + cacheTimeMs < now)) {
|
||||||
|
return Promise.resolve(cacheItem.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create new promise
|
||||||
|
const promise = new Promise(async (resolve) => {
|
||||||
|
resolve((await memoryFn(...args)));
|
||||||
|
});
|
||||||
|
// store promise reference until fulfilled
|
||||||
|
promiseMemory.set(cacheKey, promise);
|
||||||
|
return promise.then(result => {
|
||||||
|
// store promise result in cache
|
||||||
|
cache.set(cacheKey, {
|
||||||
|
result,
|
||||||
|
cacheTime: Date.now(),
|
||||||
|
});
|
||||||
|
// remove fulfilled promise from memory
|
||||||
|
promiseMemory.delete(cacheKey);
|
||||||
|
// return promise result
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//converts time in seconds to minutes:seconds
|
|
||||||
module.exports = function getFormattedTime(totalSeconds) {
|
|
||||||
let minutes = Math.floor(totalSeconds / 60);
|
|
||||||
let seconds = totalSeconds - minutes * 60;
|
|
||||||
let secondsDisplay = seconds.toFixed(3);
|
|
||||||
if (seconds < 10) {
|
|
||||||
//add a zero
|
|
||||||
secondsDisplay = "0" + secondsDisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
let formatted = minutes+ ":" + secondsDisplay;
|
|
||||||
|
|
||||||
return formatted;
|
|
||||||
}
|
|
||||||
14
src/utils/getFormattedTime.ts
Normal file
14
src/utils/getFormattedTime.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Converts time in seconds to minutes:seconds
|
||||||
|
*/
|
||||||
|
export function getFormattedTime(totalSeconds: number) {
|
||||||
|
let minutes = Math.floor(totalSeconds / 60);
|
||||||
|
let seconds = totalSeconds - minutes * 60;
|
||||||
|
let secondsDisplay = seconds.toFixed(3);
|
||||||
|
if (seconds < 10) {
|
||||||
|
//add a zero
|
||||||
|
secondsDisplay = '0' + secondsDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
return minutes + ':' + secondsDisplay;
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
var crypto = require('crypto');
|
|
||||||
|
|
||||||
module.exports = function (value, times=5000) {
|
|
||||||
if (times <= 0) return "";
|
|
||||||
|
|
||||||
for (let i = 0; i < times; i++) {
|
|
||||||
let hashCreator = crypto.createHash('sha256');
|
|
||||||
value = hashCreator.update(value).digest('hex');
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
12
src/utils/getHash.ts
Normal file
12
src/utils/getHash.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export function getHash(value: string, times = 5000) {
|
||||||
|
if (times <= 0) return "";
|
||||||
|
|
||||||
|
for (let i = 0; i < times; i++) {
|
||||||
|
let hashCreator = crypto.createHash('sha256');
|
||||||
|
value = hashCreator.update(value).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
var config = require('../config.js');
|
|
||||||
|
|
||||||
module.exports = function getIP(req) {
|
|
||||||
if (config.behindProxy === true || config.behindProxy === "true") config.behindProxy = "X-Forwarded-For";
|
|
||||||
|
|
||||||
switch (config.behindProxy) {
|
|
||||||
case "X-Forwarded-For":
|
|
||||||
return req.headers['x-forwarded-for'];
|
|
||||||
case "Cloudflare":
|
|
||||||
return req.headers['cf-connecting-ip'];
|
|
||||||
case "X-Real-IP":
|
|
||||||
return req.headers['x-real-ip'];
|
|
||||||
default:
|
|
||||||
return req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
src/utils/getIP.ts
Normal file
19
src/utils/getIP.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
import {Request} from 'express';
|
||||||
|
|
||||||
|
export function getIP(req: Request): string {
|
||||||
|
if (config.behindProxy === true || config.behindProxy === "true") {
|
||||||
|
config.behindProxy = "X-Forwarded-For";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config.behindProxy as string) {
|
||||||
|
case "X-Forwarded-For":
|
||||||
|
return req.headers['x-forwarded-for'] as string;
|
||||||
|
case "Cloudflare":
|
||||||
|
return req.headers['cf-connecting-ip'] as string;
|
||||||
|
case "X-Real-IP":
|
||||||
|
return req.headers['x-real-ip'] as string;
|
||||||
|
default:
|
||||||
|
return req.connection.remoteAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
const getHash = require('./getHash.js');
|
|
||||||
|
|
||||||
module.exports = function getSubmissionUUID(videoID, category, userID,
|
|
||||||
startTime, endTime) {
|
|
||||||
return getHash('v2-categories' + videoID + startTime + endTime + category +
|
|
||||||
userID, 1);
|
|
||||||
};
|
|
||||||
5
src/utils/getSubmissionUUID.ts
Normal file
5
src/utils/getSubmissionUUID.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import {getHash} from './getHash';
|
||||||
|
|
||||||
|
export function getSubmissionUUID(videoID: string, category: string, userID: string, startTime: number, endTime: number) {
|
||||||
|
return getHash('v2-categories' + videoID + startTime + endTime + category + userID, 1);
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
const config = require('../config.js');
|
import {config} from '../config';
|
||||||
|
|
||||||
const minimumPrefix = config.minimumPrefix || '3';
|
const minimumPrefix = config.minimumPrefix || '3';
|
||||||
const maximumPrefix = config.maximumPrefix || '32'; // Half the hash.
|
const maximumPrefix = config.maximumPrefix || '32'; // Half the hash.
|
||||||
|
|
||||||
const prefixChecker = new RegExp('^[\\da-f]{' + minimumPrefix + ',' + maximumPrefix + '}$', 'i');
|
const prefixChecker = new RegExp('^[\\da-f]{' + minimumPrefix + ',' + maximumPrefix + '}$', 'i');
|
||||||
|
|
||||||
module.exports = (prefix) => {
|
export function hashPrefixTester(prefix: string): boolean {
|
||||||
return prefixChecker.test(prefix);
|
return prefixChecker.test(prefix);
|
||||||
};
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
var databases = require('../databases/databases.js');
|
|
||||||
var db = databases.db;
|
|
||||||
|
|
||||||
//returns true if the user is considered trustworthy
|
|
||||||
//this happens after a user has made 5 submissions and has less than 60% downvoted submissions
|
|
||||||
|
|
||||||
module.exports = async (userID) => {
|
|
||||||
//check to see if this user how many submissions this user has submitted
|
|
||||||
let totalSubmissionsRow = db.prepare('get', "SELECT count(*) as totalSubmissions, sum(votes) as voteSum FROM sponsorTimes WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
if (totalSubmissionsRow.totalSubmissions > 5) {
|
|
||||||
//check if they have a high downvote ratio
|
|
||||||
let downvotedSubmissionsRow = db.prepare('get', "SELECT count(*) as downvotedSubmissions FROM sponsorTimes WHERE userID = ? AND (votes < 0 OR shadowHidden > 0)", [userID]);
|
|
||||||
|
|
||||||
return (downvotedSubmissionsRow.downvotedSubmissions / totalSubmissionsRow.totalSubmissions) < 0.6 ||
|
|
||||||
(totalSubmissionsRow.voteSum > downvotedSubmissionsRow.downvotedSubmissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
20
src/utils/isUserTrustworthy.ts
Normal file
20
src/utils/isUserTrustworthy.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import {db} from '../databases/databases';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user is considered trustworthy. This happens after a user has made 5 submissions and has less than 60% downvoted submissions
|
||||||
|
* @param userID
|
||||||
|
*/
|
||||||
|
export async function isUserTrustworthy(userID: string): Promise<boolean> {
|
||||||
|
//check to see if this user how many submissions this user has submitted
|
||||||
|
const totalSubmissionsRow = db.prepare('get', "SELECT count(*) as totalSubmissions, sum(votes) as voteSum FROM sponsorTimes WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
|
if (totalSubmissionsRow.totalSubmissions > 5) {
|
||||||
|
//check if they have a high downvote ratio
|
||||||
|
const downvotedSubmissionsRow = db.prepare('get', "SELECT count(*) as downvotedSubmissions FROM sponsorTimes WHERE userID = ? AND (votes < 0 OR shadowHidden > 0)", [userID]);
|
||||||
|
|
||||||
|
return (downvotedSubmissionsRow.downvotedSubmissions / totalSubmissionsRow.totalSubmissions) < 0.6 ||
|
||||||
|
(totalSubmissionsRow.voteSum > downvotedSubmissionsRow.downvotedSubmissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
const databases = require('../databases/databases.js');
|
import {db} from '../databases/databases';
|
||||||
const db = databases.db;
|
|
||||||
|
|
||||||
module.exports = (userID) => {
|
export function isUserVIP(userID: string): boolean {
|
||||||
return db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
return db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
const config = require('../config.js');
|
|
||||||
|
|
||||||
const levels = {
|
|
||||||
ERROR: "ERROR",
|
|
||||||
WARN: "WARN",
|
|
||||||
INFO: "INFO",
|
|
||||||
DEBUG: "DEBUG"
|
|
||||||
};
|
|
||||||
|
|
||||||
const colors = {
|
|
||||||
Reset: "\x1b[0m",
|
|
||||||
Bright: "\x1b[1m",
|
|
||||||
Dim: "\x1b[2m",
|
|
||||||
Underscore: "\x1b[4m",
|
|
||||||
Blink: "\x1b[5m",
|
|
||||||
Reverse: "\x1b[7m",
|
|
||||||
Hidden: "\x1b[8m",
|
|
||||||
|
|
||||||
FgBlack: "\x1b[30m",
|
|
||||||
FgRed: "\x1b[31m",
|
|
||||||
FgGreen: "\x1b[32m",
|
|
||||||
FgYellow: "\x1b[33m",
|
|
||||||
FgBlue: "\x1b[34m",
|
|
||||||
FgMagenta: "\x1b[35m",
|
|
||||||
FgCyan: "\x1b[36m",
|
|
||||||
FgWhite: "\x1b[37m",
|
|
||||||
|
|
||||||
BgBlack: "\x1b[40m",
|
|
||||||
BgRed: "\x1b[41m",
|
|
||||||
BgGreen: "\x1b[42m",
|
|
||||||
BgYellow: "\x1b[43m",
|
|
||||||
BgBlue: "\x1b[44m",
|
|
||||||
BgMagenta: "\x1b[45m",
|
|
||||||
BgCyan: "\x1b[46m",
|
|
||||||
BgWhite: "\x1b[47m",
|
|
||||||
}
|
|
||||||
|
|
||||||
const settings = {
|
|
||||||
ERROR: true,
|
|
||||||
WARN: true,
|
|
||||||
INFO: false,
|
|
||||||
DEBUG: false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (config.mode === 'development') {
|
|
||||||
settings.INFO = true;
|
|
||||||
settings.DEBUG = true;
|
|
||||||
} else if (config.mode === 'test') {
|
|
||||||
settings.WARN = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function log(level, string) {
|
|
||||||
if (!!settings[level]) {
|
|
||||||
let color = colors.Bright;
|
|
||||||
if (level === levels.ERROR) color = colors.FgRed;
|
|
||||||
if (level === levels.WARN) color = colors.FgYellow;
|
|
||||||
|
|
||||||
if (level.length === 4) {level = level + " "}; // ensure logs are aligned
|
|
||||||
console.log(colors.Dim, level + " " + new Date().toISOString() + ": ", color, string, colors.Reset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
levels,
|
|
||||||
log,
|
|
||||||
error: (string) => {log(levels.ERROR, string)},
|
|
||||||
warn: (string) => {log(levels.WARN, string)},
|
|
||||||
info: (string) => {log(levels.INFO, string)},
|
|
||||||
debug: (string) => {log(levels.DEBUG, string)},
|
|
||||||
};
|
|
||||||
89
src/utils/logger.ts
Normal file
89
src/utils/logger.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
|
||||||
|
const enum LogLevel {
|
||||||
|
ERROR = "ERROR",
|
||||||
|
WARN = "WARN",
|
||||||
|
INFO = "INFO",
|
||||||
|
DEBUG = "DEBUG"
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
Reset: "\x1b[0m",
|
||||||
|
Bright: "\x1b[1m",
|
||||||
|
Dim: "\x1b[2m",
|
||||||
|
Underscore: "\x1b[4m",
|
||||||
|
Blink: "\x1b[5m",
|
||||||
|
Reverse: "\x1b[7m",
|
||||||
|
Hidden: "\x1b[8m",
|
||||||
|
|
||||||
|
FgBlack: "\x1b[30m",
|
||||||
|
FgRed: "\x1b[31m",
|
||||||
|
FgGreen: "\x1b[32m",
|
||||||
|
FgYellow: "\x1b[33m",
|
||||||
|
FgBlue: "\x1b[34m",
|
||||||
|
FgMagenta: "\x1b[35m",
|
||||||
|
FgCyan: "\x1b[36m",
|
||||||
|
FgWhite: "\x1b[37m",
|
||||||
|
|
||||||
|
BgBlack: "\x1b[40m",
|
||||||
|
BgRed: "\x1b[41m",
|
||||||
|
BgGreen: "\x1b[42m",
|
||||||
|
BgYellow: "\x1b[43m",
|
||||||
|
BgBlue: "\x1b[44m",
|
||||||
|
BgMagenta: "\x1b[45m",
|
||||||
|
BgCyan: "\x1b[46m",
|
||||||
|
BgWhite: "\x1b[47m",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
private _settings = {
|
||||||
|
ERROR: true,
|
||||||
|
WARN: true,
|
||||||
|
INFO: false,
|
||||||
|
DEBUG: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (config.mode === 'development') {
|
||||||
|
this._settings.INFO = true;
|
||||||
|
this._settings.DEBUG = true;
|
||||||
|
} else if (config.mode === 'test') {
|
||||||
|
this._settings.WARN = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error(str: string) {
|
||||||
|
this.log(LogLevel.ERROR, str);
|
||||||
|
}
|
||||||
|
warn(str: string) {
|
||||||
|
this.log(LogLevel.WARN, str);
|
||||||
|
}
|
||||||
|
info(str: string) {
|
||||||
|
this.log(LogLevel.INFO, str);
|
||||||
|
}
|
||||||
|
debug(str: string) {
|
||||||
|
this.log(LogLevel.DEBUG, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private log(level: LogLevel, str: string) {
|
||||||
|
if (!this._settings[level]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = colors.Bright;
|
||||||
|
if (level === LogLevel.ERROR) color = colors.FgRed;
|
||||||
|
if (level === LogLevel.WARN) color = colors.FgYellow;
|
||||||
|
|
||||||
|
let levelStr = level.toString();
|
||||||
|
if (levelStr.length === 4) {
|
||||||
|
levelStr += " "; // ensure logs are aligned
|
||||||
|
}
|
||||||
|
console.log(colors.Dim, `${levelStr} ${new Date().toISOString()}: `, color, str, colors.Reset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const loggerInstance = new Logger();
|
||||||
|
export {
|
||||||
|
loggerInstance as Logger
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
const config = require('../config.js');
|
|
||||||
const logger = require('./logger.js');
|
|
||||||
|
|
||||||
if (config.redis) {
|
|
||||||
const redis = require('redis');
|
|
||||||
logger.info('Connected to redis');
|
|
||||||
const client = redis.createClient(config.redis);
|
|
||||||
module.exports = client;
|
|
||||||
} else {
|
|
||||||
module.exports = {
|
|
||||||
get: (key, callback) => {
|
|
||||||
callback(false);
|
|
||||||
},
|
|
||||||
set: (key, value, callback) => {
|
|
||||||
callback(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
19
src/utils/redis.ts
Normal file
19
src/utils/redis.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
import {Logger} from './logger';
|
||||||
|
import redis, {Callback} from 'redis';
|
||||||
|
|
||||||
|
let get, set;
|
||||||
|
if (config.redis) {
|
||||||
|
Logger.info('Connected to redis');
|
||||||
|
const client = redis.createClient(config.redis);
|
||||||
|
get = client.get;
|
||||||
|
set = client.set;
|
||||||
|
} else {
|
||||||
|
get = (key: string, callback?: Callback<string | null>) => callback(null, undefined);
|
||||||
|
set = (key: string, value: string, callback?: Callback<string | null>) => callback(null, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
};
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
const config = require('../config.js');
|
|
||||||
const logger = require('../utils/logger.js');
|
|
||||||
const request = require('request');
|
|
||||||
|
|
||||||
function getVoteAuthorRaw(submissionCount, isVIP, isOwnSubmission) {
|
|
||||||
if (isOwnSubmission) {
|
|
||||||
return "self";
|
|
||||||
} else if (isVIP) {
|
|
||||||
return "vip";
|
|
||||||
} else if (submissionCount === 0) {
|
|
||||||
return "new";
|
|
||||||
} else {
|
|
||||||
return "other";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function getVoteAuthor(submissionCount, isVIP, isOwnSubmission) {
|
|
||||||
if (submissionCount === 0) {
|
|
||||||
return "Report by New User";
|
|
||||||
} else if (isOwnSubmission) {
|
|
||||||
return "Report by Submitter";
|
|
||||||
} else if (isVIP) {
|
|
||||||
return "Report by VIP User";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatchEvent(scope, data) {
|
|
||||||
let webhooks = config.webhooks;
|
|
||||||
if (webhooks === undefined || webhooks.length === 0) return;
|
|
||||||
logger.debug("Dispatching webhooks");
|
|
||||||
webhooks.forEach(webhook => {
|
|
||||||
let webhookURL = webhook.url;
|
|
||||||
let authKey = webhook.key;
|
|
||||||
let scopes = webhook.scopes || [];
|
|
||||||
if (!scopes.includes(scope.toLowerCase())) return;
|
|
||||||
request.post(webhookURL, {json: data, headers: {
|
|
||||||
"Authorization": authKey,
|
|
||||||
"Event-Type": scope // Maybe change this in the future?
|
|
||||||
}}).on('error', (e) => {
|
|
||||||
logger.warn('Couldn\'t send webhook to ' + webhook.url);
|
|
||||||
logger.warn(e);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getVoteAuthorRaw,
|
|
||||||
getVoteAuthor,
|
|
||||||
dispatchEvent
|
|
||||||
}
|
|
||||||
56
src/utils/webhookUtils.ts
Normal file
56
src/utils/webhookUtils.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
import {Logger} from '../utils/logger';
|
||||||
|
import request from 'request';
|
||||||
|
|
||||||
|
function getVoteAuthorRaw(submissionCount: number, isVIP: boolean, isOwnSubmission: boolean): string {
|
||||||
|
if (isOwnSubmission) {
|
||||||
|
return "self";
|
||||||
|
} else if (isVIP) {
|
||||||
|
return "vip";
|
||||||
|
} else if (submissionCount === 0) {
|
||||||
|
return "new";
|
||||||
|
} else {
|
||||||
|
return "other";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVoteAuthor(submissionCount: number, isVIP: boolean, isOwnSubmission: boolean): string {
|
||||||
|
if (submissionCount === 0) {
|
||||||
|
return "Report by New User";
|
||||||
|
} else if (isOwnSubmission) {
|
||||||
|
return "Report by Submitter";
|
||||||
|
} else if (isVIP) {
|
||||||
|
return "Report by VIP User";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchEvent(scope: string, data: any): void {
|
||||||
|
let webhooks = config.webhooks;
|
||||||
|
if (webhooks === undefined || webhooks.length === 0) return;
|
||||||
|
Logger.debug("Dispatching webhooks");
|
||||||
|
webhooks.forEach(webhook => {
|
||||||
|
let webhookURL = webhook.url;
|
||||||
|
let authKey = webhook.key;
|
||||||
|
let scopes = webhook.scopes || [];
|
||||||
|
if (!scopes.includes(scope.toLowerCase())) return;
|
||||||
|
|
||||||
|
// TODO TYPESCRIPT deprecated
|
||||||
|
request.post(webhookURL, {
|
||||||
|
json: data, headers: {
|
||||||
|
"Authorization": authKey,
|
||||||
|
"Event-Type": scope, // Maybe change this in the future?
|
||||||
|
},
|
||||||
|
}).on('error', (e) => {
|
||||||
|
Logger.warn('Couldn\'t send webhook to ' + webhook.url);
|
||||||
|
Logger.warn(e.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getVoteAuthorRaw,
|
||||||
|
getVoteAuthor,
|
||||||
|
dispatchEvent,
|
||||||
|
};
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
var config = require('../config.js');
|
|
||||||
|
|
||||||
// YouTube API
|
|
||||||
const YouTubeAPI = require("youtube-api");
|
|
||||||
const redis = require('./redis.js');
|
|
||||||
const logger = require('./logger.js');
|
|
||||||
|
|
||||||
var exportObject;
|
|
||||||
// If in test mode, return a mocked youtube object
|
|
||||||
// otherwise return an authenticated youtube api
|
|
||||||
if (config.mode === "test") {
|
|
||||||
exportObject = require("../../test/youtubeMock.js");
|
|
||||||
} else {
|
|
||||||
YouTubeAPI.authenticate({
|
|
||||||
type: "key",
|
|
||||||
key: config.youtubeAPIKey
|
|
||||||
});
|
|
||||||
exportObject = YouTubeAPI;
|
|
||||||
|
|
||||||
// YouTubeAPI.videos.list wrapper with cacheing
|
|
||||||
exportObject.listVideos = (videoID, callback) => {
|
|
||||||
let part = 'contentDetails,snippet';
|
|
||||||
if (videoID.length !== 11 || videoID.includes(".")) {
|
|
||||||
callback("Invalid video ID");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let redisKey = "youtube.video." + videoID;
|
|
||||||
redis.get(redisKey, (getErr, result) => {
|
|
||||||
if (getErr || !result) {
|
|
||||||
logger.debug("redis: no cache for video information: " + videoID);
|
|
||||||
YouTubeAPI.videos.list({
|
|
||||||
part,
|
|
||||||
id: videoID
|
|
||||||
}, (ytErr, data) => {
|
|
||||||
if (!ytErr) {
|
|
||||||
// Only set cache if data returned
|
|
||||||
if (data.items.length > 0) {
|
|
||||||
redis.set(redisKey, JSON.stringify(data), (setErr) => {
|
|
||||||
if(setErr) {
|
|
||||||
logger.warn(setErr);
|
|
||||||
} else {
|
|
||||||
logger.debug("redis: video information cache set for: " + videoID);
|
|
||||||
}
|
|
||||||
callback(false, data); // don't fail
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
callback(false, data); // don't fail
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
callback(ytErr, data)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
logger.debug("redis: fetched video information from cache: " + videoID);
|
|
||||||
callback(getErr, JSON.parse(result));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = exportObject;
|
|
||||||
69
src/utils/youtubeApi.ts
Normal file
69
src/utils/youtubeApi.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import {config} from '../config';
|
||||||
|
import {Logger} from './logger';
|
||||||
|
import * as redis from './redis';
|
||||||
|
// @ts-ignore
|
||||||
|
import YouTubeAPI from 'youtube-api';
|
||||||
|
|
||||||
|
import {YouTubeAPI as youtubeApiTest} from '../../test/youtubeMock';
|
||||||
|
|
||||||
|
let _youtubeApi: {
|
||||||
|
listVideos: (videoID: string, callback: (err: string | boolean, data: any) => void) => void
|
||||||
|
};
|
||||||
|
// If in test mode, return a mocked youtube object
|
||||||
|
// otherwise return an authenticated youtube api
|
||||||
|
if (config.mode === "test") {
|
||||||
|
_youtubeApi = youtubeApiTest;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_youtubeApi = YouTubeAPI;
|
||||||
|
|
||||||
|
YouTubeAPI.authenticate({
|
||||||
|
type: "key",
|
||||||
|
key: config.youtubeAPIKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
// YouTubeAPI.videos.list wrapper with cacheing
|
||||||
|
_youtubeApi.listVideos = (videoID: string, callback: (err: string | boolean, data: any) => void) => {
|
||||||
|
const part = 'contentDetails,snippet';
|
||||||
|
if (videoID.length !== 11 || videoID.includes(".")) {
|
||||||
|
callback("Invalid video ID", undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const redisKey = "youtube.video." + videoID;
|
||||||
|
redis.get(redisKey, (getErr: string, result: string) => {
|
||||||
|
if (getErr || !result) {
|
||||||
|
Logger.debug("redis: no cache for video information: " + videoID);
|
||||||
|
YouTubeAPI.videos.list({
|
||||||
|
part,
|
||||||
|
id: videoID,
|
||||||
|
}, (ytErr: boolean | string, data: any) => {
|
||||||
|
if (!ytErr) {
|
||||||
|
// Only set cache if data returned
|
||||||
|
if (data.items.length > 0) {
|
||||||
|
redis.set(redisKey, JSON.stringify(data), (setErr: string) => {
|
||||||
|
if (setErr) {
|
||||||
|
Logger.warn(setErr);
|
||||||
|
} else {
|
||||||
|
Logger.debug("redis: video information cache set for: " + videoID);
|
||||||
|
}
|
||||||
|
callback(false, data); // don't fail
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(false, data); // don't fail
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(ytErr, data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Logger.debug("redis: fetched video information from cache: " + videoID);
|
||||||
|
callback(getErr, JSON.parse(result));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
_youtubeApi as YouTubeAPI
|
||||||
|
}
|
||||||
41
test.js
41
test.js
@@ -1,41 +0,0 @@
|
|||||||
var Mocha = require('mocha'),
|
|
||||||
fs = require('fs'),
|
|
||||||
path = require('path');
|
|
||||||
|
|
||||||
var config = require('./src/config.js');
|
|
||||||
// delete old test database
|
|
||||||
if (fs.existsSync(config.db)) fs.unlinkSync(config.db);
|
|
||||||
if (fs.existsSync(config.privateDB)) fs.unlinkSync(config.privateDB);
|
|
||||||
|
|
||||||
var createServer = require('./src/app.js');
|
|
||||||
var createMockServer = require('./test/mocks.js');
|
|
||||||
const logger = require('./src/utils/logger.js');
|
|
||||||
|
|
||||||
// Instantiate a Mocha instance.
|
|
||||||
var mocha = new Mocha();
|
|
||||||
|
|
||||||
var testDir = './test/cases'
|
|
||||||
|
|
||||||
// Add each .js file to the mocha instance
|
|
||||||
fs.readdirSync(testDir).filter(function(file) {
|
|
||||||
// Only keep the .js files
|
|
||||||
return file.substr(-3) === '.js';
|
|
||||||
|
|
||||||
}).forEach(function(file) {
|
|
||||||
mocha.addFile(
|
|
||||||
path.join(testDir, file)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
var mockServer = createMockServer(() => {
|
|
||||||
logger.info("Started mock HTTP Server");
|
|
||||||
var server = createServer(() => {
|
|
||||||
logger.info("Started main HTTP server");
|
|
||||||
// Run the tests.
|
|
||||||
mocha.run((failures) => {
|
|
||||||
mockServer.close();
|
|
||||||
server.close();
|
|
||||||
process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
44
test.ts
Normal file
44
test.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import Mocha from 'mocha';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import {config} from './src/config';
|
||||||
|
import {createServer} from './src/app';
|
||||||
|
import {createMockServer} from './test/mocks';
|
||||||
|
import {Logger} from './src/utils/logger';
|
||||||
|
import {initDb} from './src/databases/databases';
|
||||||
|
|
||||||
|
// delete old test database
|
||||||
|
if (fs.existsSync(config.db)) fs.unlinkSync(config.db)
|
||||||
|
if (fs.existsSync(config.privateDB)) fs.unlinkSync(config.privateDB);
|
||||||
|
|
||||||
|
initDb();
|
||||||
|
|
||||||
|
// Instantiate a Mocha instance.
|
||||||
|
const mocha = new Mocha();
|
||||||
|
|
||||||
|
const testDir = './test/cases';
|
||||||
|
|
||||||
|
// Add each .ts file to the mocha instance
|
||||||
|
fs.readdirSync(testDir)
|
||||||
|
.filter(function(file) {
|
||||||
|
// Only keep the .ts files
|
||||||
|
return file.substr(-3) === '.ts';
|
||||||
|
})
|
||||||
|
.forEach(function(file) {
|
||||||
|
mocha.addFile(
|
||||||
|
path.join(testDir, file)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockServer = createMockServer(() => {
|
||||||
|
Logger.info("Started mock HTTP Server");
|
||||||
|
const server = createServer(() => {
|
||||||
|
Logger.info("Started main HTTP server");
|
||||||
|
// Run the tests.
|
||||||
|
mocha.run((failures) => {
|
||||||
|
mockServer.close();
|
||||||
|
server.close();
|
||||||
|
process.exitCode = failures ? 1 : 0; // exit with non-zero status if there were failures
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
const databases = require('../../src/databases/databases.js');
|
|
||||||
const db = databases.db;
|
|
||||||
const privateDB = databases.privateDB;
|
|
||||||
|
|
||||||
describe('dbUpgrade', () => {
|
|
||||||
it('Should update the database version when starting the application', (done) => {
|
|
||||||
let dbVersion = db.prepare('get', 'SELECT key, value FROM config where key = ?', ['version']).value;
|
|
||||||
let privateVersion = privateDB.prepare('get', 'SELECT key, value FROM config where key = ?', ['version']).value;
|
|
||||||
if (dbVersion >= 1 && privateVersion >= 1) done();
|
|
||||||
else done('Versions are not at least 1. db is ' + dbVersion + ', private is ' + privateVersion);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
11
test/cases/dbUpgrade.ts
Normal file
11
test/cases/dbUpgrade.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import {db, privateDB} from '../../src/databases/databases';
|
||||||
|
import {Done} from '../utils';
|
||||||
|
|
||||||
|
describe('dbUpgrade', () => {
|
||||||
|
it('Should update the database version when starting the application', (done: Done) => {
|
||||||
|
let dbVersion = db.prepare('get', 'SELECT key, value FROM config where key = ?', ['version']).value;
|
||||||
|
let privateVersion = privateDB.prepare('get', 'SELECT key, value FROM config where key = ?', ['version']).value;
|
||||||
|
if (dbVersion >= 1 && privateVersion >= 1) done();
|
||||||
|
else done('Versions are not at least 1. db is ' + dbVersion + ', private is ' + privateVersion);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
var getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
var assert = require('assert');
|
|
||||||
describe('getHash', () => {
|
|
||||||
it('Should not output the input string', () => {
|
|
||||||
assert(getHash("test") !== "test");
|
|
||||||
assert(getHash("test", -1) !== "test");
|
|
||||||
assert(getHash("test", 0) !== "test");
|
|
||||||
assert(getHash("test", null) !== "test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return a hashed value', () => {
|
|
||||||
assert.equal(getHash("test"), "2f327ef967ade1ebf4319163f7debbda9cc17bb0c8c834b00b30ca1cf1c256ee");
|
|
||||||
});
|
|
||||||
|
|
||||||
it ('Should be able to output the same has the DB upgrade script will output', () => {
|
|
||||||
assert.equal(getHash("vid", 1), "1ff838dc6ca9680d88455341118157d59a055fe6d0e3870f9c002847bebe4663");
|
|
||||||
});
|
|
||||||
|
|
||||||
it ('Should take a variable number of passes', () => {
|
|
||||||
assert.equal(getHash("test", 1), "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08");
|
|
||||||
assert.equal(getHash("test", 2), "7b3d979ca8330a94fa7e9e1b466d8b99e0bcdea1ec90596c0dcc8d7ef6b4300c");
|
|
||||||
assert.equal(getHash("test", 3), "5b24f7aa99f1e1da5698a4f91ae0f4b45651a1b625c61ed669dd25ff5b937972");
|
|
||||||
});
|
|
||||||
|
|
||||||
it ('Should default to 5000 passes', () => {
|
|
||||||
assert.equal(getHash("test"), getHash("test", 5000));
|
|
||||||
});
|
|
||||||
|
|
||||||
it ('Should not take a negative number of passes', () => {
|
|
||||||
assert.equal(getHash("test", -1), "");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
33
test/cases/getHash.ts
Normal file
33
test/cases/getHash.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import {getHash} from '../../src/utils/getHash';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
describe('getHash', () => {
|
||||||
|
it('Should not output the input string', () => {
|
||||||
|
assert(getHash("test") !== "test");
|
||||||
|
assert(getHash("test", -1) !== "test");
|
||||||
|
assert(getHash("test", 0) !== "test");
|
||||||
|
assert(getHash("test", null) !== "test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return a hashed value', () => {
|
||||||
|
assert.strictEqual(getHash("test"), "2f327ef967ade1ebf4319163f7debbda9cc17bb0c8c834b00b30ca1cf1c256ee");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to output the same has the DB upgrade script will output', () => {
|
||||||
|
assert.strictEqual(getHash("vid", 1), "1ff838dc6ca9680d88455341118157d59a055fe6d0e3870f9c002847bebe4663");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should take a variable number of passes', () => {
|
||||||
|
assert.strictEqual(getHash("test", 1), "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08");
|
||||||
|
assert.strictEqual(getHash("test", 2), "7b3d979ca8330a94fa7e9e1b466d8b99e0bcdea1ec90596c0dcc8d7ef6b4300c");
|
||||||
|
assert.strictEqual(getHash("test", 3), "5b24f7aa99f1e1da5698a4f91ae0f4b45651a1b625c61ed669dd25ff5b937972");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should default to 5000 passes', () => {
|
||||||
|
assert.strictEqual(getHash("test"), getHash("test", 5000));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not take a negative number of passes', () => {
|
||||||
|
assert.strictEqual(getHash("test", -1), "");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
var request = require('request');
|
|
||||||
var utils = require('../utils.js');
|
|
||||||
var db = require('../../src/databases/databases.js').db;
|
|
||||||
var getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
describe('getIsUserVIP', () => {
|
|
||||||
before(() => {
|
|
||||||
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("supertestman") + "')");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a 200', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/isUserVIP?userID=supertestman", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
|
||||||
else done(); // pass
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('Should get a 400 if no userID', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/isUserVIP", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 400) done("non 400: " + res.statusCode);
|
|
||||||
else done(); // pass
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should say a VIP is a VIP', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/isUserVIP?userID=supertestman", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
if (JSON.parse(body).vip === true) done(); // pass
|
|
||||||
else done("Result was non-vip when should have been vip");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should say a normal user is not a VIP', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/isUserVIP?userID=regulartestman", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
if (JSON.parse(body).vip === false) done(); // pass
|
|
||||||
else done("Result was vip when should have been non-vip");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
57
test/cases/getIsUserVIP.ts
Normal file
57
test/cases/getIsUserVIP.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import request from 'request';
|
||||||
|
import {getbaseURL, Done} from '../utils';
|
||||||
|
import {db} from '../../src/databases/databases';
|
||||||
|
import {getHash} from '../../src/utils/getHash';
|
||||||
|
|
||||||
|
describe('getIsUserVIP', () => {
|
||||||
|
before(() => {
|
||||||
|
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("supertestman") + "')");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a 200', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/isUserVIP?userID=supertestman", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
||||||
|
else done(); // pass
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('Should get a 400 if no userID', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/isUserVIP", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 400) done("non 400: " + res.statusCode);
|
||||||
|
else done(); // pass
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should say a VIP is a VIP', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/isUserVIP?userID=supertestman", null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
if (JSON.parse(body).vip === true) done(); // pass
|
||||||
|
else done("Result was non-vip when should have been vip");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should say a normal user is not a VIP', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/isUserVIP?userID=regulartestman", null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
if (JSON.parse(body).vip === false) done(); // pass
|
||||||
|
else done("Result was vip when should have been non-vip");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
var request = require('request');
|
|
||||||
var utils = require('../utils.js');
|
|
||||||
var db = require('../../src/databases/databases.js').db;
|
|
||||||
var getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
describe('getSavedTimeForUser', () => {
|
|
||||||
before(() => {
|
|
||||||
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES";
|
|
||||||
db.exec(startOfQuery + "('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0, '" + getHash('getSavedTimeForUser', 0) + "')");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a 200', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/getSavedTimeForUser?userID=testman", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200");
|
|
||||||
else done(); // pass
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
21
test/cases/getSavedTimeForUser.ts
Normal file
21
test/cases/getSavedTimeForUser.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import request from 'request';
|
||||||
|
import {Done, getbaseURL} from '../utils';
|
||||||
|
import {db} from '../../src/databases/databases';
|
||||||
|
import {getHash} from '../../src/utils/getHash';
|
||||||
|
|
||||||
|
describe('getSavedTimeForUser', () => {
|
||||||
|
before(() => {
|
||||||
|
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES";
|
||||||
|
db.exec(startOfQuery + "('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0, '" + getHash('getSavedTimeForUser', 0) + "')");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a 200', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/getSavedTimeForUser?userID=testman", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200");
|
||||||
|
else done(); // pass
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
var request = require('request');
|
|
||||||
var db = require('../../src/databases/databases.js').db;
|
|
||||||
var utils = require('../utils.js');
|
|
||||||
var getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
describe('getSegmentsByHash', () => {
|
|
||||||
before(() => {
|
|
||||||
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES";
|
|
||||||
db.exec(startOfQuery + "('getSegmentsByHash-0', 1, 10, 2, 'getSegmentsByHash-0-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('getSegmentsByHash-0', 1) + "')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910
|
|
||||||
db.exec(startOfQuery + "('getSegmentsByHash-0', 20, 30, 2, 'getSegmentsByHash-0-1', 'testman', 100, 150, 'intro', 0, '" + getHash('getSegmentsByHash-0', 1) + "')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910
|
|
||||||
db.exec(startOfQuery + "('getSegmentsByHash-noMatchHash', 40, 50, 2, 'getSegmentsByHash-noMatchHash', 'testman', 0, 50, 'sponsor', 0, 'fdaffnoMatchHash')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910
|
|
||||||
db.exec(startOfQuery + "('getSegmentsByHash-1', 60, 70, 2, 'getSegmentsByHash-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('getSegmentsByHash-1', 1) + "')"); // hash = 3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a 200', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/3272f?categories=["sponsor", "intro"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
done();
|
|
||||||
} // pass
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a 200 with empty segments for video but no matching categories', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/3272f?categories=["shilling"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
if (JSON.parse(body) && JSON.parse(body).length > 0 && JSON.parse(body)[0].segments.length === 0) {
|
|
||||||
done(); // pass
|
|
||||||
} else {
|
|
||||||
done("response had segments");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get an empty array if no videos', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/11111?categories=["shilling"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 404) done("non 404 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
if (JSON.parse(body).length === 0 && body === '[]') done(); // pass
|
|
||||||
else done("non empty array returned");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 prefix too short', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/11?categories=["shilling"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 400) done("non 400 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 prefix too long', (done) => {
|
|
||||||
let prefix = new Array(50).join('1');
|
|
||||||
if (prefix.length <= 32) { // default value, config can change this
|
|
||||||
done('failed to generate a long enough string for the test ' + prefix.length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/'+prefix+'?categories=["shilling"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 400) done("non 400 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not return 400 prefix in range', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/11111?categories=["shilling"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode === 400) done("prefix length 5 gave 400 " + res.statusCode);
|
|
||||||
else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 404 for no hash', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/?categories=["shilling"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 404) done("expected 404, got " + res.statusCode);
|
|
||||||
else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 500 for bad format categories', (done) => { // should probably be 400
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/?categories=shilling', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 500) done("expected 500 got " + res.statusCode);
|
|
||||||
else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get multiple videos', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/fdaf?categories=["sponsor","intro"]', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
body = JSON.parse(body);
|
|
||||||
if (body.length !== 2) done("expected 2 video, got " + body.length);
|
|
||||||
else if (body[0].segments.length !== 2) done("expected 2 segments for first video, got " + body[0].segments.length);
|
|
||||||
else if (body[1].segments.length !== 1) done("expected 1 segment for second video, got " + body[1].segments.length);
|
|
||||||
else done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get 200 for no categories (default sponsor)', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/fdaf', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
body = JSON.parse(body);
|
|
||||||
if (body.length !== 2) done("expected 2 videos, got " + body.length);
|
|
||||||
else if (body[0].segments.length !== 1) done("expected 1 segments for first video, got " + body[0].segments.length);
|
|
||||||
else if (body[1].segments.length !== 1) done("expected 1 segments for second video, got " + body[1].segments.length);
|
|
||||||
else if (body[0].segments[0].category !== 'sponsor' || body[1].segments[0].category !== 'sponsor') done("both segments are not sponsor");
|
|
||||||
else done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to post a segment and get it using endpoint', (done) => {
|
|
||||||
let testID = 'abc123goodVideo';
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/postVideoSponsorTimes", {
|
|
||||||
json: {
|
|
||||||
userID: "test",
|
|
||||||
videoID: testID,
|
|
||||||
segments: [{
|
|
||||||
segment: [13, 17],
|
|
||||||
category: "sponsor"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done('(post) ' + err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/skipSegments/'+getHash(testID, 1).substring(0,3), null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("(get) Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("(get) non 200 status code, was " + res.statusCode);
|
|
||||||
else {
|
|
||||||
body = JSON.parse(body);
|
|
||||||
if (body.length !== 1) done("(get) expected 1 video, got " + body.length);
|
|
||||||
else if (body[0].segments.length !== 1) done("(get) expected 1 segments for first video, got " + body[0].segments.length);
|
|
||||||
else if (body[0].segments[0].category !== 'sponsor') done("(get) segment should be sponsor, was "+body[0].segments[0].category);
|
|
||||||
else done();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
done("(post) non 200 status code, was " + res.statusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
192
test/cases/getSegmentsByHash.ts
Normal file
192
test/cases/getSegmentsByHash.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import request from 'request';
|
||||||
|
import {db} from '../../src/databases/databases';
|
||||||
|
import {Done, getbaseURL} from '../utils';
|
||||||
|
import {getHash} from '../../src/utils/getHash';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
describe('getSegmentsByHash', () => {
|
||||||
|
before(() => {
|
||||||
|
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES";
|
||||||
|
db.exec(startOfQuery + "('getSegmentsByHash-0', 1, 10, 2, 'getSegmentsByHash-0-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('getSegmentsByHash-0', 1) + "')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910
|
||||||
|
db.exec(startOfQuery + "('getSegmentsByHash-0', 20, 30, 2, 'getSegmentsByHash-0-1', 'testman', 100, 150, 'intro', 0, '" + getHash('getSegmentsByHash-0', 1) + "')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910
|
||||||
|
db.exec(startOfQuery + "('getSegmentsByHash-noMatchHash', 40, 50, 2, 'getSegmentsByHash-noMatchHash', 'testman', 0, 50, 'sponsor', 0, 'fdaffnoMatchHash')"); // hash = fdaff4dee1043451faa7398324fb63d8618ebcd11bddfe0491c488db12c6c910
|
||||||
|
db.exec(startOfQuery + "('getSegmentsByHash-1', 60, 70, 2, 'getSegmentsByHash-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('getSegmentsByHash-1', 1) + "')"); // hash = 3272fa85ee0927f6073ef6f07ad5f3146047c1abba794cfa364d65ab9921692b
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a 200', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/3272f?categories=["sponsor", "intro"]', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
done();
|
||||||
|
} // pass
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a 200 with empty segments for video but no matching categories', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/3272f?categories=["shilling"]', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
if (JSON.parse(body) && JSON.parse(body).length > 0 && JSON.parse(body)[0].segments.length === 0) {
|
||||||
|
done(); // pass
|
||||||
|
} else {
|
||||||
|
done("response had segments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get an empty array if no videos', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/11111?categories=["shilling"]', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 404) done("non 404 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
if (JSON.parse(body).length === 0 && body === '[]') done(); // pass
|
||||||
|
else done("non empty array returned");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return 400 prefix too short', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/11?categories=["shilling"]', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 400) done("non 400 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return 400 prefix too long', (done: Done) => {
|
||||||
|
let prefix = new Array(50).join('1');
|
||||||
|
if (prefix.length <= 32) { // default value, config can change this
|
||||||
|
done('failed to generate a long enough string for the test ' + prefix.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/' + prefix + '?categories=["shilling"]', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 400) done("non 400 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not return 400 prefix in range', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/11111?categories=["shilling"]', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode === 400) done("prefix length 5 gave 400 " + res.statusCode);
|
||||||
|
else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return 404 for no hash', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/?categories=["shilling"]', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 404) done("expected 404, got " + res.statusCode);
|
||||||
|
else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return 500 for bad format categories', (done: Done) => { // should probably be 400
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/?categories=shilling', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 500) done("expected 500 got " + res.statusCode);
|
||||||
|
else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get multiple videos', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/fdaf?categories=["sponsor","intro"]', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
body = JSON.parse(body);
|
||||||
|
if (body.length !== 2) done("expected 2 video, got " + body.length);
|
||||||
|
else if (body[0].segments.length !== 2) done("expected 2 segments for first video, got " + body[0].segments.length);
|
||||||
|
else if (body[1].segments.length !== 1) done("expected 1 segment for second video, got " + body[1].segments.length);
|
||||||
|
else done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get 200 for no categories (default sponsor)', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/fdaf', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("non 200 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
body = JSON.parse(body);
|
||||||
|
if (body.length !== 2) done("expected 2 videos, got " + body.length);
|
||||||
|
else if (body[0].segments.length !== 1) done("expected 1 segments for first video, got " + body[0].segments.length);
|
||||||
|
else if (body[1].segments.length !== 1) done("expected 1 segments for second video, got " + body[1].segments.length);
|
||||||
|
else if (body[0].segments[0].category !== 'sponsor' || body[1].segments[0].category !== 'sponsor') done("both segments are not sponsor");
|
||||||
|
else done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to post a segment and get it using endpoint', (done: Done) => {
|
||||||
|
let testID = 'abc123goodVideo';
|
||||||
|
request.post(getbaseURL()
|
||||||
|
+ "/api/postVideoSponsorTimes", {
|
||||||
|
json: {
|
||||||
|
userID: "test",
|
||||||
|
videoID: testID,
|
||||||
|
segments: [{
|
||||||
|
segment: [13, 17],
|
||||||
|
category: "sponsor",
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done('(post) ' + err);
|
||||||
|
else if (res.statusCode === 200) {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/skipSegments/' + getHash(testID, 1).substring(0, 3), null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("(get) Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("(get) non 200 status code, was " + res.statusCode);
|
||||||
|
else {
|
||||||
|
body = JSON.parse(body);
|
||||||
|
if (body.length !== 1) done("(get) expected 1 video, got " + body.length);
|
||||||
|
else if (body[0].segments.length !== 1) done("(get) expected 1 segments for first video, got " + body[0].segments.length);
|
||||||
|
else if (body[0].segments[0].category !== 'sponsor') done("(get) segment should be sponsor, was " + body[0].segments[0].category);
|
||||||
|
else done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done("(post) non 200 status code, was " + res.statusCode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
var request = require('request');
|
|
||||||
var db = require('../../src/databases/databases.js').db;
|
|
||||||
var utils = require('../utils.js');
|
|
||||||
var getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
|
|
||||||
describe('getSkipSegments', () => {
|
|
||||||
before(() => {
|
|
||||||
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES";
|
|
||||||
db.exec(startOfQuery + "('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('testtesttest', 0) + "')");
|
|
||||||
db.exec(startOfQuery + "('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0, '" + getHash('testtesttest', 0) + "')");
|
|
||||||
db.exec(startOfQuery + "('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('testtesttest,test', 0) + "')");
|
|
||||||
db.exec(startOfQuery + "('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0, '" + getHash('test3', 0) + "')");
|
|
||||||
db.exec(startOfQuery + "('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0, '" + getHash('test3', 0) + "')");
|
|
||||||
db.exec(startOfQuery + "('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0, '" + getHash('multiple', 0) + "')");
|
|
||||||
db.exec(startOfQuery + "('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0, '" + getHash('multiple', 0) + "')");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('Should be able to get a time by category 1', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest&category=sponsor", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
|
||||||
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a time by category 2', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest&category=intro", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 20 && data[0].segment[1] === 33
|
|
||||||
&& data[0].category === "intro" && data[0].UUID === "1-uuid-2") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a time by categories array', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest&categories=[\"sponsor\"]", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
|
||||||
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a time by categories array 2', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest&categories=[\"intro\"]", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 20 && data[0].segment[1] === 33
|
|
||||||
&& data[0].category === "intro" && data[0].UUID === "1-uuid-2") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get multiple times by category', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=multiple&categories=[\"intro\"]", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 2) {
|
|
||||||
|
|
||||||
let success = true;
|
|
||||||
for (const segment of data) {
|
|
||||||
if ((segment.segment[0] !== 20 || segment.segment[1] !== 33
|
|
||||||
|| segment.category !== "intro" || segment.UUID !== "1-uuid-7") &&
|
|
||||||
(segment.segment[0] !== 1 || segment.segment[1] !== 11
|
|
||||||
|| segment.category !== "intro" || segment.UUID !== "1-uuid-6")) {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) done();
|
|
||||||
else done("Received incorrect body: " + res.body);
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get multiple times by multiple categories', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest&categories=[\"sponsor\", \"intro\"]", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 2) {
|
|
||||||
|
|
||||||
let success = true;
|
|
||||||
for (const segment of data) {
|
|
||||||
if ((segment.segment[0] !== 20 || segment.segment[1] !== 33
|
|
||||||
|| segment.category !== "intro" || segment.UUID !== "1-uuid-2") &&
|
|
||||||
(segment.segment[0] !== 1 || segment.segment[1] !== 11
|
|
||||||
|| segment.category !== "sponsor" || segment.UUID !== "1-uuid-0")) {
|
|
||||||
success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) done();
|
|
||||||
else done("Received incorrect body: " + res.body);
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be possible to send unexpected query parameters', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest&fakeparam=hello&category=sponsor", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
|
||||||
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Low voted submissions should be hidden', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=test3&category=sponsor", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
|
||||||
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-4") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 404 if no segment found', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=notarealvideo", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 404) done("non 404 respone code: " + res.statusCode);
|
|
||||||
else done(); // pass
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('Should be able send a comma in a query param', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ "/api/skipSegments?videoID=testtesttest,test&category=sponsor", null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done("Couldn't call endpoint");
|
|
||||||
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
|
||||||
else {
|
|
||||||
let data = JSON.parse(res.body);
|
|
||||||
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
|
||||||
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-1") {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Received incorrect body: " + res.body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
216
test/cases/getSkipSegments.ts
Normal file
216
test/cases/getSkipSegments.ts
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
import request from 'request';
|
||||||
|
import {db} from '../../src/databases/databases';
|
||||||
|
import {Done, getbaseURL} from '../utils';
|
||||||
|
import {getHash} from '../../src/utils/getHash';
|
||||||
|
|
||||||
|
describe('getSkipSegments', () => {
|
||||||
|
before(() => {
|
||||||
|
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID) VALUES";
|
||||||
|
db.exec(startOfQuery + "('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0, '" + getHash('testtesttest', 0) + "')");
|
||||||
|
db.exec(startOfQuery + "('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0, '" + getHash('testtesttest', 0) + "')");
|
||||||
|
db.exec(startOfQuery + "('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0, '" + getHash('testtesttest,test', 0) + "')");
|
||||||
|
db.exec(startOfQuery + "('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0, '" + getHash('test3', 0) + "')");
|
||||||
|
db.exec(startOfQuery + "('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0, '" + getHash('test3', 0) + "')");
|
||||||
|
db.exec(startOfQuery + "('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0, '" + getHash('multiple', 0) + "')");
|
||||||
|
db.exec(startOfQuery + "('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0, '" + getHash('multiple', 0) + "')");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('Should be able to get a time by category 1', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest&category=sponsor", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
||||||
|
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a time by category 2', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest&category=intro", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 20 && data[0].segment[1] === 33
|
||||||
|
&& data[0].category === "intro" && data[0].UUID === "1-uuid-2") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a time by categories array', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest&categories=[\"sponsor\"]", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
||||||
|
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a time by categories array 2', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest&categories=[\"intro\"]", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 20 && data[0].segment[1] === 33
|
||||||
|
&& data[0].category === "intro" && data[0].UUID === "1-uuid-2") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get multiple times by category', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=multiple&categories=[\"intro\"]", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 2) {
|
||||||
|
|
||||||
|
let success = true;
|
||||||
|
for (const segment of data) {
|
||||||
|
if ((segment.segment[0] !== 20 || segment.segment[1] !== 33
|
||||||
|
|| segment.category !== "intro" || segment.UUID !== "1-uuid-7") &&
|
||||||
|
(segment.segment[0] !== 1 || segment.segment[1] !== 11
|
||||||
|
|| segment.category !== "intro" || segment.UUID !== "1-uuid-6")) {
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) done();
|
||||||
|
else done("Received incorrect body: " + res.body);
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get multiple times by multiple categories', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest&categories=[\"sponsor\", \"intro\"]", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 2) {
|
||||||
|
|
||||||
|
let success = true;
|
||||||
|
for (const segment of data) {
|
||||||
|
if ((segment.segment[0] !== 20 || segment.segment[1] !== 33
|
||||||
|
|| segment.category !== "intro" || segment.UUID !== "1-uuid-2") &&
|
||||||
|
(segment.segment[0] !== 1 || segment.segment[1] !== 11
|
||||||
|
|| segment.category !== "sponsor" || segment.UUID !== "1-uuid-0")) {
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success) done();
|
||||||
|
else done("Received incorrect body: " + res.body);
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be possible to send unexpected query parameters', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest&fakeparam=hello&category=sponsor", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
||||||
|
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Low voted submissions should be hidden', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=test3&category=sponsor", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
||||||
|
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-4") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return 404 if no segment found', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=notarealvideo", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 404) done("non 404 respone code: " + res.statusCode);
|
||||||
|
else done(); // pass
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('Should be able send a comma in a query param', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ "/api/skipSegments?videoID=testtesttest,test&category=sponsor", null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode !== 200) done("Status code was: " + res.statusCode);
|
||||||
|
else {
|
||||||
|
let data = JSON.parse(res.body);
|
||||||
|
if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11
|
||||||
|
&& data[0].category === "sponsor" && data[0].UUID === "1-uuid-1") {
|
||||||
|
done();
|
||||||
|
} else {
|
||||||
|
done("Received incorrect body: " + res.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const getSubmissionUUID = require('../../src/utils/getSubmissionUUID.js');
|
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
describe('getSubmissionUUID', () => {
|
|
||||||
it('Should return the hashed value', () => {
|
|
||||||
assert.equal(getSubmissionUUID('video001', 'sponsor', 'testuser001', 13.33337, 42.000001), '1d33d7016aa6482849019bd906d75c08fe6b815e64e823146df35f66c35612dd');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
8
test/cases/getSubmissionUUID.ts
Normal file
8
test/cases/getSubmissionUUID.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import {getSubmissionUUID} from '../../src/utils/getSubmissionUUID';
|
||||||
|
import assert from 'assert';
|
||||||
|
|
||||||
|
describe('getSubmissionUUID', () => {
|
||||||
|
it('Should return the hashed value', () => {
|
||||||
|
assert.strictEqual(getSubmissionUUID('video001', 'sponsor', 'testuser001', 13.33337, 42.000001), '1d33d7016aa6482849019bd906d75c08fe6b815e64e823146df35f66c35612dd');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
var request = require('request');
|
|
||||||
var utils = require('../utils.js');
|
|
||||||
var db = require('../../src/databases/databases.js').db;
|
|
||||||
var getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
describe('getUserInfo', () => {
|
|
||||||
before(() => {
|
|
||||||
let startOfUserNamesQuery = "INSERT INTO userNames (userID, userName) VALUES";
|
|
||||||
db.exec(startOfUserNamesQuery + "('" + getHash("getuserinfo_user_01") + "', 'Username user 01')");
|
|
||||||
let startOfSponsorTimesQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES";
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000001', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 0)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000002', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 0)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('yyyxxxzzz', 1, 11, -1, 'uuid000003', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 0)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('yyyxxxzzz', 1, 11, -2, 'uuid000004', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 1)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('xzzzxxyyy', 1, 11, -5, 'uuid000005', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 1)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('zzzxxxyyy', 1, 11, 2, 'uuid000006', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 0)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000007', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 1)");
|
|
||||||
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000008', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 1)");
|
|
||||||
|
|
||||||
|
|
||||||
db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_0') + "', 10, 'getuserinfo_vip')");
|
|
||||||
db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip')");
|
|
||||||
db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip')");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a 200', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo?userID=getuserinfo_user_01', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done('couldn\'t call endpoint');
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
done('non 200 (' + res.statusCode + ')');
|
|
||||||
} else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to get a 400 (No userID parameter)', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done('couldn\'t call endpoint');
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 400) {
|
|
||||||
done('non 400');
|
|
||||||
} else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return info', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo?userID=getuserinfo_user_01', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done("couldn't call endpoint");
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
done("non 200");
|
|
||||||
} else {
|
|
||||||
const data = JSON.parse(body);
|
|
||||||
if (data.userName !== 'Username user 01') {
|
|
||||||
done('Returned incorrect userName "' + data.userName + '"');
|
|
||||||
} else if (data.minutesSaved !== 5) {
|
|
||||||
done('Returned incorrect minutesSaved "' + data.minutesSaved + '"');
|
|
||||||
} else if (data.viewCount !== 30) {
|
|
||||||
done('Returned incorrect viewCount "' + data.viewCount + '"');
|
|
||||||
} else if (data.segmentCount !== 3) {
|
|
||||||
done('Returned incorrect segmentCount "' + data.segmentCount + '"');
|
|
||||||
} else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should get warning data', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo?userID=getuserinfo_warning_0', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done("couldn't call endpoint");
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
done("non 200");
|
|
||||||
} else {
|
|
||||||
const data = JSON.parse(body);
|
|
||||||
if (data.warnings !== 1) {
|
|
||||||
done('wrong number of warnings: ' + data.warnings + ', not ' + 1);
|
|
||||||
} else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should get multiple warnings', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo?userID=getuserinfo_warning_1', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done("couldn't call endpoint");
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
done("non 200");
|
|
||||||
} else {
|
|
||||||
const data = JSON.parse(body);
|
|
||||||
if (data.warnings !== 2) {
|
|
||||||
done('wrong number of warnings: ' + data.warnings + ', not ' + 2);
|
|
||||||
} else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not get warnings if noe', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo?userID=getuserinfo_warning_2', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done("couldn't call endpoint");
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
done("non 200");
|
|
||||||
} else {
|
|
||||||
const data = JSON.parse(body);
|
|
||||||
if (data.warnings !== 0) {
|
|
||||||
done('wrong number of warnings: ' + data.warnings + ', not ' + 0);
|
|
||||||
} else {
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return userID for userName (No userName set)', (done) => {
|
|
||||||
request.get(utils.getbaseURL()
|
|
||||||
+ '/api/getUserInfo?userID=getuserinfo_user_02', null,
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) {
|
|
||||||
done('couldn\'t call endpoint');
|
|
||||||
} else {
|
|
||||||
if (res.statusCode !== 200) {
|
|
||||||
done('non 200');
|
|
||||||
} else {
|
|
||||||
const data = JSON.parse(body);
|
|
||||||
if (data.userName !== 'c2a28fd225e88f74945794ae85aef96001d4a1aaa1022c656f0dd48ac0a3ea0f') {
|
|
||||||
return done('Did not return userID for userName');
|
|
||||||
}
|
|
||||||
done(); // pass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
167
test/cases/getUserInfo.ts
Normal file
167
test/cases/getUserInfo.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import request from 'request';
|
||||||
|
import {Done, getbaseURL} from '../utils';
|
||||||
|
import {db} from '../../src/databases/databases';
|
||||||
|
import {getHash} from '../../src/utils/getHash';
|
||||||
|
|
||||||
|
describe('getUserInfo', () => {
|
||||||
|
before(() => {
|
||||||
|
let startOfUserNamesQuery = "INSERT INTO userNames (userID, userName) VALUES";
|
||||||
|
db.exec(startOfUserNamesQuery + "('" + getHash("getuserinfo_user_01") + "', 'Username user 01')");
|
||||||
|
let startOfSponsorTimesQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES";
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000001', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 0)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000002', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 0)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('yyyxxxzzz', 1, 11, -1, 'uuid000003', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 0)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('yyyxxxzzz', 1, 11, -2, 'uuid000004', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 1)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('xzzzxxyyy', 1, 11, -5, 'uuid000005', '" + getHash("getuserinfo_user_01") + "', 0, 10, 'sponsor', 1)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('zzzxxxyyy', 1, 11, 2, 'uuid000006', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 0)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000007', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 1)");
|
||||||
|
db.exec(startOfSponsorTimesQuery + "('xxxyyyzzz', 1, 11, 2, 'uuid000008', '" + getHash("getuserinfo_user_02") + "', 0, 10, 'sponsor', 1)");
|
||||||
|
|
||||||
|
|
||||||
|
db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_0') + "', 10, 'getuserinfo_vip')");
|
||||||
|
db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip')");
|
||||||
|
db.exec("INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES ('" + getHash('getuserinfo_warning_1') + "', 10, 'getuserinfo_vip')");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a 200', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo?userID=getuserinfo_user_01', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) {
|
||||||
|
done('couldn\'t call endpoint');
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
done('non 200 (' + res.statusCode + ')');
|
||||||
|
} else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to get a 400 (No userID parameter)', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo', null,
|
||||||
|
(err, res) => {
|
||||||
|
if (err) {
|
||||||
|
done('couldn\'t call endpoint');
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 400) {
|
||||||
|
done('non 400');
|
||||||
|
} else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return info', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo?userID=getuserinfo_user_01', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) {
|
||||||
|
done("couldn't call endpoint");
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
done("non 200");
|
||||||
|
} else {
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
if (data.userName !== 'Username user 01') {
|
||||||
|
done('Returned incorrect userName "' + data.userName + '"');
|
||||||
|
} else if (data.minutesSaved !== 5) {
|
||||||
|
done('Returned incorrect minutesSaved "' + data.minutesSaved + '"');
|
||||||
|
} else if (data.viewCount !== 30) {
|
||||||
|
done('Returned incorrect viewCount "' + data.viewCount + '"');
|
||||||
|
} else if (data.segmentCount !== 3) {
|
||||||
|
done('Returned incorrect segmentCount "' + data.segmentCount + '"');
|
||||||
|
} else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should get warning data', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo?userID=getuserinfo_warning_0', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) {
|
||||||
|
done("couldn't call endpoint");
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
done("non 200");
|
||||||
|
} else {
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
if (data.warnings !== 1) {
|
||||||
|
done('wrong number of warnings: ' + data.warnings + ', not ' + 1);
|
||||||
|
} else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should get multiple warnings', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo?userID=getuserinfo_warning_1', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) {
|
||||||
|
done("couldn't call endpoint");
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
done("non 200");
|
||||||
|
} else {
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
if (data.warnings !== 2) {
|
||||||
|
done('wrong number of warnings: ' + data.warnings + ', not ' + 2);
|
||||||
|
} else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not get warnings if noe', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo?userID=getuserinfo_warning_2', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) {
|
||||||
|
done("couldn't call endpoint");
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
done("non 200");
|
||||||
|
} else {
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
if (data.warnings !== 0) {
|
||||||
|
done('wrong number of warnings: ' + data.warnings + ', not ' + 0);
|
||||||
|
} else {
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return userID for userName (No userName set)', (done: Done) => {
|
||||||
|
request.get(getbaseURL()
|
||||||
|
+ '/api/getUserInfo?userID=getuserinfo_user_02', null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) {
|
||||||
|
done('couldn\'t call endpoint');
|
||||||
|
} else {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
done('non 200');
|
||||||
|
} else {
|
||||||
|
const data = JSON.parse(body);
|
||||||
|
if (data.userName !== 'c2a28fd225e88f74945794ae85aef96001d4a1aaa1022c656f0dd48ac0a3ea0f') {
|
||||||
|
return done('Did not return userID for userName');
|
||||||
|
}
|
||||||
|
done(); // pass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,466 +0,0 @@
|
|||||||
var request = require('request');
|
|
||||||
|
|
||||||
var utils = require('../utils.js');
|
|
||||||
const getHash = require('../../src/utils/getHash.js');
|
|
||||||
|
|
||||||
var databases = require('../../src/databases/databases.js');
|
|
||||||
const logger = require('../../src/utils/logger.js');
|
|
||||||
var db = databases.db;
|
|
||||||
|
|
||||||
describe('noSegmentRecords', () => {
|
|
||||||
before(() => {
|
|
||||||
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser-noSegments") + "')");
|
|
||||||
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id', 'sponsor')");
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id', 'intro')");
|
|
||||||
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id-1', 'sponsor')");
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id-1', 'intro')");
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'noSubmitVideo', 'sponsor')");
|
|
||||||
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'delete-record', 'sponsor')");
|
|
||||||
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'delete-record-1', 'sponsor')");
|
|
||||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'delete-record-1', 'intro')");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should update the database version when starting the application', (done) => {
|
|
||||||
let version = db.prepare('get', 'SELECT key, value FROM config where key = ?', ['version']).value;
|
|
||||||
if (version > 1) done();
|
|
||||||
else done('Version isn\'t greater than 1. Version is ' + version);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to submit categories not in video (http response)', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'no-segments-video-id',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'outro',
|
|
||||||
'shilling',
|
|
||||||
'shilling',
|
|
||||||
'shil ling',
|
|
||||||
'',
|
|
||||||
'intro'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
let expected = {
|
|
||||||
submitted: [
|
|
||||||
'outro',
|
|
||||||
'shilling'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
if (JSON.stringify(body) === JSON.stringify(expected)) {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Incorrect response: expected " + JSON.stringify(expected) + " got " + JSON.stringify(body));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(body);
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to submit categories not in video (sql check)', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'no-segments-video-id-1',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'outro',
|
|
||||||
'shilling',
|
|
||||||
'shilling',
|
|
||||||
'shil ling',
|
|
||||||
'',
|
|
||||||
'intro'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['no-segments-video-id-1']);
|
|
||||||
if (result.length !== 4) {
|
|
||||||
console.log(result);
|
|
||||||
done("Expected 4 entrys in db, got " + result.length);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(body);
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to submit categories with _ in the category', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'underscore',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'word_word',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['underscore']);
|
|
||||||
if (result.length !== 1) {
|
|
||||||
console.log(result);
|
|
||||||
done("Expected 1 entrys in db, got " + result.length);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(body);
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to submit categories with upper and lower case in the category', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'bothCases',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'wordWord',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['bothCases']);
|
|
||||||
if (result.length !== 1) {
|
|
||||||
console.log(result);
|
|
||||||
done("Expected 1 entrys in db, got " + result.length);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(body);
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not be able to submit categories with $ in the category', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'specialChar',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'word&word',
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['specialChar']);
|
|
||||||
if (result.length !== 0) {
|
|
||||||
console.log(result);
|
|
||||||
done("Expected 0 entrys in db, got " + result.length);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(body);
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 for missing params', (done) => {
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json: {}},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 400) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 for no categories', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'test',
|
|
||||||
userID: 'test',
|
|
||||||
categories: []
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 400) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 for no userID', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'test',
|
|
||||||
userID: null,
|
|
||||||
categories: ['sponsor']
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 400) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 for no videoID', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: null,
|
|
||||||
userID: 'test',
|
|
||||||
categories: ['sponsor']
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 400) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 object categories)', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'test',
|
|
||||||
userID: 'test',
|
|
||||||
categories: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 400) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 400 bad format categories', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'test',
|
|
||||||
userID: 'test',
|
|
||||||
categories: 'sponsor'
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 400) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should return 403 if user is not VIP', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'test',
|
|
||||||
userID: 'test',
|
|
||||||
categories: [
|
|
||||||
'sponsor'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 403) {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to delete a noSegment record', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'delete-record',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'sponsor'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.delete(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['delete-record']);
|
|
||||||
if (result.length === 0) {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Didn't delete record");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to delete one noSegment record without removing another', (done) => {
|
|
||||||
let json = {
|
|
||||||
videoID: 'delete-record-1',
|
|
||||||
userID: 'VIPUser-noSegments',
|
|
||||||
categories: [
|
|
||||||
'sponsor'
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
request.delete(utils.getbaseURL()
|
|
||||||
+ "/api/noSegments", {json},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['delete-record-1']);
|
|
||||||
if (result.length === 1) {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
done("Didn't delete record");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Submission tests in this file do not check database records, only status codes.
|
|
||||||
* To test the submission code properly see ./test/cases/postSkipSegments.js
|
|
||||||
*/
|
|
||||||
|
|
||||||
it('Should not be able to submit a segment to a video with a no-segment record (single submission)', (done) => {
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/postVideoSponsorTimes", {
|
|
||||||
json: {
|
|
||||||
userID: "testman42",
|
|
||||||
videoID: "noSubmitVideo",
|
|
||||||
segments: [{
|
|
||||||
segment: [20, 40],
|
|
||||||
category: "sponsor"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 403) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not be able to submit segments to a video where any of the submissions with a no-segment record', (done) => {
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/postVideoSponsorTimes", {
|
|
||||||
json: {
|
|
||||||
userID: "testman42",
|
|
||||||
videoID: "noSubmitVideo",
|
|
||||||
segments: [{
|
|
||||||
segment: [20, 40],
|
|
||||||
category: "sponsor"
|
|
||||||
},{
|
|
||||||
segment: [50, 60],
|
|
||||||
category: "intro"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 403) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('Should be able to submit a segment to a video with a different no-segment record', (done) => {
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/postVideoSponsorTimes", {
|
|
||||||
json: {
|
|
||||||
userID: "testman42",
|
|
||||||
videoID: "noSubmitVideo",
|
|
||||||
segments: [{
|
|
||||||
segment: [20, 40],
|
|
||||||
category: "intro"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should be able to submit a segment to a video with no no-segment records', (done) => {
|
|
||||||
request.post(utils.getbaseURL()
|
|
||||||
+ "/api/postVideoSponsorTimes", {
|
|
||||||
json: {
|
|
||||||
userID: "testman42",
|
|
||||||
videoID: "normalVideo",
|
|
||||||
segments: [{
|
|
||||||
segment: [20, 40],
|
|
||||||
category: "intro"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(err, res, body) => {
|
|
||||||
if (err) done(err);
|
|
||||||
else if (res.statusCode === 200) {
|
|
||||||
done()
|
|
||||||
} else {
|
|
||||||
done("Status code was " + res.statusCode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user