mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2024-11-10 01:02:30 +01:00
migrate to typescript
This commit is contained in:
parent
c462323dd5
commit
08d27265fc
120 changed files with 5002 additions and 4711 deletions
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
|
||||
.DS_Store
|
||||
/.idea/
|
||||
|
|
|
@ -2,7 +2,7 @@ FROM node:12
|
|||
WORKDIR /usr/src/app
|
||||
COPY package.json .
|
||||
RUN npm install
|
||||
COPY index.js .
|
||||
COPY index.ts .
|
||||
COPY src src
|
||||
RUN mkdir databases
|
||||
COPY databases/*.sql databases/
|
||||
|
|
|
@ -23,4 +23,4 @@ cp /etc/sponsorblock/config.json . || cat <<EOF > config.json
|
|||
"readOnly": false
|
||||
}
|
||||
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",
|
||||
"version": "0.1.0",
|
||||
"description": "Server that holds the SponsorBlock database",
|
||||
"main": "index.js",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"test": "node test.js",
|
||||
"test": "ts-node test.ts",
|
||||
"dev": "nodemon -x \"(npm test || echo test failed) && npm start\"",
|
||||
"dev:bash": "nodemon -x 'npm test ; npm start'",
|
||||
"start": "node index.js"
|
||||
"start": "ts-node index.ts"
|
||||
},
|
||||
"author": "Ajay Ramachandran",
|
||||
"license": "MIT",
|
||||
|
@ -19,12 +19,23 @@
|
|||
"iso8601-duration": "^1.2.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"redis": "^3.0.2",
|
||||
"request": "^2.88.2",
|
||||
"sync-mysql": "^3.0.1",
|
||||
"uuid": "^3.3.2",
|
||||
"youtube-api": "^2.0.10"
|
||||
"youtube-api": "^3.0.1"
|
||||
},
|
||||
"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",
|
||||
"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).
|
||||
var 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');
|
||||
|
||||
const app = express();
|
||||
// Rate limit endpoint lists
|
||||
let voteEndpoints = [voteOnSponsorTime.endpoint];
|
||||
let viewEndpoints = [viewedVideoSponsorTime];
|
||||
const voteEndpoints: RequestHandler[] = [voteOnSponsorTime];
|
||||
const viewEndpoints: RequestHandler[] = [viewedVideoSponsorTime];
|
||||
if (config.rateLimit) {
|
||||
if (config.rateLimit.vote) voteEndpoints.unshift(rateLimitMiddleware(config.rateLimit.vote));
|
||||
if (config.rateLimit.view) viewEndpoints.unshift(rateLimitMiddleware(config.rateLimit.view));
|
||||
|
@ -49,7 +41,7 @@ if (config.rateLimit) {
|
|||
//setup CORS correctly
|
||||
app.use(corsMiddleware);
|
||||
app.use(loggerMiddleware);
|
||||
app.use(express.json())
|
||||
app.use(express.json());
|
||||
|
||||
if (config.userCounterURL) app.use(userCounter);
|
||||
|
||||
|
@ -122,16 +114,16 @@ app.delete('/api/noSegments', deleteNoSegments);
|
|||
app.get('/api/isUserVIP', getIsUserVIP);
|
||||
|
||||
//sent user a warning
|
||||
app.post('/api/warnUser', warnUser);
|
||||
app.post('/api/warnUser', postWarning);
|
||||
|
||||
//get if user is a vip
|
||||
app.post('/api/segmentShift', postSegmentShift);
|
||||
|
||||
app.get('/database.db', function (req, res) {
|
||||
res.sendFile("./databases/sponsorTimes.db", { root: "./" });
|
||||
app.get('/database.db', function (req: Request, res: Response) {
|
||||
res.sendFile("./databases/sponsorTimes.db", {root: "./"});
|
||||
});
|
||||
|
||||
// Create an HTTP service.
|
||||
module.exports = function createServer (callback) {
|
||||
export function createServer(callback: () => void) {
|
||||
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');
|
||||
var config = require('../config.js');
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {db} from '../databases/databases';
|
||||
import {config} from '../config';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
var db = require('../databases/databases.js').db;
|
||||
var getHash = require('../utils/getHash.js');
|
||||
export async function addUserAsVIP(req: Request, res: Response) {
|
||||
const userID = req.query.userID as string;
|
||||
let adminUserIDInput = req.query.adminUserID as string;
|
||||
|
||||
module.exports = async function addUserAsVIP (req, res) {
|
||||
let userID = req.query.userID;
|
||||
let adminUserIDInput = req.query.adminUserID;
|
||||
|
||||
let enabled = req.query.enabled;
|
||||
if (enabled === undefined){
|
||||
enabled = true;
|
||||
} else {
|
||||
enabled = enabled === "true";
|
||||
}
|
||||
const enabled = req.query.enabled === undefined
|
||||
? false
|
||||
: req.query.enabled === 'true';
|
||||
|
||||
if (userID == undefined || adminUserIDInput == undefined) {
|
||||
//invalid request
|
||||
|
@ -31,7 +27,7 @@ module.exports = async function addUserAsVIP (req, res) {
|
|||
}
|
||||
|
||||
//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) {
|
||||
//add them to the vip list
|
|
@ -1,13 +1,13 @@
|
|||
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');
|
||||
import {Request, Response} from 'express';
|
||||
import {isUserVIP} from '../utils/isUserVIP';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {db} from '../databases/databases';
|
||||
|
||||
module.exports = (req, res) => {
|
||||
export function deleteNoSegments(req: Request, res: Response) {
|
||||
// Collect user input data
|
||||
let videoID = req.body.videoID;
|
||||
const videoID = req.body.videoID;
|
||||
let userID = req.body.userID;
|
||||
let categories = req.body.categories;
|
||||
const categories = req.body.categories;
|
||||
|
||||
// Check input data is valid
|
||||
if (!videoID
|
||||
|
@ -17,27 +17,27 @@ module.exports = (req, res) => {
|
|||
|| categories.length === 0
|
||||
) {
|
||||
res.status(400).json({
|
||||
message: 'Bad Format'
|
||||
message: 'Bad Format',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user is VIP
|
||||
userID = getHash(userID);
|
||||
let userIsVIP = isUserVIP(userID);
|
||||
const userIsVIP = isUserVIP(userID);
|
||||
|
||||
if (!userIsVIP) {
|
||||
res.status(403).json({
|
||||
message: 'Must be a VIP to mark videos.'
|
||||
message: 'Must be a VIP to mark videos.',
|
||||
});
|
||||
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);
|
||||
}).forEach((entry) => {
|
||||
}).forEach((entry: any) => {
|
||||
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});
|
||||
};
|
||||
}
|
|
@ -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');
|
||||
const logger = require('../utils/logger.js');
|
||||
const isUserVIP = require('../utils/isUserVIP.js');
|
||||
|
||||
module.exports = (req, res) => {
|
||||
let userID = req.query.userID;
|
||||
export function getIsUserVIP(req: Request, res: Response): void {
|
||||
let userID = req.query.userID as string;
|
||||
|
||||
if (userID == undefined) {
|
||||
//invalid request
|
||||
|
@ -20,10 +19,10 @@ module.exports = (req, res) => {
|
|||
let vipState = isUserVIP(userID);
|
||||
res.status(200).json({
|
||||
hashedUserID: userID,
|
||||
vip: vipState
|
||||
vip: vipState,
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
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');
|
||||
var getHash = require('../utils/getHash.js');
|
||||
var getIP = require('../utils/getIP.js');
|
||||
function cleanGetSegments(req: Request, videoID: string, categories: any[]) {
|
||||
let userHashedIP: any;
|
||||
let shadowHiddenSegments: any[];
|
||||
|
||||
function cleanGetSegments(req, videoID, categories) {
|
||||
let userHashedIP, shadowHiddenSegments;
|
||||
|
||||
let segments = [];
|
||||
let segments: { category: any; segment: any[]; UUID: any; }[] = [];
|
||||
|
||||
try {
|
||||
for (const category of categories) {
|
||||
const categorySegments = db
|
||||
const categorySegments: any[] = db
|
||||
.prepare(
|
||||
'all',
|
||||
'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) {
|
||||
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({
|
||||
category,
|
||||
segment: [chosenSegment.startTime, chosenSegment.endTime],
|
||||
|
@ -58,7 +57,7 @@ function cleanGetSegments(req, videoID, categories) {
|
|||
return segments;
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
Logger.error(err);
|
||||
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.
|
||||
//amountOfChoices specifies the maximum amount of choices to return, 1 or more.
|
||||
//choices are unique
|
||||
function getWeightedRandomChoice(choices, amountOfChoices) {
|
||||
function getWeightedRandomChoice(choices: any[], amountOfChoices: number) {
|
||||
//trivial case: no need to go through the whole process
|
||||
if (amountOfChoices >= choices.length) {
|
||||
return choices;
|
||||
|
@ -79,10 +78,10 @@ function getWeightedRandomChoice(choices, amountOfChoices) {
|
|||
//The 3 makes -2 the minimum votes before being ignored completely
|
||||
//https://www.desmos.com/calculator/c1duhfrmts
|
||||
//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;
|
||||
|
||||
return { ...choice, weight };
|
||||
return {...choice, weight};
|
||||
});
|
||||
|
||||
//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.
|
||||
//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
|
||||
function chooseSegments(segments) {
|
||||
function chooseSegments(segments: any[]) {
|
||||
//Create groups of segments that are similar to eachother
|
||||
//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
|
||||
//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
|
||||
const similarSegmentsGroups = [];
|
||||
let currentGroup;
|
||||
const similarSegmentsGroups: any[] = [];
|
||||
let currentGroup: any;
|
||||
let cursor = -1; //-1 to make sure that, even if the 1st segment starts at 0, a new group is created
|
||||
segments.forEach(segment => {
|
||||
if (segment.startTime > cursor) {
|
||||
currentGroup = { segments: [], votes: 0 };
|
||||
currentGroup = {segments: [], votes: 0};
|
||||
similarSegmentsGroups.push(currentGroup);
|
||||
}
|
||||
|
||||
|
@ -136,7 +135,7 @@ function chooseSegments(segments) {
|
|||
//if there are too many groups, find the best 8
|
||||
return getWeightedRandomChoice(similarSegmentsGroups, 8).map(
|
||||
//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
|
||||
*/
|
||||
function handleGetSegments(req, res) {
|
||||
const videoID = req.query.videoID;
|
||||
function handleGetSegments(req: Request, res: Response) {
|
||||
const videoID = req.query.videoID as string;
|
||||
// Default to sponsor
|
||||
// If using params instead of JSON, only one category can be pulled
|
||||
const categories = req.query.categories
|
||||
? JSON.parse(req.query.categories)
|
||||
? JSON.parse(req.query.categories as string)
|
||||
: req.query.category
|
||||
? [req.query.category]
|
||||
: ['sponsor'];
|
||||
? [req.query.category]
|
||||
: ['sponsor'];
|
||||
|
||||
let segments = cleanGetSegments(req, videoID, categories);
|
||||
|
||||
|
@ -175,15 +174,17 @@ function handleGetSegments(req, res) {
|
|||
return segments;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
function endpoint(req: Request, res: Response) {
|
||||
const segments = handleGetSegments(req, res);
|
||||
|
||||
if (segments) {
|
||||
//send result
|
||||
res.send(segments);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
handleGetSegments,
|
||||
cleanGetSegments,
|
||||
endpoint: function (req, res) {
|
||||
let segments = handleGetSegments(req, res);
|
||||
|
||||
if (segments) {
|
||||
//send result
|
||||
res.send(segments);
|
||||
}
|
||||
},
|
||||
endpoint,
|
||||
};
|
|
@ -1,11 +1,9 @@
|
|||
const hashPrefixTester = require('../utils/hashPrefixTester.js');
|
||||
const getSegments = require('./getSkipSegments.js').cleanGetSegments;
|
||||
import {hashPrefixTester} from '../utils/hashPrefixTester';
|
||||
import {cleanGetSegments} from './getSkipSegments';
|
||||
import {db} from '../databases/databases';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
const databases = require('../databases/databases.js');
|
||||
const logger = require('../utils/logger.js');
|
||||
const db = databases.db;
|
||||
|
||||
module.exports = async function (req, res) {
|
||||
export async function getSkipSegmentsByHash(req: Request, res: Response) {
|
||||
let hashPrefix = req.params.prefix;
|
||||
if (!hashPrefixTester(req.params.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
|
||||
? JSON.parse(req.query.categories)
|
||||
: req.query.category
|
||||
? [req.query.category]
|
||||
: ['sponsor'];
|
||||
? JSON.parse(req.query.categories as string)
|
||||
: req.query.category
|
||||
? [req.query.category]
|
||||
: ['sponsor'];
|
||||
|
||||
// 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 {
|
||||
videoID: video.videoID,
|
||||
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;
|
||||
const request = require('request');
|
||||
const config = require('../config.js');
|
||||
import {db} from '../databases/databases';
|
||||
import request from 'request';
|
||||
import {config} from '../config';
|
||||
import {Request, Response} from 'express';
|
||||
|
||||
// A cache of the number of chrome web store users
|
||||
let chromeUsersCache = null;
|
||||
let firefoxUsersCache = null;
|
||||
let chromeUsersCache = 0;
|
||||
let firefoxUsersCache = 0;
|
||||
|
||||
// By the privacy friendly user counter
|
||||
let apiUsersCache = null;
|
||||
let apiUsersCache = 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, " +
|
||||
"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) {
|
||||
let extensionUsers = chromeUsersCache + firefoxUsersCache;
|
||||
|
@ -25,7 +26,7 @@ module.exports = function getTotalStats (req, res) {
|
|||
apiUsers: Math.max(apiUsersCache, extensionUsers),
|
||||
viewCount: row.viewCount,
|
||||
totalSubmissions: row.totalSubmissions,
|
||||
minutesSaved: row.minutesSaved
|
||||
minutesSaved: row.minutesSaved,
|
||||
});
|
||||
|
||||
// Check if the cache should be updated (every ~14 hours)
|
||||
|
@ -49,7 +50,7 @@ function updateExtensionUsers() {
|
|||
try {
|
||||
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) {
|
||||
try {
|
||||
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');
|
||||
const logger = require('../utils/logger.js');
|
||||
|
||||
module.exports = function getUsername (req, res) {
|
||||
let userID = req.query.userID;
|
||||
export function getUsername(req: Request, res: Response) {
|
||||
let userID = req.query.userID as string;
|
||||
|
||||
if (userID == undefined) {
|
||||
//invalid request
|
||||
|
@ -20,16 +20,16 @@ module.exports = function getUsername (req, res) {
|
|||
|
||||
if (row !== undefined) {
|
||||
res.send({
|
||||
userName: row.userName
|
||||
userName: row.userName,
|
||||
});
|
||||
} else {
|
||||
//no username yet, just send back the userID
|
||||
res.send({
|
||||
userName: userID
|
||||
userName: userID,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
Logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
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';
|
||||
|
||||
|
||||
module.exports = function (req, res) {
|
||||
let videoID = req.query.videoID;
|
||||
|
||||
let segments = getSkipSegments.handleGetSegments(req, res);
|
||||
export function oldGetVideoSponsorTimes(req: Request, res: Response) {
|
||||
let segments = handleGetSegments(req, res);
|
||||
|
||||
if (segments) {
|
||||
// Convert to old outputs
|
||||
|
@ -18,8 +16,8 @@ module.exports = function (req, res) {
|
|||
|
||||
res.send({
|
||||
sponsorTimes,
|
||||
UUIDs
|
||||
})
|
||||
UUIDs,
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
const getHash = require('../utils/getHash.js');
|
||||
const isUserVIP = require('../utils/isUserVIP.js');
|
||||
const logger = require('../utils/logger.js');
|
||||
import {Logger} from '../utils/logger';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {isUserVIP} from '../utils/isUserVIP';
|
||||
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
|
||||
let videoID = req.body.videoID;
|
||||
let userID = req.body.userID;
|
||||
|
@ -17,7 +18,7 @@ module.exports = (req, res) => {
|
|||
|| categories.length === 0
|
||||
) {
|
||||
res.status(400).json({
|
||||
message: 'Bad Format'
|
||||
message: 'Bad Format',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -28,7 +29,7 @@ module.exports = (req, res) => {
|
|||
|
||||
if (!userIsVIP) {
|
||||
res.status(403).json({
|
||||
message: 'Must be a VIP to mark videos.'
|
||||
message: 'Must be a VIP to mark videos.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -38,7 +39,7 @@ module.exports = (req, res) => {
|
|||
if (!noSegmentList || noSegmentList.length === 0) {
|
||||
noSegmentList = [];
|
||||
} else {
|
||||
noSegmentList = noSegmentList.map((obj) => {
|
||||
noSegmentList = noSegmentList.map((obj: any) => {
|
||||
return obj.category;
|
||||
});
|
||||
}
|
||||
|
@ -60,15 +61,15 @@ module.exports = (req, res) => {
|
|||
try {
|
||||
db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]);
|
||||
} catch (err) {
|
||||
logger.error("Error submitting 'noSegment' marker for category '" + category + "' for video '" + videoID + "'");
|
||||
logger.error(err);
|
||||
Logger.error("Error submitting 'noSegment' marker for category '" + category + "' for video '" + videoID + "'");
|
||||
Logger.error(err);
|
||||
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({
|
||||
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');
|
||||
const getIP = require('../utils/getIP.js');
|
||||
const getFormattedTime = require('../utils/getFormattedTime.js');
|
||||
const isUserTrustworthy = require('../utils/isUserTrustworthy.js')
|
||||
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];
|
||||
function sendWebhookNotification(userID: string, videoID: string, UUID: string, submissionCount: number, youtubeData: any, {submissionStart, submissionEnd}: { submissionStart: number; submissionEnd: number; }, segmentInfo: any) {
|
||||
const row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]);
|
||||
const userName = row !== undefined ? row.userName : null;
|
||||
const video = youtubeData.items[0];
|
||||
|
||||
let scopeName = "submissions.other";
|
||||
if (submissionCount <= 1) {
|
||||
|
@ -31,7 +29,7 @@ function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtube
|
|||
"id": videoID,
|
||||
"title": video.snippet.title,
|
||||
"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": {
|
||||
"UUID": UUID,
|
||||
|
@ -40,25 +38,28 @@ function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtube
|
|||
"endTime": submissionEnd,
|
||||
"user": {
|
||||
"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) {
|
||||
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) {
|
||||
err && logger.error(err);
|
||||
err && Logger.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let startTime = parseFloat(segmentInfo.segment[0]);
|
||||
let endTime = parseFloat(segmentInfo.segment[1]);
|
||||
sendWebhookNotification(userID, videoID, UUID, userSubmissionCountRow.submissionCount, data, {submissionStart: startTime, submissionEnd: endTime}, segmentInfo);
|
||||
const startTime = parseFloat(segmentInfo.segment[0]);
|
||||
const endTime = parseFloat(segmentInfo.segment[1]);
|
||||
sendWebhookNotification(userID, videoID, UUID, userSubmissionCountRow.submissionCount, data, {
|
||||
submissionStart: startTime,
|
||||
submissionEnd: endTime,
|
||||
}, segmentInfo);
|
||||
|
||||
// If it is a first time submission
|
||||
// Then send a notification to discord
|
||||
|
@ -67,45 +68,45 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
|||
json: {
|
||||
"embeds": [{
|
||||
"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 +
|
||||
"\n\nTimestamp: " +
|
||||
getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||
"\n\nCategory: " + segmentInfo.category,
|
||||
"\n\nTimestamp: " +
|
||||
getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||
"\n\nCategory: " + segmentInfo.category,
|
||||
"color": 10813440,
|
||||
"author": {
|
||||
"name": userID
|
||||
"name": userID,
|
||||
},
|
||||
"thumbnail": {
|
||||
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
}],
|
||||
},
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
logger.error("Failed to send first time submission Discord hook.");
|
||||
logger.error(JSON.stringify(err));
|
||||
logger.error("\n");
|
||||
} else if (res && res.statusCode >= 400) {
|
||||
logger.error("Error sending first time submission Discord hook");
|
||||
logger.error(JSON.stringify(res));
|
||||
logger.error("\n");
|
||||
}
|
||||
if (err) {
|
||||
Logger.error("Failed to send first time submission Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
} else if (res && res.statusCode >= 400) {
|
||||
Logger.error("Error sending first time submission Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
Logger.error("\n");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function sendWebhooksNB(userID, videoID, UUID, startTime, endTime, category, probability, ytData) {
|
||||
let submissionInfoRow = db.prepare('get', "SELECT " +
|
||||
function sendWebhooksNB(userID: string, videoID: string, UUID: string, startTime: number, endTime: number, category: string, probability: number, ytData: any) {
|
||||
const submissionInfoRow = db.prepare('get', "SELECT " +
|
||||
"(select count(1) from sponsorTimes where userID = ?) count, " +
|
||||
"(select count(1) from sponsorTimes where userID = ? and votes <= -2) disregarded, " +
|
||||
"coalesce((select userName FROM userNames WHERE userID = ?), ?) userName",
|
||||
[userID, userID, userID, userID]);
|
||||
|
||||
let submittedBy = "";
|
||||
let submittedBy: string;
|
||||
// If a userName was created then show both
|
||||
if (submissionInfoRow.userName !== userID){
|
||||
if (submissionInfoRow.userName !== userID) {
|
||||
submittedBy = submissionInfoRow.userName + "\n " + userID;
|
||||
} else {
|
||||
submittedBy = userID;
|
||||
|
@ -117,30 +118,30 @@ function sendWebhooksNB(userID, videoID, UUID, startTime, endTime, category, pro
|
|||
json: {
|
||||
"embeds": [{
|
||||
"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 +
|
||||
"\n**Timestamp:** " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||
"\n**Predicted Probability:** " + probability +
|
||||
"\n**Category:** " + category +
|
||||
"\n**Submitted by:** "+ submittedBy +
|
||||
"\n**Total User Submissions:** "+submissionInfoRow.count +
|
||||
"\n**Ignored User Submissions:** "+submissionInfoRow.disregarded,
|
||||
"\n**Timestamp:** " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||
"\n**Predicted Probability:** " + probability +
|
||||
"\n**Category:** " + category +
|
||||
"\n**Submitted by:** " + submittedBy +
|
||||
"\n**Total User Submissions:** " + submissionInfoRow.count +
|
||||
"\n**Ignored User Submissions:** " + submissionInfoRow.disregarded,
|
||||
"color": 10813440,
|
||||
"thumbnail": {
|
||||
"url": ytData.items[0].snippet.thumbnails.maxres ? ytData.items[0].snippet.thumbnails.maxres.url : "",
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
}],
|
||||
},
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
logger.error("Failed to send NeuralBlock Discord hook.");
|
||||
logger.error(JSON.stringify(err));
|
||||
logger.error("\n");
|
||||
} else if (res && res.statusCode >= 400) {
|
||||
logger.error("Error sending NeuralBlock Discord hook");
|
||||
logger.error(JSON.stringify(res));
|
||||
logger.error("\n");
|
||||
}
|
||||
if (err) {
|
||||
Logger.error("Failed to send NeuralBlock Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
} else if (res && res.statusCode >= 400) {
|
||||
Logger.error("Error sending NeuralBlock Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
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
|
||||
// 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.
|
||||
async function autoModerateSubmission(submission) {
|
||||
async function autoModerateSubmission(submission: { videoID: any; userID: any; segments: any }) {
|
||||
// Get the video information from the youtube API
|
||||
if (config.youtubeAPIKey !== null) {
|
||||
let {err, data} = await new Promise((resolve, reject) => {
|
||||
YouTubeAPI.listVideos(submission.videoID, (err, data) => resolve({err, data}));
|
||||
const {err, data} = await new Promise((resolve) => {
|
||||
YouTubeAPI.listVideos(submission.videoID, (err: any, data: any) => resolve({err, data}));
|
||||
});
|
||||
|
||||
if (err) {
|
||||
|
@ -164,54 +165,54 @@ async function autoModerateSubmission(submission) {
|
|||
if (data.pageInfo.totalResults === 0) {
|
||||
return "No video exists with id " + submission.videoID;
|
||||
} else {
|
||||
let segments = submission.segments;
|
||||
const segments = submission.segments;
|
||||
let nbString = "";
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
let startTime = parseFloat(segments[i].segment[0]);
|
||||
let endTime = parseFloat(segments[i].segment[1]);
|
||||
const startTime = parseFloat(segments[i].segment[0]);
|
||||
const endTime = parseFloat(segments[i].segment[1]);
|
||||
|
||||
let duration = data.items[0].contentDetails.duration;
|
||||
duration = isoDurations.toSeconds(isoDurations.parse(duration));
|
||||
if (duration == 0) {
|
||||
// Allow submission if the duration is 0 (bug in youtube api)
|
||||
return false;
|
||||
} else if ((endTime - startTime) > (duration/100)*80) {
|
||||
} else if ((endTime - startTime) > (duration / 100) * 80) {
|
||||
// Reject submission if over 80% of the video
|
||||
return "One of your submitted segments is over 80% of the video.";
|
||||
} else {
|
||||
if (segments[i].category === "sponsor") {
|
||||
//Prepare timestamps to send to NB all at once
|
||||
nbString = nbString + segments[i].segment[0] + "," + segments[i].segment[1] + ";";
|
||||
//Prepare timestamps to send to NB all at once
|
||||
nbString = nbString + segments[i].segment[0] + "," + segments[i].segment[1] + ";";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check NeuralBlock
|
||||
let neuralBlockURL = config.neuralBlockURL;
|
||||
const neuralBlockURL = config.neuralBlockURL;
|
||||
if (!neuralBlockURL) return false;
|
||||
let response = await fetch(neuralBlockURL + "/api/checkSponsorSegments?vid=" + submission.videoID +
|
||||
"&segments=" + nbString.substring(0,nbString.length-1));
|
||||
const response = await fetch(neuralBlockURL + "/api/checkSponsorSegments?vid=" + submission.videoID +
|
||||
"&segments=" + nbString.substring(0, nbString.length - 1));
|
||||
if (!response.ok) return false;
|
||||
|
||||
let nbPredictions = await response.json();
|
||||
nbDecision = false;
|
||||
const nbPredictions = await response.json();
|
||||
let nbDecision = false;
|
||||
let predictionIdx = 0; //Keep track because only sponsor categories were submitted
|
||||
for (let i = 0; i < segments.length; i++){
|
||||
if (segments[i].category === "sponsor"){
|
||||
if (nbPredictions.probabilities[predictionIdx] < 0.70){
|
||||
nbDecision = true; // At least one bad entry
|
||||
startTime = parseFloat(segments[i].segment[0]);
|
||||
endTime = parseFloat(segments[i].segment[1]);
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
if (segments[i].category === "sponsor") {
|
||||
if (nbPredictions.probabilities[predictionIdx] < 0.70) {
|
||||
nbDecision = true; // At least one bad entry
|
||||
const startTime = parseFloat(segments[i].segment[0]);
|
||||
const endTime = parseFloat(segments[i].segment[1]);
|
||||
|
||||
const UUID = getSubmissionUUID(submission.videoID, segments[i].category, submission.userID, startTime, endTime);
|
||||
// Send to Discord
|
||||
// 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);
|
||||
const UUID = getSubmissionUUID(submission.videoID, segments[i].category, submission.userID, startTime, endTime);
|
||||
// Send to Discord
|
||||
// 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);
|
||||
}
|
||||
predictionIdx++;
|
||||
}
|
||||
|
||||
}
|
||||
if (nbDecision){
|
||||
if (nbDecision) {
|
||||
return "Rejected based on NeuralBlock predictions.";
|
||||
} else {
|
||||
return false;
|
||||
|
@ -219,7 +220,7 @@ async function autoModerateSubmission(submission) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Skipped YouTube API");
|
||||
Logger.debug("Skipped YouTube API");
|
||||
|
||||
// Can't moderate the submission without calling the youtube API
|
||||
// so allow by default.
|
||||
|
@ -227,33 +228,33 @@ async function autoModerateSubmission(submission) {
|
|||
}
|
||||
}
|
||||
|
||||
function proxySubmission(req) {
|
||||
request.post(config.proxySubmission + '/api/skipSegments?userID='+req.query.userID+'&videoID='+req.query.videoID, {json: req.body}, (err, result) => {
|
||||
function proxySubmission(req: Request) {
|
||||
request.post(config.proxySubmission + '/api/skipSegments?userID=' + req.query.userID + '&videoID=' + req.query.videoID, {json: req.body}, (err, result) => {
|
||||
if (config.mode === 'development') {
|
||||
if (!err) {
|
||||
logger.debug('Proxy Submission: ' + result.statusCode + ' ('+result.body+')');
|
||||
Logger.debug('Proxy Submission: ' + result.statusCode + ' (' + result.body + ')');
|
||||
} 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) {
|
||||
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 segments = req.body.segments;
|
||||
|
||||
let segments = req.body.segments;
|
||||
if (segments === undefined) {
|
||||
// Use query instead
|
||||
segments = [{
|
||||
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);
|
||||
|
||||
//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 now = Date.now();
|
||||
let warningsCount = db.prepare('get', "SELECT count(1) as count FROM warnings WHERE userID = ? AND issueTime > ?",
|
||||
[userID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))]
|
||||
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))],
|
||||
).count;
|
||||
|
||||
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
|
||||
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
|
||||
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)) {
|
||||
res.status("400").send("Category doesn't exist.");
|
||||
return;
|
||||
res.status(400).send("Category doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reject segemnt if it's in the no segments list
|
||||
if (!isVIP && noSegmentList.indexOf(segments[i].category) !== -1) {
|
||||
// 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(
|
||||
"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 === "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 " : "")
|
||||
+ "If you believe this is incorrect, please contact someone on Discord."
|
||||
"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 === "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 " : "")
|
||||
+ "If you believe this is incorrect, please contact someone on Discord.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -330,7 +333,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
let endTime = parseFloat(segments[i].segment[1]);
|
||||
|
||||
if (isNaN(startTime) || isNaN(endTime)
|
||||
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime >= endTime) {
|
||||
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime >= endTime) {
|
||||
//invalid request
|
||||
res.status(400).send("One of your segments times are invalid (too short, startTime before endTime, etc.)");
|
||||
return;
|
||||
|
@ -343,7 +346,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
}
|
||||
|
||||
//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]);
|
||||
if (duplicateCheck2Row.count > 0) {
|
||||
res.sendStatus(409);
|
||||
|
@ -353,8 +356,8 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
|
||||
// Auto moderator check
|
||||
if (!isVIP) {
|
||||
let autoModerateResult = await autoModerateSubmission({userID, videoID, segments});//startTime, endTime, category: segments[i].category});
|
||||
if (autoModerateResult == "Rejected based on NeuralBlock predictions."){
|
||||
const autoModerateResult = await autoModerateSubmission({userID, videoID, segments});//startTime, endTime, category: segments[i].category});
|
||||
if (autoModerateResult == "Rejected based on NeuralBlock predictions.") {
|
||||
// If NB automod rejects, the submission will start with -2 votes.
|
||||
// Note, if one submission is bad all submissions will be affected.
|
||||
// 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
|
||||
let UUIDs = [];
|
||||
const UUIDs = [];
|
||||
|
||||
try {
|
||||
//get current time
|
||||
let timeSubmitted = Date.now();
|
||||
const timeSubmitted = Date.now();
|
||||
|
||||
let yesterday = timeSubmitted - 86400000;
|
||||
const yesterday = timeSubmitted - 86400000;
|
||||
|
||||
// Disable IP ratelimiting for now
|
||||
if (false) {
|
||||
//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) {
|
||||
//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
|
||||
if (false) {
|
||||
//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) {
|
||||
//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
|
||||
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;
|
||||
|
||||
|
@ -418,20 +421,20 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
}
|
||||
|
||||
if (config.youtubeAPIKey !== null) {
|
||||
let {err, data} = await new Promise((resolve, reject) => {
|
||||
YouTubeAPI.listVideos(videoID, (err, data) => resolve({err, data}));
|
||||
let {err, data} = await new Promise((resolve) => {
|
||||
YouTubeAPI.listVideos(videoID, (err: any, data: any) => resolve({err, data}));
|
||||
});
|
||||
|
||||
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 {
|
||||
//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]);
|
||||
let allSegmentTimes = [];
|
||||
const allSubmittedByUser = db.prepare('all', "SELECT startTime, endTime FROM sponsorTimes WHERE userID = ? and videoID = ? and votes > -1", [userID, videoID]);
|
||||
const allSegmentTimes = [];
|
||||
if (allSubmittedByUser !== undefined) {
|
||||
//add segments the user has previously submitted
|
||||
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
|
||||
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;
|
||||
videoDuration = isoDurations.toSeconds(isoDurations.parse(videoDuration));
|
||||
|
@ -451,7 +456,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
let allSegmentDuration = 0;
|
||||
//sum all segment times together
|
||||
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
|
||||
res.status(400).send("Total length of your submitted segments are over 80% of the video.");
|
||||
return;
|
||||
|
@ -470,8 +475,8 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
db.prepare('run', "INSERT INTO sponsorTimes " +
|
||||
"(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden, hashedVideoID)" +
|
||||
"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
|
||||
|
@ -479,7 +484,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
} catch (err) {
|
||||
//a DB change probably occurred
|
||||
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);
|
||||
|
||||
return;
|
||||
|
@ -488,7 +493,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
UUIDs.push(UUID);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
Logger.error(err);
|
||||
|
||||
res.sendStatus(500);
|
||||
|
||||
|
@ -516,8 +521,9 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
// [50, 80],
|
||||
// [100, 150]
|
||||
// ]
|
||||
function mergeTimeSegments(ranges) {
|
||||
var result = [], last;
|
||||
function mergeTimeSegments(ranges: number[][]) {
|
||||
const result: number[][] = [];
|
||||
let last: number[];
|
||||
|
||||
ranges.forEach(function (r) {
|
||||
if (!last || r[0] > last[1])
|
|
@ -1,9 +1,10 @@
|
|||
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');
|
||||
import {Request, Response} from 'express';
|
||||
import {Logger} from '../utils/logger';
|
||||
import {db} from '../databases/databases';
|
||||
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
|
||||
let issuerUserID = getHash(req.body.issuerUserID);
|
||||
let userID = getHash(req.body.userID);
|
||||
|
@ -11,14 +12,14 @@ module.exports = (req, res) => {
|
|||
|
||||
// Ensure user is a VIP
|
||||
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"});
|
||||
return;
|
||||
}
|
||||
|
||||
db.prepare('run', 'INSERT INTO warnings (userID, issueTime, issuerUserID) VALUES (?, ?, ?)', [userID, issueTime, issuerUserID]);
|
||||
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');
|
||||
|
||||
var getHash = require('../utils/getHash.js');
|
||||
var getIP = require('../utils/getIP.js');
|
||||
var getFormattedTime = require('../utils/getFormattedTime.js');
|
||||
var isUserTrustworthy = require('../utils/isUserTrustworthy.js');
|
||||
const { getVoteAuthor, getVoteAuthorRaw, dispatchEvent } = require('../utils/webhookUtils.js');
|
||||
|
||||
var databases = require('../databases/databases.js');
|
||||
var db = databases.db;
|
||||
var privateDB = databases.privateDB;
|
||||
var YouTubeAPI = require('../utils/youtubeAPI.js');
|
||||
var request = require('request');
|
||||
const logger = require('../utils/logger.js');
|
||||
const isUserVIP = require('../utils/isUserVIP.js');
|
||||
import {Request, Response} from 'express';
|
||||
import {Logger} from '../utils/logger';
|
||||
import {isUserVIP} from '../utils/isUserVIP';
|
||||
import request from 'request';
|
||||
import {YouTubeAPI} from '../utils/youtubeApi';
|
||||
import {db, privateDB} from '../databases/databases';
|
||||
import {dispatchEvent, getVoteAuthor, getVoteAuthorRaw} from '../utils/webhookUtils';
|
||||
import {isUserTrustworthy} from '../utils/isUserTrustworthy';
|
||||
import {getFormattedTime} from '../utils/getFormattedTime';
|
||||
import {getIP} from '../utils/getIP';
|
||||
import {getHash} from '../utils/getHash';
|
||||
import {config} from '../config';
|
||||
|
||||
const voteTypes = {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} voteData
|
||||
* @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, " +
|
||||
function sendWebhooks(voteData: VoteData) {
|
||||
const 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 and votes <= -2) disregarded " +
|
||||
"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) {
|
||||
let webhookURL = null;
|
||||
let webhookURL: string = null;
|
||||
if (voteData.voteTypeEnum === voteTypes.normal) {
|
||||
webhookURL = config.discordReportChannelWebhookURL;
|
||||
} else if (voteData.voteTypeEnum === voteTypes.incorrect) {
|
||||
|
@ -51,20 +51,20 @@ function sendWebhooks(voteData) {
|
|||
if (config.youtubeAPIKey !== null) {
|
||||
YouTubeAPI.listVideos(submissionInfoRow.videoID, (err, data) => {
|
||||
if (err || data.items.length === 0) {
|
||||
err && logger.error(err);
|
||||
err && Logger.error(err.toString());
|
||||
return;
|
||||
}
|
||||
let isUpvote = voteData.incrementAmount > 0;
|
||||
const isUpvote = voteData.incrementAmount > 0;
|
||||
// Send custom webhooks
|
||||
dispatchEvent(isUpvote ? "vote.up" : "vote.down", {
|
||||
"user": {
|
||||
"status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission)
|
||||
"status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission),
|
||||
},
|
||||
"video": {
|
||||
"id": submissionInfoRow.videoID,
|
||||
"title": data.items[0].snippet.title,
|
||||
"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": {
|
||||
"UUID": voteData.UUID,
|
||||
|
@ -77,14 +77,14 @@ function sendWebhooks(voteData) {
|
|||
"username": submissionInfoRow.userName,
|
||||
"submissions": {
|
||||
"total": submissionInfoRow.count,
|
||||
"ignored": submissionInfoRow.disregarded
|
||||
}
|
||||
}
|
||||
"ignored": submissionInfoRow.disregarded,
|
||||
},
|
||||
},
|
||||
},
|
||||
"votes": {
|
||||
"before": voteData.row.votes,
|
||||
"after": (voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount)
|
||||
}
|
||||
"after": (voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount),
|
||||
},
|
||||
});
|
||||
|
||||
// Send discord message
|
||||
|
@ -99,29 +99,29 @@ function sendWebhooks(voteData) {
|
|||
(voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount) + " Votes Now | " + voteData.row.views
|
||||
+ " Views**\n\n**Submission ID:** " + voteData.UUID
|
||||
+ "\n**Category:** " + submissionInfoRow.category
|
||||
+ "\n\n**Submitted by:** "+submissionInfoRow.userName+"\n " + submissionInfoRow.userID
|
||||
+ "\n\n**Total User Submissions:** "+submissionInfoRow.count
|
||||
+ "\n**Ignored User Submissions:** "+submissionInfoRow.disregarded
|
||||
+"\n\n**Timestamp:** " +
|
||||
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
|
||||
+ "\n\n**Submitted by:** " + submissionInfoRow.userName + "\n " + submissionInfoRow.userID
|
||||
+ "\n\n**Total User Submissions:** " + submissionInfoRow.count
|
||||
+ "\n**Ignored User Submissions:** " + submissionInfoRow.disregarded
|
||||
+ "\n\n**Timestamp:** " +
|
||||
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
|
||||
"color": 10813440,
|
||||
"author": {
|
||||
"name": getVoteAuthor(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission)
|
||||
"name": getVoteAuthor(userSubmissionCountRow.submissionCount, voteData.isVIP, voteData.isOwnSubmission),
|
||||
},
|
||||
"thumbnail": {
|
||||
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
}],
|
||||
},
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
logger.error("Failed to send reported submission Discord hook.");
|
||||
logger.error(JSON.stringify(err));
|
||||
logger.error("\n");
|
||||
Logger.error("Failed to send reported submission Discord hook.");
|
||||
Logger.error(JSON.stringify(err));
|
||||
Logger.error("\n");
|
||||
} else if (res && res.statusCode >= 400) {
|
||||
logger.error("Error sending reported submission Discord hook");
|
||||
logger.error(JSON.stringify(res));
|
||||
logger.error("\n");
|
||||
Logger.error("Error sending reported submission Discord hook");
|
||||
Logger.error(JSON.stringify(res));
|
||||
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
|
||||
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) {
|
||||
// Double vote, ignore
|
||||
|
@ -141,21 +141,21 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||
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) {
|
||||
// Submission doesn't exist
|
||||
res.status("400").send("Submission doesn't exist.");
|
||||
res.status(400).send("Submission doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.categoryList.includes(category)) {
|
||||
res.status("400").send("Category doesn't exist.");
|
||||
return;
|
||||
res.status(400).send("Category doesn't exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
let timeSubmitted = Date.now();
|
||||
const timeSubmitted = Date.now();
|
||||
|
||||
let voteAmount = isVIP ? 500 : 1;
|
||||
const voteAmount = isVIP ? 500 : 1;
|
||||
|
||||
// Add the vote
|
||||
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
|
||||
if (previousVoteInfo !== undefined) {
|
||||
if (previousVoteInfo !== undefined) {
|
||||
// Reverse the previous vote
|
||||
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
|
||||
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]);
|
||||
let isSubmissionVIP = submissionInfo && isUserVIP(submissionInfo.userID);
|
||||
let startingVotes = isSubmissionVIP ? 10000 : 1;
|
||||
const submissionInfo = db.prepare("get", "SELECT userID, timeSubmitted FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||
const isSubmissionVIP = submissionInfo && isUserVIP(submissionInfo.userID);
|
||||
const startingVotes = isSubmissionVIP ? 10000 : 1;
|
||||
|
||||
// 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
|
||||
let currentCategoryCount = (currentCategoryInfo === undefined || currentCategoryInfo === null) ? startingVotes : currentCategoryInfo.votes;
|
||||
const currentCategoryCount = (currentCategoryInfo === undefined || currentCategoryInfo === null) ? startingVotes : currentCategoryInfo.votes;
|
||||
|
||||
// Add submission as vote
|
||||
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]);
|
||||
}
|
||||
|
||||
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
|
||||
// VIPs change it every time
|
||||
|
@ -206,11 +206,11 @@ function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
|||
res.sendStatus(200);
|
||||
}
|
||||
|
||||
async function voteOnSponsorTime(req, res) {
|
||||
let UUID = req.query.UUID;
|
||||
let userID = req.query.userID;
|
||||
let type = req.query.type;
|
||||
let category = req.query.category;
|
||||
async function voteOnSponsorTime(req: Request, res: Response) {
|
||||
const UUID = req.query.UUID as string;
|
||||
let userID = req.query.userID as string;
|
||||
let type = req.query.type !== undefined ? parseInt(req.query.type as string) : undefined;
|
||||
const category = req.query.category as string;
|
||||
|
||||
if (UUID === undefined || userID === undefined || (type === undefined && category === undefined)) {
|
||||
//invalid request
|
||||
|
@ -219,20 +219,20 @@ async function voteOnSponsorTime(req, res) {
|
|||
}
|
||||
|
||||
//hash the userID
|
||||
let nonAnonUserID = getHash(userID);
|
||||
const nonAnonUserID = getHash(userID);
|
||||
userID = getHash(userID + UUID);
|
||||
|
||||
//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
|
||||
let hashedIP = getHash(ip + config.globalSalt);
|
||||
const hashedIP = getHash(ip + config.globalSalt);
|
||||
|
||||
//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
|
||||
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) {
|
||||
return categoryVote(UUID, nonAnonUserID, isVIP, category, hashedIP, res);
|
||||
|
@ -240,29 +240,29 @@ async function voteOnSponsorTime(req, res) {
|
|||
|
||||
if (type == 1 && !isVIP && !isOwnSubmission) {
|
||||
// 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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const MILLISECONDS_IN_HOUR = 3600000;
|
||||
const now = Date.now();
|
||||
let warningsCount = db.prepare('get', "SELECT count(1) as count FROM warnings WHERE userID = ? AND issueTime > ?",
|
||||
[nonAnonUserID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))]
|
||||
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))],
|
||||
).count;
|
||||
|
||||
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 {
|
||||
//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
|
||||
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)
|
||||
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 ((isVIP || isOwnSubmission) && incrementAmount < 0) {
|
||||
//this user is a vip and a downvote
|
||||
incrementAmount = - (row.votes + 2 - oldIncrementAmount);
|
||||
incrementAmount = -(row.votes + 2 - oldIncrementAmount);
|
||||
type = incrementAmount;
|
||||
}
|
||||
} 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
|
||||
let ableToVote = isVIP
|
||||
|| (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 UUID FROM votes WHERE UUID = ? AND hashedIP = ? AND userID != ?", [UUID, hashedIP, userID]) === undefined);
|
||||
const ableToVote = isVIP
|
||||
|| (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 UUID FROM votes WHERE UUID = ? AND hashedIP = ? AND userID != ?", [UUID, hashedIP, userID]) === undefined);
|
||||
|
||||
if (ableToVote) {
|
||||
//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
|
||||
if (incrementAmount > 0 && voteTypeEnum === voteTypes.normal) {
|
||||
//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) {
|
||||
// They are voting on a non-existent submission
|
||||
res.status(400).send("Voting on a non-existent submission");
|
||||
return;
|
||||
}
|
||||
|
||||
let submissionUserID = submissionUserIDInfo.userID;
|
||||
const submissionUserID = submissionUserIDInfo.userID;
|
||||
|
||||
//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) {
|
||||
//see if some of this users submissions should be visible again
|
||||
|
@ -387,17 +387,16 @@ async function voteOnSponsorTime(req, res) {
|
|||
row,
|
||||
category,
|
||||
incrementAmount,
|
||||
oldIncrementAmount
|
||||
oldIncrementAmount,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
Logger.error(err);
|
||||
|
||||
res.status(500).json({error: 'Internal error creating segment vote'});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
voteOnSponsorTime,
|
||||
endpoint: voteOnSponsorTime
|
||||
export {
|
||||
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 maximumPrefix = config.maximumPrefix || '32'; // Half the hash.
|
||||
|
||||
const prefixChecker = new RegExp('^[\\da-f]{' + minimumPrefix + ',' + maximumPrefix + '}$', 'i');
|
||||
|
||||
module.exports = (prefix) => {
|
||||
export function hashPrefixTester(prefix: string): boolean {
|
||||
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');
|
||||
const db = databases.db;
|
||||
import {db} from '../databases/databases';
|
||||
|
||||
module.exports = (userID) => {
|
||||
export function isUserVIP(userID: string): boolean {
|
||||
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
Loading…
Reference in a new issue