mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-14 23:47:00 +03:00
Merge pull request #3 from Joe-Dowd/no-segments
fixed db update, started no segments
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
"discordReportChannelWebhookURL": null, //URL from discord if you would like notifications when someone makes a report [optional]
|
"discordReportChannelWebhookURL": null, //URL from discord if you would like notifications when someone makes a report [optional]
|
||||||
"discordFirstTimeSubmissionsWebhookURL": null, //URL from discord if you would like notifications when someone makes a first time submission [optional]
|
"discordFirstTimeSubmissionsWebhookURL": null, //URL from discord if you would like notifications when someone makes a first time submission [optional]
|
||||||
"discordCompletelyIncorrectReportWebhookURL": null, //URL from discord if you would like notifications when someone reports a submission as completely incorrect [optional]
|
"discordCompletelyIncorrectReportWebhookURL": null, //URL from discord if you would like notifications when someone reports a submission as completely incorrect [optional]
|
||||||
|
"userCounterURL": null, // For user counting. URL to instance of https://github.com/ajayyy/PrivacyUserCount
|
||||||
"proxySubmission": null, // Base url to proxy submissions to persist // e.g. https://sponsor.ajay.app (no trailing slash)
|
"proxySubmission": null, // Base url to proxy submissions to persist // e.g. https://sponsor.ajay.app (no trailing slash)
|
||||||
"behindProxy": "X-Forwarded-For", //Options: "X-Forwarded-For", "Cloudflare", "X-Real-IP", anything else will mean it is not behind a proxy. True defaults to "X-Forwarded-For"
|
"behindProxy": "X-Forwarded-For", //Options: "X-Forwarded-For", "Cloudflare", "X-Real-IP", anything else will mean it is not behind a proxy. True defaults to "X-Forwarded-For"
|
||||||
"db": "./databases/sponsorTimes.db",
|
"db": "./databases/sponsorTimes.db",
|
||||||
@@ -17,5 +18,6 @@
|
|||||||
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
||||||
"privateDBSchema": "./databases/_private.db.sql",
|
"privateDBSchema": "./databases/_private.db.sql",
|
||||||
"mode": "development",
|
"mode": "development",
|
||||||
"readOnly": false
|
"readOnly": false,
|
||||||
|
"webhooks": []
|
||||||
}
|
}
|
||||||
|
|||||||
13
databases/_upgrade_sponsorTimes_2.sql
Normal file
13
databases/_upgrade_sponsorTimes_2.sql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
/* Add new table: noSegments */
|
||||||
|
CREATE TABLE "noSegments" (
|
||||||
|
"videoID" TEXT NOT NULL,
|
||||||
|
"userID" TEXT NOT NULL,
|
||||||
|
"category" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Add version to config */
|
||||||
|
UPDATE config SET value = 2 WHERE key = 'version';
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -5,33 +5,20 @@ cd /usr/src/app
|
|||||||
cp /etc/sponsorblock/config.json . || cat <<EOF > config.json
|
cp /etc/sponsorblock/config.json . || cat <<EOF > config.json
|
||||||
{
|
{
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
"mysql": {
|
"globalSalt": "[CHANGE THIS]",
|
||||||
"host": "127.0.0.1",
|
"adminUserID": "[CHANGE THIS]",
|
||||||
"port": 3306,
|
"youtubeAPIKey": null,
|
||||||
"database": "sponsorblock",
|
|
||||||
"user": "sponsorblock",
|
|
||||||
"password": "sponsorblock"
|
|
||||||
},
|
|
||||||
"privateMysql": {
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"port": 3306,
|
|
||||||
"database": "sponsorblock_private",
|
|
||||||
"user": "sponsorblock",
|
|
||||||
"password": "sponsorblock"
|
|
||||||
},
|
|
||||||
"globalSalt": "",
|
|
||||||
"adminUserID": "",
|
|
||||||
"youtubeAPIKey": "",
|
|
||||||
"discordReportChannelWebhookURL": null,
|
"discordReportChannelWebhookURL": null,
|
||||||
"discordFirstTimeSubmissionsWebhookURL": null,
|
"discordFirstTimeSubmissionsWebhookURL": null,
|
||||||
"discordAutoModWebhookURL": null,
|
"discordAutoModWebhookURL": null,
|
||||||
"behindProxy": true,
|
"proxySubmission": null,
|
||||||
"db": null,
|
"behindProxy": "X-Forwarded-For",
|
||||||
"privateDB": null,
|
"db": "./databases/sponsorTimes.db",
|
||||||
|
"privateDB": "./databases/private.db",
|
||||||
"createDatabaseIfNotExist": true,
|
"createDatabaseIfNotExist": true,
|
||||||
"schemaFolder": null,
|
"schemaFolder": "./databases",
|
||||||
"dbSchema": null,
|
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
||||||
"privateDBSchema": null,
|
"privateDBSchema": "./databases/_private.db.sql",
|
||||||
"mode": "development",
|
"mode": "development",
|
||||||
"readOnly": false
|
"readOnly": false
|
||||||
}
|
}
|
||||||
|
|||||||
3
index.js
3
index.js
@@ -1,5 +1,6 @@
|
|||||||
var config = require('./src/config.js');
|
var config = require('./src/config.js');
|
||||||
var createServer = require('./src/app.js');
|
var createServer = require('./src/app.js');
|
||||||
|
const logger = require('./src/utils/logger.js');
|
||||||
var server = createServer(() => {
|
var server = createServer(() => {
|
||||||
console.log("Server started on port " + config.port + ".");
|
logger.info("Server started on port " + config.port + ".");
|
||||||
});
|
});
|
||||||
|
|||||||
12
src/app.js
12
src/app.js
@@ -6,6 +6,7 @@ var config = require('./config.js');
|
|||||||
// Middleware
|
// Middleware
|
||||||
var corsMiddleware = require('./middleware/cors.js');
|
var corsMiddleware = require('./middleware/cors.js');
|
||||||
var loggerMiddleware = require('./middleware/logger.js');
|
var loggerMiddleware = require('./middleware/logger.js');
|
||||||
|
const userCounter = require('./middleware/userCounter.js');
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
var getSkipSegments = require('./routes/getSkipSegments.js').endpoint;
|
var getSkipSegments = require('./routes/getSkipSegments.js').endpoint;
|
||||||
@@ -21,6 +22,8 @@ var getViewsForUser = require('./routes/getViewsForUser.js');
|
|||||||
var getTopUsers = require('./routes/getTopUsers.js');
|
var getTopUsers = require('./routes/getTopUsers.js');
|
||||||
var getTotalStats = require('./routes/getTotalStats.js');
|
var getTotalStats = require('./routes/getTotalStats.js');
|
||||||
var getDaysSavedFormatted = require('./routes/getDaysSavedFormatted.js');
|
var getDaysSavedFormatted = require('./routes/getDaysSavedFormatted.js');
|
||||||
|
var postNoSegments = require('./routes/postNoSegments.js');
|
||||||
|
var getIsUserVIP = require('./routes/getIsUserVIP.js');
|
||||||
|
|
||||||
// Old Routes
|
// Old Routes
|
||||||
var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js');
|
var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js');
|
||||||
@@ -31,6 +34,8 @@ app.use(corsMiddleware);
|
|||||||
app.use(loggerMiddleware);
|
app.use(loggerMiddleware);
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
|
|
||||||
|
if (config.userCounterURL) app.use(userCounter);
|
||||||
|
|
||||||
// Setup pretty JSON
|
// Setup pretty JSON
|
||||||
if (config.mode === "development") app.set('json spaces', 2);
|
if (config.mode === "development") app.set('json spaces', 2);
|
||||||
|
|
||||||
@@ -86,6 +91,13 @@ app.get('/api/getTotalStats', getTotalStats);
|
|||||||
//send out a formatted time saved total
|
//send out a formatted time saved total
|
||||||
app.get('/api/getDaysSavedFormatted', getDaysSavedFormatted);
|
app.get('/api/getDaysSavedFormatted', getDaysSavedFormatted);
|
||||||
|
|
||||||
|
//submit video containing no segments
|
||||||
|
app.post('/api/noSegments', postNoSegments);
|
||||||
|
|
||||||
|
//get if user is a vip
|
||||||
|
app.get('/api/isUserVIP', getIsUserVIP);
|
||||||
|
|
||||||
|
|
||||||
app.get('/database.db', function (req, res) {
|
app.get('/database.db', function (req, res) {
|
||||||
res.sendFile("./databases/sponsorTimes.db", { root: "./" });
|
res.sendFile("./databases/sponsorTimes.db", { root: "./" });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
var MysqlInterface = require('sync-mysql');
|
var MysqlInterface = require('sync-mysql');
|
||||||
var config = require('../config.js');
|
const logger = require('../utils/logger.js');
|
||||||
var logger = require('../utils/logger.js');
|
|
||||||
|
|
||||||
class Mysql {
|
class Mysql {
|
||||||
constructor(msConfig) {
|
constructor(msConfig) {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ var Sqlite3 = require('better-sqlite3');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Sqlite = require('./Sqlite.js')
|
var Sqlite = require('./Sqlite.js')
|
||||||
var Mysql = require('./Mysql.js')
|
var Mysql = require('./Mysql.js');
|
||||||
|
const logger = require('../utils/logger.js');
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
readonly: config.readOnly,
|
readonly: config.readOnly,
|
||||||
@@ -60,12 +61,16 @@ if (config.mysql) {
|
|||||||
let versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version");
|
let versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version");
|
||||||
let versionCode = versionCodeInfo ? versionCodeInfo.value : 0;
|
let versionCode = versionCodeInfo ? versionCodeInfo.value : 0;
|
||||||
|
|
||||||
let path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (versionCode + 1) + ".sql";
|
let path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
||||||
|
logger.debug('db update: trying ' + path);
|
||||||
while (fs.existsSync(path)) {
|
while (fs.existsSync(path)) {
|
||||||
|
logger.debug('db update: updating ' + path);
|
||||||
db.exec(fs.readFileSync(path).toString());
|
db.exec(fs.readFileSync(path).toString());
|
||||||
|
|
||||||
versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value;
|
versionCode = db.prepare("SELECT value FROM config WHERE key = ?").get("version").value;
|
||||||
path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (versionCode + 1) + ".sql";
|
path = config.schemaFolder + "/_upgrade_" + prefix + "_" + (parseInt(versionCode) + 1) + ".sql";
|
||||||
}
|
logger.debug('db update: trying ' + path);
|
||||||
|
}
|
||||||
|
logger.debug('db update: no file ' + path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
const log = require('../utils/logger.js'); // log not logger to not interfere with function name
|
const log = require('../utils/logger.js'); // log not logger to not interfere with function name
|
||||||
|
|
||||||
module.exports = function logger (req, res, next) {
|
module.exports = function logger (req, res, next) {
|
||||||
log.info('Request recieved: ' + req.url);
|
log.info("Request recieved: " + req.method + " " + req.url);
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
11
src/middleware/userCounter.js
Normal file
11
src/middleware/userCounter.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
var request = require('request');
|
||||||
|
|
||||||
|
var config = require('../config.js');
|
||||||
|
var getIP = require('../utils/getIP.js');
|
||||||
|
const getHash = require('../utils/getHash.js');
|
||||||
|
|
||||||
|
module.exports = function userCounter(req, res, next) {
|
||||||
|
request.post(config.userCounterURL + "/api/v1/addIP?hashedIP=" + getHash(getIP(req), 1));
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
31
src/routes/getIsUserVIP.js
Normal file
31
src/routes/getIsUserVIP.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
var db = require('../databases/databases.js').db;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (userID == undefined) {
|
||||||
|
//invalid request
|
||||||
|
res.sendStatus(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//hash the userID
|
||||||
|
userID = getHash(userID);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let vipState = isUserVIP(userID);
|
||||||
|
res.status(200).json({
|
||||||
|
hashedUserID: userID,
|
||||||
|
vip: vipState
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err);
|
||||||
|
res.sendStatus(500);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
var fs = require('fs');
|
|
||||||
var config = require('../config.js');
|
var config = require('../config.js');
|
||||||
|
|
||||||
var databases = require('../databases/databases.js');
|
var databases = require('../databases/databases.js');
|
||||||
@@ -21,11 +20,10 @@ function getWeightedRandomChoice(choices, amountOfChoices) {
|
|||||||
//assign a weight to each choice
|
//assign a weight to each choice
|
||||||
let totalWeight = 0;
|
let totalWeight = 0;
|
||||||
choices = choices.map(choice => {
|
choices = choices.map(choice => {
|
||||||
//multiplying by 10 makes around 13 votes the point where it the votes start not mattering as much (10 + 3)
|
|
||||||
//The 3 makes -2 the minimum votes before being ignored completely
|
//The 3 makes -2 the minimum votes before being ignored completely
|
||||||
//https://www.desmos.com/calculator/ljftxolg9j
|
//https://www.desmos.com/calculator/c1duhfrmts
|
||||||
//this can be changed if this system increases in popularity.
|
//this can be changed if this system increases in popularity.
|
||||||
const weight = Math.sqrt((choice.votes + 3) * 10);
|
const weight = Math.exp((choice.votes + 3), 0.85);
|
||||||
totalWeight += weight;
|
totalWeight += weight;
|
||||||
|
|
||||||
return { ...choice, weight };
|
return { ...choice, weight };
|
||||||
|
|||||||
@@ -42,11 +42,13 @@ module.exports = function getTopUsers (req, res) {
|
|||||||
|
|
||||||
let rows = db.prepare('all', "SELECT COUNT(*) as totalSubmissions, SUM(views) as viewCount," +
|
let rows = db.prepare('all', "SELECT COUNT(*) as totalSubmissions, SUM(views) as viewCount," +
|
||||||
"SUM((sponsorTimes.endTime - sponsorTimes.startTime) / 60 * sponsorTimes.views) as minutesSaved, " +
|
"SUM((sponsorTimes.endTime - sponsorTimes.startTime) / 60 * sponsorTimes.views) as minutesSaved, " +
|
||||||
|
"SUM(votes) as userVotes, " +
|
||||||
additionalFields +
|
additionalFields +
|
||||||
"IFNULL(userNames.userName, sponsorTimes.userID) as userName FROM sponsorTimes LEFT JOIN userNames ON sponsorTimes.userID=userNames.userID " +
|
"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 " +
|
"LEFT JOIN privateDB.shadowBannedUsers ON sponsorTimes.userID=privateDB.shadowBannedUsers.userID " +
|
||||||
"WHERE sponsorTimes.votes > -1 AND sponsorTimes.shadowHidden != 1 AND privateDB.shadowBannedUsers.userID IS NULL " +
|
"WHERE sponsorTimes.votes > -1 AND sponsorTimes.shadowHidden != 1 AND privateDB.shadowBannedUsers.userID IS NULL " +
|
||||||
"GROUP BY IFNULL(userName, sponsorTimes.userID) ORDER BY " + sortBy + " DESC LIMIT 100", []);
|
"GROUP BY IFNULL(userName, sponsorTimes.userID) HAVING userVotes > 20 " +
|
||||||
|
"ORDER BY " + sortBy + " DESC LIMIT 100", []);
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
userNames[i] = rows[i].userName;
|
userNames[i] = rows[i].userName;
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
var db = require('../databases/databases.js').db;
|
const db = require('../databases/databases.js').db;
|
||||||
var request = require('request');
|
const request = require('request');
|
||||||
|
const config = require('../config.js');
|
||||||
|
|
||||||
// A cache of the number of chrome web store users
|
// A cache of the number of chrome web store users
|
||||||
var chromeUsersCache = null;
|
let chromeUsersCache = null;
|
||||||
var firefoxUsersCache = null;
|
let firefoxUsersCache = null;
|
||||||
var lastUserCountCheck = 0;
|
|
||||||
|
|
||||||
|
// By the privacy friendly user counter
|
||||||
|
let apiUsersCache = null;
|
||||||
|
|
||||||
|
let lastUserCountCheck = 0;
|
||||||
|
|
||||||
module.exports = function getTotalStats (req, res) {
|
module.exports = function getTotalStats (req, res) {
|
||||||
let row = db.prepare('get', "SELECT COUNT(DISTINCT userID) as userCount, COUNT(*) as totalSubmissions, " +
|
let row = db.prepare('get', "SELECT COUNT(DISTINCT userID) as userCount, COUNT(*) as totalSubmissions, " +
|
||||||
"SUM(views) as viewCount, SUM((endTime - startTime) / 60 * views) as minutesSaved FROM sponsorTimes WHERE shadowHidden != 1", []);
|
"SUM(views) as viewCount, SUM((endTime - startTime) / 60 * views) as minutesSaved FROM sponsorTimes WHERE shadowHidden != 1 AND votes >= 0", []);
|
||||||
|
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
//send this result
|
//send this result
|
||||||
res.send({
|
res.send({
|
||||||
userCount: row.userCount,
|
userCount: row.userCount,
|
||||||
activeUsers: chromeUsersCache + firefoxUsersCache,
|
activeUsers: chromeUsersCache + firefoxUsersCache,
|
||||||
|
apiUsers: apiUsersCache,
|
||||||
viewCount: row.viewCount,
|
viewCount: row.viewCount,
|
||||||
totalSubmissions: row.totalSubmissions,
|
totalSubmissions: row.totalSubmissions,
|
||||||
minutesSaved: row.minutesSaved
|
minutesSaved: row.minutesSaved
|
||||||
@@ -26,7 +31,18 @@ module.exports = function getTotalStats (req, res) {
|
|||||||
if (now - lastUserCountCheck > 5000000) {
|
if (now - lastUserCountCheck > 5000000) {
|
||||||
lastUserCountCheck = now;
|
lastUserCountCheck = now;
|
||||||
|
|
||||||
// Get total users
|
updateExtensionUsers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateExtensionUsers() {
|
||||||
|
if (config.userCounterURL) {
|
||||||
|
request.get(config.userCounterURL + "/api/v1/userCount", (err, response, body) => {
|
||||||
|
apiUsersCache = Math.max(apiUsersCache, JSON.parse(body).userCount);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
request.get("https://addons.mozilla.org/api/v3/addons/addon/sponsorblock/", function (err, firefoxResponse, body) {
|
request.get("https://addons.mozilla.org/api/v3/addons/addon/sponsorblock/", function (err, firefoxResponse, body) {
|
||||||
try {
|
try {
|
||||||
firefoxUsersCache = parseInt(JSON.parse(body).average_daily_users);
|
firefoxUsersCache = parseInt(JSON.parse(body).average_daily_users);
|
||||||
@@ -49,5 +65,3 @@ module.exports = function getTotalStats (req, res) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
74
src/routes/postNoSegments.js
Normal file
74
src/routes/postNoSegments.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
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');
|
||||||
|
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
// Collect user input data
|
||||||
|
let videoID = req.body.videoID;
|
||||||
|
let userID = req.body.userID;
|
||||||
|
let categories = req.body.categories;
|
||||||
|
|
||||||
|
// Check input data is valid
|
||||||
|
if (!videoID
|
||||||
|
|| !userID
|
||||||
|
|| !categories
|
||||||
|
|| !Array.isArray(categories)
|
||||||
|
|| categories.length === 0
|
||||||
|
) {
|
||||||
|
res.status(400).json({
|
||||||
|
message: 'Bad Format'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if user is VIP
|
||||||
|
userID = getHash(userID);
|
||||||
|
let userIsVIP = isUserVIP(userID);
|
||||||
|
|
||||||
|
if (!userIsVIP) {
|
||||||
|
res.status(403).json({
|
||||||
|
message: 'Must be a VIP to mark videos.'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get existing no segment markers
|
||||||
|
let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]);
|
||||||
|
if (!noSegmentList || noSegmentList.length === 0) {
|
||||||
|
noSegmentList = [];
|
||||||
|
} else {
|
||||||
|
noSegmentList = noSegmentList.map((obj) => {
|
||||||
|
return obj.category;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// get user categories not already submitted that match accepted format
|
||||||
|
let categoriesToMark = categories.filter((category) => {
|
||||||
|
return !!category.match(/^[_a-zA-Z]+$/);
|
||||||
|
}).filter((category) => {
|
||||||
|
return noSegmentList.indexOf(category) === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove any duplicates
|
||||||
|
categoriesToMark = categoriesToMark.filter((category, index) => {
|
||||||
|
return categoriesToMark.indexOf(category) === index;
|
||||||
|
});
|
||||||
|
|
||||||
|
// create database entry
|
||||||
|
categoriesToMark.forEach((category) => {
|
||||||
|
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);
|
||||||
|
res.status(500).json({
|
||||||
|
message: "Internal Server Error: Could not write marker to the database."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
submitted: categoriesToMark
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -11,16 +11,43 @@ var isoDurations = require('iso8601-duration');
|
|||||||
var getHash = require('../utils/getHash.js');
|
var getHash = require('../utils/getHash.js');
|
||||||
var getIP = require('../utils/getIP.js');
|
var getIP = require('../utils/getIP.js');
|
||||||
var getFormattedTime = require('../utils/getFormattedTime.js');
|
var getFormattedTime = require('../utils/getFormattedTime.js');
|
||||||
var isUserTrustworthy = require('../utils/isUserTrustworthy.js')
|
var isUserTrustworthy = require('../utils/isUserTrustworthy.js');
|
||||||
|
const { dispatchEvent } = require('../utils/webhookUtils.js');
|
||||||
|
|
||||||
function sendDiscordNotification(userID, videoID, UUID, segmentInfo) {
|
function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtubeData, {submissionStart, submissionEnd}, segmentInfo) {
|
||||||
//check if they are a first time user
|
let row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]);
|
||||||
//if so, send a notification to discord
|
let userName = row !== undefined ? row.userName : null;
|
||||||
if (config.youtubeAPIKey !== null && config.discordFirstTimeSubmissionsWebhookURL !== null) {
|
let video = youtubeData.items[0];
|
||||||
|
|
||||||
|
let scopeName = "submissions.other";
|
||||||
|
if (submissionCount <= 1) {
|
||||||
|
scopeName = "submissions.new";
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchEvent(scopeName, {
|
||||||
|
"video": {
|
||||||
|
"id": videoID,
|
||||||
|
"title": video.snippet.title,
|
||||||
|
"thumbnail": video.snippet.thumbnails.maxres ? video.snippet.thumbnails.maxres : null,
|
||||||
|
"url": "https://www.youtube.com/watch?v=" + videoID
|
||||||
|
},
|
||||||
|
"submission": {
|
||||||
|
"UUID": UUID,
|
||||||
|
"category": segmentInfo.category,
|
||||||
|
"startTime": submissionStart,
|
||||||
|
"endTime": submissionEnd,
|
||||||
|
"user": {
|
||||||
|
"UUID": userID,
|
||||||
|
"username": userName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
||||||
|
if (config.youtubeAPIKey !== null) {
|
||||||
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [userID]);
|
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
// If it is a first time submission
|
|
||||||
if (userSubmissionCountRow.submissionCount <= 1) {
|
|
||||||
YouTubeAPI.videos.list({
|
YouTubeAPI.videos.list({
|
||||||
part: "snippet",
|
part: "snippet",
|
||||||
id: videoID
|
id: videoID
|
||||||
@@ -32,7 +59,11 @@ function sendDiscordNotification(userID, videoID, UUID, segmentInfo) {
|
|||||||
|
|
||||||
let startTime = parseFloat(segmentInfo.segment[0]);
|
let startTime = parseFloat(segmentInfo.segment[0]);
|
||||||
let endTime = parseFloat(segmentInfo.segment[1]);
|
let 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
|
||||||
|
if (config.discordFirstTimeSubmissionsWebhookURL === null) return;
|
||||||
request.post(config.discordFirstTimeSubmissionsWebhookURL, {
|
request.post(config.discordFirstTimeSubmissionsWebhookURL, {
|
||||||
json: {
|
json: {
|
||||||
"embeds": [{
|
"embeds": [{
|
||||||
@@ -65,7 +96,6 @@ function sendDiscordNotification(userID, videoID, UUID, segmentInfo) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// submission: {videoID, startTime, endTime}
|
// submission: {videoID, startTime, endTime}
|
||||||
// callback: function(reject: "String containing reason the submission was rejected")
|
// callback: function(reject: "String containing reason the submission was rejected")
|
||||||
@@ -118,9 +148,9 @@ function proxySubmission(req) {
|
|||||||
request.post(config.proxySubmission + '/api/skipSegments?userID='+req.query.userID+'&videoID='+req.query.videoID, {json: req.body}, (err, result) => {
|
request.post(config.proxySubmission + '/api/skipSegments?userID='+req.query.userID+'&videoID='+req.query.videoID, {json: req.body}, (err, result) => {
|
||||||
if (config.mode === 'development') {
|
if (config.mode === 'development') {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
logger.error('Proxy Submission: ' + result.statusCode + ' ('+result.body+')');
|
logger.debug('Proxy Submission: ' + result.statusCode + ' ('+result.body+')');
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Proxy Submission: Failed to make call");
|
logger.error("Proxy Submission: Failed to make call");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -157,6 +187,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
//hash the ip 5000 times so no one can get it from the database
|
//hash the ip 5000 times so no one can get it from the database
|
||||||
let hashedIP = getHash(getIP(req) + config.globalSalt);
|
let hashedIP = getHash(getIP(req) + config.globalSalt);
|
||||||
|
|
||||||
|
let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]).map((list) => { return list.category });
|
||||||
// Check if all submissions are correct
|
// Check if all submissions are correct
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) {
|
if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) {
|
||||||
@@ -165,11 +196,23 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject segemnt if it's in the no segments list
|
||||||
|
if (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 + "'");
|
||||||
|
res.status(403).send(
|
||||||
|
"Request rejected by auto moderator: This video has been reported as not containing any segments with the category '"
|
||||||
|
+ segments[i].category + "'. If you believe this is incorrect, contact someone on Discord."
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let startTime = parseFloat(segments[i].segment[0]);
|
let startTime = parseFloat(segments[i].segment[0]);
|
||||||
let endTime = parseFloat(segments[i].segment[1]);
|
let endTime = parseFloat(segments[i].segment[1]);
|
||||||
|
|
||||||
if (isNaN(startTime) || isNaN(endTime)
|
if (isNaN(startTime) || isNaN(endTime)
|
||||||
|| startTime === Infinity || endTime === Infinity || startTime > endTime) {
|
|| startTime === Infinity || endTime === Infinity || startTime < 0 || startTime >= endTime) {
|
||||||
//invalid request
|
//invalid request
|
||||||
res.sendStatus(400);
|
res.sendStatus(400);
|
||||||
return;
|
return;
|
||||||
@@ -196,6 +239,9 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Will be filled when submitting
|
||||||
|
let UUIDs = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//check if this user is on the vip list
|
//check if this user is on the vip list
|
||||||
let vipRow = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]);
|
let vipRow = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]);
|
||||||
@@ -271,8 +317,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discord notification
|
UUIDs.push(UUID);
|
||||||
sendDiscordNotification(userID, videoID, UUID, segmentInfo);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
@@ -283,4 +328,8 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
|
|
||||||
|
for (let i = 0; i < segments.length; i++) {
|
||||||
|
sendWebhooks(userID, videoID, UUIDs[i], segments[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,18 @@ module.exports = function setUsername(req, res) {
|
|||||||
|
|
||||||
let adminUserIDInput = req.query.adminUserID;
|
let adminUserIDInput = req.query.adminUserID;
|
||||||
|
|
||||||
if (userID == undefined || userName == undefined || userID === "undefined") {
|
if (userID == undefined || userName == undefined || userID === "undefined" || userName.length > 50) {
|
||||||
//invalid request
|
//invalid request
|
||||||
res.sendStatus(400);
|
res.sendStatus(400);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userName.includes("discord")) {
|
||||||
|
// Don't allow
|
||||||
|
res.sendStatus(200);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (adminUserIDInput != undefined) {
|
if (adminUserIDInput != undefined) {
|
||||||
//this is the admin controlling the other users account, don't hash the controling account's ID
|
//this is the admin controlling the other users account, don't hash the controling account's ID
|
||||||
adminUserIDInput = getHash(adminUserIDInput);
|
adminUserIDInput = getHash(adminUserIDInput);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var getHash = require('../utils/getHash.js');
|
|||||||
|
|
||||||
module.exports = async function shadowBanUser(req, res) {
|
module.exports = async function shadowBanUser(req, res) {
|
||||||
let userID = req.query.userID;
|
let userID = req.query.userID;
|
||||||
|
let hashedIP = req.query.hashedIP;
|
||||||
let adminUserIDInput = req.query.adminUserID;
|
let adminUserIDInput = req.query.adminUserID;
|
||||||
|
|
||||||
let enabled = req.query.enabled;
|
let enabled = req.query.enabled;
|
||||||
@@ -20,7 +21,7 @@ module.exports = async function shadowBanUser(req, res) {
|
|||||||
//if enabled is false and the old submissions should be made visible again
|
//if enabled is false and the old submissions should be made visible again
|
||||||
let unHideOldSubmissions = req.query.unHideOldSubmissions !== "false";
|
let unHideOldSubmissions = req.query.unHideOldSubmissions !== "false";
|
||||||
|
|
||||||
if (adminUserIDInput == undefined || userID == undefined) {
|
if (adminUserIDInput == undefined || (userID == undefined && hashedIP == undefined)) {
|
||||||
//invalid request
|
//invalid request
|
||||||
res.sendStatus(400);
|
res.sendStatus(400);
|
||||||
return;
|
return;
|
||||||
@@ -35,6 +36,7 @@ module.exports = async function shadowBanUser(req, res) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userID) {
|
||||||
//check to see if this user is already shadowbanned
|
//check to see if this user is already shadowbanned
|
||||||
let row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
let row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]);
|
||||||
|
|
||||||
@@ -57,6 +59,35 @@ module.exports = async function shadowBanUser(req, res) {
|
|||||||
db.prepare('run', "UPDATE sponsorTimes SET shadowHidden = 0 WHERE userID = ?", [userID]);
|
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);
|
res.sendStatus(200);
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,8 @@ var config = require('../config.js');
|
|||||||
var getHash = require('../utils/getHash.js');
|
var getHash = require('../utils/getHash.js');
|
||||||
var getIP = require('../utils/getIP.js');
|
var getIP = require('../utils/getIP.js');
|
||||||
var getFormattedTime = require('../utils/getFormattedTime.js');
|
var getFormattedTime = require('../utils/getFormattedTime.js');
|
||||||
var isUserTrustworthy = require('../utils/isUserTrustworthy.js')
|
var isUserTrustworthy = require('../utils/isUserTrustworthy.js');
|
||||||
|
const { getVoteAuthor, getVoteAuthorRaw, dispatchEvent } = require('../utils/webhookUtils.js');
|
||||||
|
|
||||||
var databases = require('../databases/databases.js');
|
var databases = require('../databases/databases.js');
|
||||||
var db = databases.db;
|
var db = databases.db;
|
||||||
@@ -13,6 +14,126 @@ var YouTubeAPI = require('../utils/youtubeAPI.js');
|
|||||||
var request = require('request');
|
var request = require('request');
|
||||||
const logger = require('../utils/logger.js');
|
const logger = require('../utils/logger.js');
|
||||||
|
|
||||||
|
const voteTypes = {
|
||||||
|
normal: 0,
|
||||||
|
incorrect: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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, " +
|
||||||
|
"(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]);
|
||||||
|
|
||||||
|
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [voteData.nonAnonUserID]);
|
||||||
|
|
||||||
|
if (submissionInfoRow !== undefined && userSubmissionCountRow != undefined) {
|
||||||
|
let webhookURL = null;
|
||||||
|
if (voteData.voteTypeEnum === voteTypes.normal) {
|
||||||
|
webhookURL = config.discordReportChannelWebhookURL;
|
||||||
|
} else if (voteData.voteTypeEnum === voteTypes.incorrect) {
|
||||||
|
webhookURL = config.discordCompletelyIncorrectReportWebhookURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.youtubeAPIKey !== null) {
|
||||||
|
YouTubeAPI.videos.list({
|
||||||
|
part: "snippet",
|
||||||
|
id: submissionInfoRow.videoID
|
||||||
|
}, function (err, data) {
|
||||||
|
if (err || data.items.length === 0) {
|
||||||
|
err && logger.error(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let isUpvote = voteData.incrementAmount > 0;
|
||||||
|
// Send custom webhooks
|
||||||
|
dispatchEvent(isUpvote ? "vote.up" : "vote.down", {
|
||||||
|
"user": {
|
||||||
|
"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 : ""
|
||||||
|
},
|
||||||
|
"submission": {
|
||||||
|
"UUID": voteData.UUID,
|
||||||
|
"views": voteData.row.views,
|
||||||
|
"category": voteData.category,
|
||||||
|
"startTime": submissionInfoRow.startTime,
|
||||||
|
"endTime": submissionInfoRow.endTime,
|
||||||
|
"user": {
|
||||||
|
"UUID": submissionInfoRow.userID,
|
||||||
|
"username": submissionInfoRow.userName,
|
||||||
|
"submissions": {
|
||||||
|
"total": submissionInfoRow.count,
|
||||||
|
"ignored": submissionInfoRow.disregarded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"votes": {
|
||||||
|
"before": voteData.row.votes,
|
||||||
|
"after": (voteData.row.votes + voteData.incrementAmount - voteData.oldIncrementAmount)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send discord message
|
||||||
|
if (webhookURL !== null && !isUpvote) {
|
||||||
|
request.post(webhookURL, {
|
||||||
|
json: {
|
||||||
|
"embeds": [{
|
||||||
|
"title": data.items[0].snippet.title,
|
||||||
|
"url": "https://www.youtube.com/watch?v=" + submissionInfoRow.videoID
|
||||||
|
+ "&t=" + (submissionInfoRow.startTime.toFixed(0) - 2),
|
||||||
|
"description": "**" + voteData.row.votes + " Votes Prior | " +
|
||||||
|
(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),
|
||||||
|
"color": 10813440,
|
||||||
|
"author": {
|
||||||
|
"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");
|
||||||
|
} else if (res && res.statusCode >= 400) {
|
||||||
|
logger.error("Error sending reported submission Discord hook");
|
||||||
|
logger.error(JSON.stringify(res));
|
||||||
|
logger.error("\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
|
||||||
// Check if they've already made a vote
|
// Check if they've already made a vote
|
||||||
let previousVoteInfo = privateDB.prepare('get', "select count(*) as votes, category from categoryVotes where UUID = ? and userID = ?", [UUID, userID]);
|
let previousVoteInfo = privateDB.prepare('get', "select count(*) as votes, category from categoryVotes where UUID = ? and userID = ?", [UUID, userID]);
|
||||||
@@ -97,11 +218,14 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
//check if this user is on the vip list
|
//check if this user is on the vip list
|
||||||
let isVIP = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [nonAnonUserID]).userCount > 0;
|
let 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;
|
||||||
|
|
||||||
if (type === undefined && category !== undefined) {
|
if (type === undefined && category !== undefined) {
|
||||||
return categoryVote(UUID, userID, isVIP, category, hashedIP, res);
|
return categoryVote(UUID, userID, isVIP, category, hashedIP, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == 1 && !isVIP) {
|
if (type == 1 && !isVIP && !isOwnSubmission) {
|
||||||
// Check if upvoting hidden segment
|
// Check if upvoting hidden segment
|
||||||
let voteInfo = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
let voteInfo = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||||
|
|
||||||
@@ -111,11 +235,6 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let voteTypes = {
|
|
||||||
normal: 0,
|
|
||||||
incorrect: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
let voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect;
|
let voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -166,87 +285,19 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
let row = db.prepare('get', "SELECT votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
let row = db.prepare('get', "SELECT votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]);
|
||||||
|
|
||||||
if (voteTypeEnum === voteTypes.normal) {
|
if (voteTypeEnum === voteTypes.normal) {
|
||||||
if (isVIP && incrementAmount < 0) {
|
if ((isVIP || isOwnSubmission) && incrementAmount < 0) {
|
||||||
//this user is a vip and a downvote
|
//this user is a vip and a downvote
|
||||||
incrementAmount = - (row.votes + 2 - oldIncrementAmount);
|
incrementAmount = - (row.votes + 2 - oldIncrementAmount);
|
||||||
type = incrementAmount;
|
type = incrementAmount;
|
||||||
}
|
}
|
||||||
} else if (voteTypeEnum == voteTypes.incorrect) {
|
} else if (voteTypeEnum == voteTypes.incorrect) {
|
||||||
if (isVIP) {
|
if (isVIP || isOwnSubmission) {
|
||||||
//this user is a vip and a downvote
|
//this user is a vip and a downvote
|
||||||
incrementAmount = 500 * incrementAmount;
|
incrementAmount = 500 * incrementAmount;
|
||||||
type = incrementAmount < 0 ? 12 : 13;
|
type = incrementAmount < 0 ? 12 : 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send discord message
|
|
||||||
if (incrementAmount < 0) {
|
|
||||||
// Get video ID
|
|
||||||
let submissionInfoRow = db.prepare('get', "SELECT s.videoID, s.userID, s.startTime, s.endTime, s.category, u.userName, " +
|
|
||||||
"(select count(1) from sponsorTimes where userID = s.userID) count, " +
|
|
||||||
"(select count(1) from sponsorTimes where userID = s.userID and votes <= -2) disregarded " +
|
|
||||||
"FROM sponsorTimes s left join userNames u on s.userID = u.userID where s.UUID=?",
|
|
||||||
[UUID]);
|
|
||||||
|
|
||||||
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]);
|
|
||||||
|
|
||||||
if (submissionInfoRow !== undefined && userSubmissionCountRow != undefined) {
|
|
||||||
let webhookURL = null;
|
|
||||||
if (voteTypeEnum === voteTypes.normal) {
|
|
||||||
webhookURL = config.discordReportChannelWebhookURL;
|
|
||||||
} else if (voteTypeEnum === voteTypes.incorrect) {
|
|
||||||
webhookURL = config.discordCompletelyIncorrectReportWebhookURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.youtubeAPIKey !== null && webhookURL !== null) {
|
|
||||||
YouTubeAPI.videos.list({
|
|
||||||
part: "snippet",
|
|
||||||
id: submissionInfoRow.videoID
|
|
||||||
}, function (err, data) {
|
|
||||||
if (err || data.items.length === 0) {
|
|
||||||
err && logger.error(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
request.post(webhookURL, {
|
|
||||||
json: {
|
|
||||||
"embeds": [{
|
|
||||||
"title": data.items[0].snippet.title,
|
|
||||||
"url": "https://www.youtube.com/watch?v=" + submissionInfoRow.videoID
|
|
||||||
+ "&t=" + (submissionInfoRow.startTime.toFixed(0) - 2),
|
|
||||||
"description": "**" + row.votes + " Votes Prior | " + (row.votes + incrementAmount - oldIncrementAmount) + " Votes Now | " + row.views
|
|
||||||
+ " Views**\n\n**Submission ID:** " + 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),
|
|
||||||
"color": 10813440,
|
|
||||||
"author": {
|
|
||||||
"name": userSubmissionCountRow.submissionCount === 0 ? "Report by New User" : (isVIP ? "Report by VIP User" : "")
|
|
||||||
},
|
|
||||||
"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");
|
|
||||||
} else if (res && res.statusCode >= 400) {
|
|
||||||
logger.error("Error sending reported submission Discord hook");
|
|
||||||
logger.error(JSON.stringify(res));
|
|
||||||
logger.error("\n");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only change the database if they have made a submission before and haven't voted recently
|
// Only change the database if they have made a submission before and haven't voted recently
|
||||||
let ableToVote = isVIP
|
let ableToVote = isVIP
|
||||||
|| (db.prepare("get", "SELECT userID FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]) !== undefined
|
|| (db.prepare("get", "SELECT userID FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]) !== undefined
|
||||||
@@ -299,6 +350,18 @@ async function voteOnSponsorTime(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
|
|
||||||
|
sendWebhooks({
|
||||||
|
UUID,
|
||||||
|
nonAnonUserID,
|
||||||
|
voteTypeEnum,
|
||||||
|
isVIP,
|
||||||
|
isOwnSubmission,
|
||||||
|
row,
|
||||||
|
category,
|
||||||
|
incrementAmount,
|
||||||
|
oldIncrementAmount
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
//converts time in seconds to minutes:seconds
|
//converts time in seconds to minutes:seconds
|
||||||
module.exports = function getFormattedTime(seconds) {
|
module.exports = function getFormattedTime(totalSeconds) {
|
||||||
let minutes = Math.floor(seconds / 60);
|
let minutes = Math.floor(totalSeconds / 60);
|
||||||
let secondsDisplay = Math.round(seconds - minutes * 60);
|
let seconds = totalSeconds - minutes * 60;
|
||||||
if (secondsDisplay < 10) {
|
let secondsDisplay = seconds.toFixed(3);
|
||||||
|
if (seconds < 10) {
|
||||||
//add a zero
|
//add a zero
|
||||||
secondsDisplay = "0" + secondsDisplay;
|
secondsDisplay = "0" + secondsDisplay;
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/utils/isUserVIP.js
Normal file
8
src/utils/isUserVIP.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
const databases = require('../databases/databases.js');
|
||||||
|
const db = databases.db;
|
||||||
|
|
||||||
|
module.exports = (userID) => {
|
||||||
|
return db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -7,6 +7,34 @@ const levels = {
|
|||||||
DEBUG: "DEBUG"
|
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 = {
|
const settings = {
|
||||||
ERROR: true,
|
ERROR: true,
|
||||||
WARN: true,
|
WARN: true,
|
||||||
@@ -17,12 +45,18 @@ const settings = {
|
|||||||
if (config.mode === 'development') {
|
if (config.mode === 'development') {
|
||||||
settings.INFO = true;
|
settings.INFO = true;
|
||||||
settings.DEBUG = true;
|
settings.DEBUG = true;
|
||||||
|
} else if (config.mode === 'test') {
|
||||||
|
settings.WARN = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function log(level, string) {
|
function log(level, string) {
|
||||||
if (!!settings[level]) {
|
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
|
if (level.length === 4) {level = level + " "}; // ensure logs are aligned
|
||||||
console.log(level + " " + new Date().toISOString() + " : " + string);
|
console.log(colors.Dim, level + " " + new Date().toISOString() + ": ", color, string, colors.Reset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
src/utils/webhookUtils.js
Normal file
52
src/utils/webhookUtils.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
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 (isVIP) {
|
||||||
|
return "Report by VIP User";
|
||||||
|
} else if (isOwnSubmission) {
|
||||||
|
return "Report by Submitter";
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
5
test.js
5
test.js
@@ -9,6 +9,7 @@ if (fs.existsSync(config.privateDB)) fs.unlinkSync(config.privateDB);
|
|||||||
|
|
||||||
var createServer = require('./src/app.js');
|
var createServer = require('./src/app.js');
|
||||||
var createMockServer = require('./test/mocks.js');
|
var createMockServer = require('./test/mocks.js');
|
||||||
|
const logger = require('./src/utils/logger.js');
|
||||||
|
|
||||||
// Instantiate a Mocha instance.
|
// Instantiate a Mocha instance.
|
||||||
var mocha = new Mocha();
|
var mocha = new Mocha();
|
||||||
@@ -27,9 +28,9 @@ fs.readdirSync(testDir).filter(function(file) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var mockServer = createMockServer(() => {
|
var mockServer = createMockServer(() => {
|
||||||
console.log("Started mock HTTP Server");
|
logger.info("Started mock HTTP Server");
|
||||||
var server = createServer(() => {
|
var server = createServer(() => {
|
||||||
console.log("Started main HTTP server");
|
logger.info("Started main HTTP server");
|
||||||
// Run the tests.
|
// Run the tests.
|
||||||
mocha.run(function(failures) {
|
mocha.run(function(failures) {
|
||||||
mockServer.close();
|
mockServer.close();
|
||||||
|
|||||||
33
test.json
33
test.json
@@ -15,5 +15,36 @@
|
|||||||
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
||||||
"privateDBSchema": "./databases/_private.db.sql",
|
"privateDBSchema": "./databases/_private.db.sql",
|
||||||
"mode": "test",
|
"mode": "test",
|
||||||
"readOnly": false
|
"readOnly": false,
|
||||||
|
"webhooks": [
|
||||||
|
{
|
||||||
|
"url": "http://127.0.0.1:8081/CustomWebhook",
|
||||||
|
"key": "superSecretKey",
|
||||||
|
"scopes": [
|
||||||
|
"vote.up",
|
||||||
|
"vote.down"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"url": "http://127.0.0.1:8081/FailedWebhook",
|
||||||
|
"key": "superSecretKey",
|
||||||
|
"scopes": [
|
||||||
|
"vote.up",
|
||||||
|
"vote.down"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"url": "http://127.0.0.1:8099/WrongPort",
|
||||||
|
"key": "superSecretKey",
|
||||||
|
"scopes": [
|
||||||
|
"vote.up",
|
||||||
|
"vote.down"
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"url": "http://unresolvable.host:8081/FailedWebhook",
|
||||||
|
"key": "superSecretKey",
|
||||||
|
"scopes": [
|
||||||
|
"vote.up",
|
||||||
|
"vote.down"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
57
test/cases/getIsUserVIP.js
Normal file
57
test/cases/getIsUserVIP.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
408
test/cases/noSegmentRecords.js
Normal file
408
test/cases/noSegmentRecords.js
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
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')");
|
||||||
|
});
|
||||||
|
|
||||||
|
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 that 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -20,6 +20,8 @@ describe('voteOnSponsorTime', () => {
|
|||||||
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-9', '" + getHash("randomID2") + "', 0, 50, 'sponsor', 0)");
|
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-9', '" + getHash("randomID2") + "', 0, 50, 'sponsor', 0)");
|
||||||
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-10', '" + getHash("randomID3") + "', 0, 50, 'sponsor', 0)");
|
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-10', '" + getHash("randomID3") + "', 0, 50, 'sponsor', 0)");
|
||||||
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-11', '" + getHash("randomID4") + "', 0, 50, 'sponsor', 0)");
|
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-11', '" + getHash("randomID4") + "', 0, 50, 'sponsor', 0)");
|
||||||
|
db.exec(startOfQuery + "('own-submission-video', 1, 11, 500, 'own-submission-uuid', '"+ getHash('own-submission-id') +"', 0, 50, 'sponsor', 0)");
|
||||||
|
db.exec(startOfQuery + "('not-own-submission-video', 1, 11, 500, 'not-own-submission-uuid', '"+ getHash('somebody-else-id') +"', 0, 50, 'sponsor', 0)");
|
||||||
|
|
||||||
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser") + "')");
|
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser") + "')");
|
||||||
privateDB.exec("INSERT INTO shadowBannedUsers (userID) VALUES ('" + getHash("randomID4") + "')");
|
privateDB.exec("INSERT INTO shadowBannedUsers (userID) VALUES ('" + getHash("randomID4") + "')");
|
||||||
@@ -151,6 +153,42 @@ describe('voteOnSponsorTime', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to completely downvote your own segment', (done) => {
|
||||||
|
request.get(utils.getbaseURL()
|
||||||
|
+ "/api/voteOnSponsorTime?userID=own-submission-id&UUID=own-submission-uuid&type=0", null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done(err);
|
||||||
|
else if (res.statusCode === 200) {
|
||||||
|
let row = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", ["own-submission-uuid"]);
|
||||||
|
if (row.votes <= -2) {
|
||||||
|
done()
|
||||||
|
} else {
|
||||||
|
done("Vote did not succeed. Submission went from 500 votes to " + row.votes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done("Status code was " + res.statusCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not be able to completely downvote somebody elses segment', (done) => {
|
||||||
|
request.get(utils.getbaseURL()
|
||||||
|
+ "/api/voteOnSponsorTime?userID=randomID2&UUID=not-own-submission-uuid&type=0", null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done(err);
|
||||||
|
else if (res.statusCode === 200) {
|
||||||
|
let row = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", ["not-own-submission-uuid"]);
|
||||||
|
if (row.votes === 499) {
|
||||||
|
done()
|
||||||
|
} else {
|
||||||
|
done("Vote did not succeed. Submission went from 500 votes to " + row.votes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done("Status code was " + res.statusCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Should be able to vote for a category and it should immediately change (for now)', (done) => {
|
it('Should be able to vote for a category and it should immediately change (for now)', (done) => {
|
||||||
request.get(utils.getbaseURL()
|
request.get(utils.getbaseURL()
|
||||||
+ "/api/voteOnSponsorTime?userID=randomID2&UUID=vote-uuid-4&category=intro", null,
|
+ "/api/voteOnSponsorTime?userID=randomID2&UUID=vote-uuid-4&category=intro", null,
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ app.post('/CompletelyIncorrectReportWebhook', (req, res) => {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.post('/CustomWebhook', (req, res) => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = function createMockServer(callback) {
|
module.exports = function createMockServer(callback) {
|
||||||
return app.listen(config.mockPort, callback);
|
return app.listen(config.mockPort, callback);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user