From d5a720fa0c895e4f31c2474109e39bc7e92dc374 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 2 Aug 2020 11:58:11 -0400 Subject: [PATCH 01/58] Check that start time is not less than zero --- src/routes/postSkipSegments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index f575e53..acecbc2 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -148,7 +148,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 > endTime) { + || startTime === Infinity || endTime === Infinity || startTime < 0 || startTime > endTime) { //invalid request res.sendStatus(400); return; From 25166348562f2281e0cdd77138eb6c7e8c61839a Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 2 Aug 2020 12:08:44 -0400 Subject: [PATCH 02/58] Don't allow 0 ms submissions --- src/routes/postSkipSegments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index acecbc2..2aac4ea 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -148,7 +148,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.sendStatus(400); return; From 16c68dd51d5f1066a925438e1726190987f99a3a Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Fri, 21 Aug 2020 22:32:34 +0100 Subject: [PATCH 03/58] allow removing own segments with a downvote --- index.js | 3 ++- src/databases/Mysql.js | 2 +- src/routes/voteOnSponsorTime.js | 7 ++++-- test/cases/voteOnSponsorTime.js | 38 +++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 508c089..a877bab 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ var config = require('./src/config.js'); var createServer = require('./src/app.js'); +const logger = require('./src/utils/logger.js'); var server = createServer(() => { - console.log("Server started on port " + config.port + "."); + logger.info("Server started on port " + config.port + "."); }); diff --git a/src/databases/Mysql.js b/src/databases/Mysql.js index 40e3d76..8f3d8ba 100644 --- a/src/databases/Mysql.js +++ b/src/databases/Mysql.js @@ -1,6 +1,6 @@ var MysqlInterface = require('sync-mysql'); var config = require('../config.js'); -var logger = require('../utils/logger.js'); +const logger = require('../utils/logger.js'); class Mysql { constructor(msConfig) { diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 0e9fdf8..1e55ed9 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -97,6 +97,9 @@ async function voteOnSponsorTime(req, res) { //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; + //check if user voting on own submission + let isOwnSubmission = !!db.prepare('all', 'SELECT UUID as submissionCount FROM sponsorTimes where userID = ? AND UUID = ?', [nonAnonUserID, UUID]).length; + if (type === undefined && category !== undefined) { return categoryVote(UUID, userID, isVIP, category, hashedIP, res); } @@ -166,13 +169,13 @@ async function voteOnSponsorTime(req, res) { let row = db.prepare('get', "SELECT votes, views FROM sponsorTimes WHERE UUID = ?", [UUID]); if (voteTypeEnum === voteTypes.normal) { - if (isVIP && incrementAmount < 0) { + if ((isVIP || isOwnSubmission) && incrementAmount < 0) { //this user is a vip and a downvote incrementAmount = - (row.votes + 2 - oldIncrementAmount); type = incrementAmount; } } else if (voteTypeEnum == voteTypes.incorrect) { - if (isVIP) { + if (isVIP || isOwnSubmission) { //this user is a vip and a downvote incrementAmount = 500 * incrementAmount; type = incrementAmount < 0 ? 12 : 13; diff --git a/test/cases/voteOnSponsorTime.js b/test/cases/voteOnSponsorTime.js index 1ce4b7c..bcbca78 100644 --- a/test/cases/voteOnSponsorTime.js +++ b/test/cases/voteOnSponsorTime.js @@ -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-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 + "('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") + "')"); 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) => { request.get(utils.getbaseURL() + "/api/voteOnSponsorTime?userID=randomID2&UUID=vote-uuid-4&category=intro", null, From 44ea0c418a6d34490fc84c0145534b048401c12f Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 02:14:19 +0100 Subject: [PATCH 04/58] fixed db update, started no segments --- databases/_upgrade_sponsorTimes_2.sql | 13 ++ index.js | 3 +- src/app.js | 4 + src/databases/databases.js | 11 +- src/routes/postNoSegments.js | 31 +++++ src/routes/voteOnSponsorTime.js | 2 +- src/utils/isUserVIP.js | 8 ++ src/utils/logger.js | 2 + test.js | 5 +- test/cases/postNoSegments.js | 175 ++++++++++++++++++++++++++ 10 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 databases/_upgrade_sponsorTimes_2.sql create mode 100644 src/routes/postNoSegments.js create mode 100644 src/utils/isUserVIP.js create mode 100644 test/cases/postNoSegments.js diff --git a/databases/_upgrade_sponsorTimes_2.sql b/databases/_upgrade_sponsorTimes_2.sql new file mode 100644 index 0000000..46c0fad --- /dev/null +++ b/databases/_upgrade_sponsorTimes_2.sql @@ -0,0 +1,13 @@ +BEGIN TRANSACTION; + +/* Add incorrectVotes field */ +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; \ No newline at end of file diff --git a/index.js b/index.js index 508c089..a877bab 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ var config = require('./src/config.js'); var createServer = require('./src/app.js'); +const logger = require('./src/utils/logger.js'); var server = createServer(() => { - console.log("Server started on port " + config.port + "."); + logger.info("Server started on port " + config.port + "."); }); diff --git a/src/app.js b/src/app.js index 1ce1674..ef6c554 100644 --- a/src/app.js +++ b/src/app.js @@ -21,6 +21,7 @@ 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 postNoSegments = require('./routes/postNoSegments.js'); // Old Routes var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js'); @@ -86,6 +87,9 @@ app.get('/api/getTotalStats', getTotalStats); //send out a formatted time saved total app.get('/api/getDaysSavedFormatted', getDaysSavedFormatted); +//submit video containing no segments +app.post('/api/postNoSegments', postNoSegments); + app.get('/database.db', function (req, res) { res.sendFile("./databases/sponsorTimes.db", { root: "./" }); }); diff --git a/src/databases/databases.js b/src/databases/databases.js index 1a1b448..a3aeb59 100644 --- a/src/databases/databases.js +++ b/src/databases/databases.js @@ -3,7 +3,8 @@ var Sqlite3 = require('better-sqlite3'); var fs = require('fs'); var path = require('path'); var Sqlite = require('./Sqlite.js') -var Mysql = require('./Mysql.js') +var Mysql = require('./Mysql.js'); +const logger = require('../utils/logger.js'); let options = { readonly: config.readOnly, @@ -60,12 +61,16 @@ if (config.mysql) { let versionCodeInfo = db.prepare("SELECT value FROM config WHERE key = ?").get("version"); 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)) { + 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 + "_" + (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); } } \ No newline at end of file diff --git a/src/routes/postNoSegments.js b/src/routes/postNoSegments.js new file mode 100644 index 0000000..58074b8 --- /dev/null +++ b/src/routes/postNoSegments.js @@ -0,0 +1,31 @@ +const getHash = require('../utils/getHash.js'); +const isUserVIP = require('../utils/isUserVIP.js'); + +module.exports = (req, res) => { + // Collect user input data + let videoID = req.body.videoID; + let userID = req.body.userID; + let categorys = req.body.categorys; + + // Check input data is valid + if (!videoID + || !userID + || !categorys + || !Array.isArray(categorys) + || categorys.length === 0 + ) { + res.status(400).json({}); + return; + } + + // Check if user is VIP + userID = getHash(userID); + let userIsVIP = isUserVIP(userID); + + if (!userIsVIP) { + res.status(403).json({}); + return; + } + + res.status(200).json({status: 200}); +}; \ No newline at end of file diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 0e9fdf8..9ce6afa 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -4,7 +4,7 @@ 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') +var isUserTrustworthy = require('../utils/isUserTrustworthy.js'); var databases = require('../databases/databases.js'); var db = databases.db; diff --git a/src/utils/isUserVIP.js b/src/utils/isUserVIP.js new file mode 100644 index 0000000..eeb25dc --- /dev/null +++ b/src/utils/isUserVIP.js @@ -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; +} + + diff --git a/src/utils/logger.js b/src/utils/logger.js index 70fe13e..351e45c 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -17,6 +17,8 @@ const settings = { if (config.mode === 'development') { settings.INFO = true; settings.DEBUG = true; +} else if (config.mode === 'test') { + settings.DEBUG = true; } function log(level, string) { diff --git a/test.js b/test.js index 45ace90..9810786 100644 --- a/test.js +++ b/test.js @@ -9,6 +9,7 @@ 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(); @@ -27,9 +28,9 @@ fs.readdirSync(testDir).filter(function(file) { }); var mockServer = createMockServer(() => { - console.log("Started mock HTTP Server"); + logger.info("Started mock HTTP Server"); var server = createServer(() => { - console.log("Started main HTTP server"); + logger.info("Started main HTTP server"); // Run the tests. mocha.run(function(failures) { mockServer.close(); diff --git a/test/cases/postNoSegments.js b/test/cases/postNoSegments.js new file mode 100644 index 0000000..5ab2b3d --- /dev/null +++ b/test/cases/postNoSegments.js @@ -0,0 +1,175 @@ +var request = require('request'); + +var utils = require('../utils.js'); +const getHash = require('../../src/utils/getHash.js'); + +var databases = require('../../src/databases/databases.js'); +var db = databases.db; + +describe('postNoSegments', () => { + before(() => { + db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser-noSegments") + "')"); + }); + + 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 no segments', (done) => { + let json = { + videoID: 'noSegmentsTestVideoID', + userID: 'VIPUser-noSegments', + categorys: [ + 'sponsor' + ] + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {json}, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + //let row = db.prepare('get', "SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?", ["noSegmentsTestVideoID"]); + //if (row.startTime === 2 && row.endTime === 10 && row.category === "sponsor") { + done() + //} else { + // done("Submitted times were not saved. Actual submission: " + JSON.stringify(row)); + //} + } else { + console.log(body); + done("Status code was " + res.statusCode); + } + }); + }); + + it('Should return 400 for missing params', (done) => { + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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 categorys', (done) => { + let json = { + videoID: 'test', + userID: 'test', + categorys: [] + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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, + categorys: ['sponsor'] + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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', + categorys: ['sponsor'] + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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 categorys)', (done) => { + let json = { + videoID: 'test', + userID: 'test', + categorys: {} + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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 categorys', (done) => { + let json = { + videoID: 'test', + userID: 'test', + categorys: 'sponsor' + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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', + categorys: [ + 'sponsor' + ] + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {json}, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 403) { + done() + } else { + done("Status code was " + res.statusCode); + } + }); + }); +}); \ No newline at end of file From f53c541538c613254ba620fc7dc46ee4d713bbf8 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 03:05:51 +0100 Subject: [PATCH 05/58] added ability for vips to submit segments not in a video --- src/routes/getSkipSegments.js | 1 - src/routes/postNoSegments.js | 33 ++++++++++++++++++++++++++++++++- test/cases/postNoSegments.js | 33 +++++++++++++++++++++++---------- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/routes/getSkipSegments.js b/src/routes/getSkipSegments.js index ba23262..7b68d3f 100644 --- a/src/routes/getSkipSegments.js +++ b/src/routes/getSkipSegments.js @@ -1,4 +1,3 @@ -var fs = require('fs'); var config = require('../config.js'); var databases = require('../databases/databases.js'); diff --git a/src/routes/postNoSegments.js b/src/routes/postNoSegments.js index 58074b8..f2e681b 100644 --- a/src/routes/postNoSegments.js +++ b/src/routes/postNoSegments.js @@ -1,5 +1,7 @@ +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 @@ -27,5 +29,34 @@ module.exports = (req, res) => { return; } - res.status(200).json({status: 200}); + // 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 categorys not already submitted + let categorysToMark = categorys.filter((category) => { + return noSegmentList.indexOf(category) === -1; + }); + + // remove any duplicates + categorysToMark = categorysToMark.filter((category, index) => { + return categorysToMark.indexOf(category) === index; + }); + + // create database entry + categorysToMark.forEach((category) => { + db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]); + //ogger.debug('submitting ' + category); + }); + + res.status(200).json({ + status: 200, + submitted: categorysToMark + }); }; \ No newline at end of file diff --git a/test/cases/postNoSegments.js b/test/cases/postNoSegments.js index 5ab2b3d..7d88e7d 100644 --- a/test/cases/postNoSegments.js +++ b/test/cases/postNoSegments.js @@ -9,6 +9,9 @@ var db = databases.db; describe('postNoSegments', () => { 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')"); }); it('should update the database version when starting the application', (done) => { @@ -17,12 +20,23 @@ describe('postNoSegments', () => { else done('Version isn\'t greater that 1. Version is ' + version); }); - it('Should be able to submit no segments', (done) => { + it('Should be able to submit categorys not in video', (done) => { let json = { - videoID: 'noSegmentsTestVideoID', + videoID: 'no-segments-video-id', userID: 'VIPUser-noSegments', categorys: [ - 'sponsor' + 'outro', + 'shilling', + 'shilling', + 'intro' + ] + }; + + let expected = { + status: 200, + submitted: [ + 'outro', + 'shilling' ] }; @@ -31,12 +45,11 @@ describe('postNoSegments', () => { (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { - //let row = db.prepare('get', "SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?", ["noSegmentsTestVideoID"]); - //if (row.startTime === 2 && row.endTime === 10 && row.category === "sponsor") { - done() - //} else { - // done("Submitted times were not saved. Actual submission: " + JSON.stringify(row)); - //} + 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); @@ -166,7 +179,7 @@ describe('postNoSegments', () => { (err, res, body) => { if (err) done(err); else if (res.statusCode === 403) { - done() + done(); } else { done("Status code was " + res.statusCode); } From db3de8ce6f140cbbd26ac97eb87d9e5b3ea602bd Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 03:21:10 +0100 Subject: [PATCH 06/58] changed copy/pasted comment --- databases/_upgrade_sponsorTimes_2.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/databases/_upgrade_sponsorTimes_2.sql b/databases/_upgrade_sponsorTimes_2.sql index 46c0fad..f6816bc 100644 --- a/databases/_upgrade_sponsorTimes_2.sql +++ b/databases/_upgrade_sponsorTimes_2.sql @@ -1,6 +1,6 @@ BEGIN TRANSACTION; -/* Add incorrectVotes field */ +/* Add new table: noSegments */ CREATE TABLE "noSegments" ( "videoID" TEXT NOT NULL, "userID" TEXT NOT NULL, From 6bde59c14ad0e6ede742c5928aefea5e0f6a0464 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 03:29:16 +0100 Subject: [PATCH 07/58] added extra sql test --- test/cases/postNoSegments.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/test/cases/postNoSegments.js b/test/cases/postNoSegments.js index 7d88e7d..7f89b77 100644 --- a/test/cases/postNoSegments.js +++ b/test/cases/postNoSegments.js @@ -12,6 +12,9 @@ describe('postNoSegments', () => { 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')"); }); it('should update the database version when starting the application', (done) => { @@ -20,7 +23,7 @@ describe('postNoSegments', () => { else done('Version isn\'t greater that 1. Version is ' + version); }); - it('Should be able to submit categorys not in video', (done) => { + it('Should be able to submit categorys not in video (http response)', (done) => { let json = { videoID: 'no-segments-video-id', userID: 'VIPUser-noSegments', @@ -57,6 +60,37 @@ describe('postNoSegments', () => { }); }); + it('Should be able to submit categorys not in video (sql check)', (done) => { + let json = { + videoID: 'no-segments-video-id-1', + userID: 'VIPUser-noSegments', + categorys: [ + 'outro', + 'shilling', + 'shilling', + 'intro' + ] + }; + + request.post(utils.getbaseURL() + + "/api/postNoSegments", {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 return 400 for missing params', (done) => { request.post(utils.getbaseURL() + "/api/postNoSegments", {json: {}}, From c946d2309ed8e687fc977286a91a2cbdcc117cbe Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 03:58:27 +0100 Subject: [PATCH 08/58] add is VIP endpoint --- src/app.js | 5 ++++ src/routes/getIsUserVIP.js | 31 +++++++++++++++++++++ test/cases/getIsUserVIP.js | 57 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/routes/getIsUserVIP.js create mode 100644 test/cases/getIsUserVIP.js diff --git a/src/app.js b/src/app.js index ef6c554..8617566 100644 --- a/src/app.js +++ b/src/app.js @@ -22,6 +22,7 @@ var getTopUsers = require('./routes/getTopUsers.js'); var getTotalStats = require('./routes/getTotalStats.js'); var getDaysSavedFormatted = require('./routes/getDaysSavedFormatted.js'); var postNoSegments = require('./routes/postNoSegments.js'); +var getIsUserVIP = require('./routes/getIsUserVIP.js'); // Old Routes var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js'); @@ -90,6 +91,10 @@ app.get('/api/getDaysSavedFormatted', getDaysSavedFormatted); //submit video containing no segments app.post('/api/postNoSegments', postNoSegments); +//get if user is a vip +app.get('/api/getIsUserVIP', getIsUserVIP); + + app.get('/database.db', function (req, res) { res.sendFile("./databases/sponsorTimes.db", { root: "./" }); }); diff --git a/src/routes/getIsUserVIP.js b/src/routes/getIsUserVIP.js new file mode 100644 index 0000000..d38ef9d --- /dev/null +++ b/src/routes/getIsUserVIP.js @@ -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 = function getUsername (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; + } +} \ No newline at end of file diff --git a/test/cases/getIsUserVIP.js b/test/cases/getIsUserVIP.js new file mode 100644 index 0000000..0b25e7d --- /dev/null +++ b/test/cases/getIsUserVIP.js @@ -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('getSavedTimeForUser', () => { + before(() => { + db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("supertestman") + "')"); + }); + + it('Should be able to get a 200', (done) => { + request.get(utils.getbaseURL() + + "/api/getIsUserVIP?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/getIsUserVIP", 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/getIsUserVIP?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/getIsUserVIP?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"); + } + }); + }); +}); \ No newline at end of file From a971072cb8be995133d4a07c87d31a8391d8e38b Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 04:23:28 +0100 Subject: [PATCH 09/58] reject submissions that have meen marked as invalid by a vip --- src/routes/postSkipSegments.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index 1dbaf21..40a3e25 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -157,6 +157,8 @@ module.exports = async function postSkipSegments(req, res) { //hash the ip 5000 times so no one can get it from the database let hashedIP = getHash(getIP(req) + config.globalSalt); + let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]); + // Check if all submissions are correct for (let i = 0; i < segments.length; i++) { if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) { @@ -165,6 +167,14 @@ module.exports = async function postSkipSegments(req, res) { 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 + res.sendStatus(403); + return; + } + + let startTime = parseFloat(segments[i].segment[0]); let endTime = parseFloat(segments[i].segment[1]); From a5cf59d85467715bb61b108b63ed8f2ac7440dd0 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 20:39:13 +0100 Subject: [PATCH 10/58] improved error handling --- src/routes/postNoSegments.js | 26 +++++++++++++++++++++----- test/cases/postNoSegments.js | 4 ++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/routes/postNoSegments.js b/src/routes/postNoSegments.js index f2e681b..6494095 100644 --- a/src/routes/postNoSegments.js +++ b/src/routes/postNoSegments.js @@ -16,7 +16,10 @@ module.exports = (req, res) => { || !Array.isArray(categorys) || categorys.length === 0 ) { - res.status(400).json({}); + res.status(400).json({ + status: 400, + message: 'Bad Format' + }); return; } @@ -25,7 +28,10 @@ module.exports = (req, res) => { let userIsVIP = isUserVIP(userID); if (!userIsVIP) { - res.status(403).json({}); + res.status(403).json({ + status: 403, + message: 'Must be a VIP to mark videos.' + }); return; } @@ -39,8 +45,10 @@ module.exports = (req, res) => { }); } - // get user categorys not already submitted + // get user categorys not already submitted that match accepted format let categorysToMark = categorys.filter((category) => { + return !!category.match(/^[a-zA-Z]+$/); + }).filter((category) => { return noSegmentList.indexOf(category) === -1; }); @@ -51,8 +59,16 @@ module.exports = (req, res) => { // create database entry categorysToMark.forEach((category) => { - db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]); - //ogger.debug('submitting ' + 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({ + status: 500, + message: "Internal Server Error: Could not write marker to the database." + }); + } }); res.status(200).json({ diff --git a/test/cases/postNoSegments.js b/test/cases/postNoSegments.js index 7f89b77..8489f61 100644 --- a/test/cases/postNoSegments.js +++ b/test/cases/postNoSegments.js @@ -31,6 +31,8 @@ describe('postNoSegments', () => { 'outro', 'shilling', 'shilling', + 'shil ling', + '', 'intro' ] }; @@ -68,6 +70,8 @@ describe('postNoSegments', () => { 'outro', 'shilling', 'shilling', + 'shil ling', + '', 'intro' ] }; From fba25fea0fe68d9db7ec8715fbe3c51866fd2e5d Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 20:45:31 +0100 Subject: [PATCH 11/58] Update src/routes/voteOnSponsorTime.js Co-authored-by: Ajay Ramachandran --- src/routes/voteOnSponsorTime.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 1e55ed9..3841390 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -98,7 +98,7 @@ async function voteOnSponsorTime(req, res) { 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('all', 'SELECT UUID as submissionCount FROM sponsorTimes where userID = ? AND UUID = ?', [nonAnonUserID, UUID]).length; + let 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, userID, isVIP, category, hashedIP, res); @@ -314,4 +314,4 @@ module.exports = { endpoint: function (req, res) { voteOnSponsorTime(req, res); }, - }; \ No newline at end of file + }; From 17649157c10a70b288d59abad3b9dab6b8d5cfbd Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sat, 22 Aug 2020 20:53:32 +0100 Subject: [PATCH 12/58] changed dockler default config to use sqlite --- entrypoint.sh | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 5e14a9e..6215525 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,33 +5,20 @@ cd /usr/src/app cp /etc/sponsorblock/config.json . || cat < config.json { "port": 8080, - "mysql": { - "host": "127.0.0.1", - "port": 3306, - "database": "sponsorblock", - "user": "sponsorblock", - "password": "sponsorblock" - }, - "privateMysql": { - "host": "127.0.0.1", - "port": 3306, - "database": "sponsorblock_private", - "user": "sponsorblock", - "password": "sponsorblock" - }, - "globalSalt": "", - "adminUserID": "", - "youtubeAPIKey": "", + "globalSalt": "[CHANGE THIS]", + "adminUserID": "[CHANGE THIS]", + "youtubeAPIKey": null, "discordReportChannelWebhookURL": null, "discordFirstTimeSubmissionsWebhookURL": null, "discordAutoModWebhookURL": null, - "behindProxy": true, - "db": null, - "privateDB": null, + "proxySubmission": null, + "behindProxy": "X-Forwarded-For", + "db": "./databases/sponsorTimes.db", + "privateDB": "./databases/private.db", "createDatabaseIfNotExist": true, - "schemaFolder": null, - "dbSchema": null, - "privateDBSchema": null, + "schemaFolder": "./databases", + "dbSchema": "./databases/_sponsorTimes.db.sql", + "privateDBSchema": "./databases/_private.db.sql", "mode": "development", "readOnly": false } From 9cebb8769f34368f499bb7282c0a402877b0da41 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 23 Aug 2020 11:34:07 -0400 Subject: [PATCH 13/58] Add author name to discord notification --- src/routes/voteOnSponsorTime.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 3841390..f2cf152 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -13,6 +13,18 @@ var YouTubeAPI = require('../utils/youtubeAPI.js'); var request = require('request'); const logger = require('../utils/logger.js'); +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 categoryVote(UUID, userID, isVIP, category, hashedIP, res) { // 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]); @@ -227,7 +239,7 @@ async function voteOnSponsorTime(req, res) { getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime), "color": 10813440, "author": { - "name": userSubmissionCountRow.submissionCount === 0 ? "Report by New User" : (isVIP ? "Report by VIP User" : "") + "name": getVoteAuthor(userSubmissionCountRow.submissionCount, isVIP, isOwnSubmission) }, "thumbnail": { "url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "", From ece475076e4577ac210f6b9f0b4922654837e7eb Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 23 Aug 2020 12:35:57 -0400 Subject: [PATCH 14/58] Added log colors and method name --- src/middleware/logger.js | 2 +- src/utils/logger.js | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/middleware/logger.js b/src/middleware/logger.js index 32e888b..43a0c36 100644 --- a/src/middleware/logger.js +++ b/src/middleware/logger.js @@ -1,6 +1,6 @@ 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.url); + log.info("Request recieved: " + req.method + " " + req.url); next(); } \ No newline at end of file diff --git a/src/utils/logger.js b/src/utils/logger.js index 70fe13e..dd3f62c 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -7,6 +7,34 @@ const levels = { 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, @@ -21,8 +49,12 @@ if (config.mode === 'development') { 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(level + " " + new Date().toISOString() + " : " + string); + console.log(colors.Dim, level + " " + new Date().toISOString() + ": ", color, string, colors.Reset); } } From f7d162b9551c3da2bd57fa742f68fb362acb9d65 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 19:42:52 +0200 Subject: [PATCH 15/58] Add custom webhooks for votes --- src/routes/voteOnSponsorTime.js | 109 +++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index f664d40..02ffee8 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -178,41 +178,89 @@ async function voteOnSponsorTime(req, res) { } } - // 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]); + // 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]); + 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 && console.log(err); - return; - } - + 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 || config.webhooks.size !== 0)) { + YouTubeAPI.videos.list({ + part: "snippet", + id: submissionInfoRow.videoID + }, function (err, data) { + if (err || data.items.length === 0) { + err && console.log(err); + return; + } + let isUpvote = incrementAmount > 0 + // Send custom webhooks + if (config.webhooks.size !== 0) { + console.log("Dispatching webhooks"); + config.webhooks.forEach(customWebhook => { + let customWebhookURL = customWebhook.url; + let scopes = customWebhook.scopes; + let key = customWebhook.key; + if ((!isUpvote && !scopes.includes("vote.down")) || (isUpvote && !scopes.includes("vote.up"))) { + return; + } + request.post(customWebhookURL, { + json: { + "user": { + "status": userSubmissionCountRow.submissionCount === 0 ? "new" : (isVIP ? "vip" : "normal") + }, + "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": { + "id": UUID, + "views": row.views, + "category": category, + "startTime": submissionInfoRow.startTime, + "endTime": submissionInfoRow.endTime, + "user": { + "uuid": submissionInfoRow.userID, + "username": submissionInfoRow.userName, + "submissions": { + "total": submissionInfoRow.count, + "ignored": submissionInfoRow.disregarded + } + } + }, + "votes": { + "before": row.votes, + "after": (row.votes + incrementAmount - oldIncrementAmount) + } + }, + headers: { + "Authorization": key, + "Event-Type": isUpvote ? "upvote" : "downvote" + } + }); + }); + } + // 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), + + "&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 @@ -241,8 +289,9 @@ async function voteOnSponsorTime(req, res) { console.log("\n"); } }); - }); - } + } + + }); } } From 2ffed72977a1632301a5da4f6d9cac2bb97f0637 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 20:02:18 +0200 Subject: [PATCH 16/58] Fix merge conflicts --- src/routes/voteOnSponsorTime.js | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index fe49002..a5d6ace 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -194,7 +194,6 @@ async function voteOnSponsorTime(req, res) { } } -<<<<<<< HEAD // 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, " + @@ -272,37 +271,6 @@ async function voteOnSponsorTime(req, res) { } // Send discord message if (webhookURL !== null && !isUpvote) { -======= - // 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; - } - ->>>>>>> origin/master request.post(webhookURL, { json: { "embeds": [{ From 77d08d2340188acbbabb068fe9b6195d6a7a1acf Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 20:05:11 +0200 Subject: [PATCH 17/58] Use logger instead of console.log --- src/routes/voteOnSponsorTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index a5d6ace..dea5a31 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -223,7 +223,7 @@ async function voteOnSponsorTime(req, res) { let isUpvote = incrementAmount > 0 // Send custom webhooks if (config.webhooks.size !== 0) { - console.log("Dispatching webhooks"); + logger.debug("Dispatching webhooks"); config.webhooks.forEach(customWebhook => { let customWebhookURL = customWebhook.url; let scopes = customWebhook.scopes; From a268f9a892d020423a2f2ff6834f96a4f29b0d44 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 20:54:18 +0200 Subject: [PATCH 18/58] Update example & test config --- config.json.example | 3 ++- test.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config.json.example b/config.json.example index 22971d9..dea95da 100644 --- a/config.json.example +++ b/config.json.example @@ -17,5 +17,6 @@ "dbSchema": "./databases/_sponsorTimes.db.sql", "privateDBSchema": "./databases/_private.db.sql", "mode": "development", - "readOnly": false + "readOnly": false, + "webhooks": [] } diff --git a/test.json b/test.json index 28373a1..0dbb51d 100644 --- a/test.json +++ b/test.json @@ -15,5 +15,6 @@ "dbSchema": "./databases/_sponsorTimes.db.sql", "privateDBSchema": "./databases/_private.db.sql", "mode": "test", - "readOnly": false + "readOnly": false, + "webhooks": [] } From e5aa631da0188cb289bb7a17e09401c0ddfc59e3 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 21:44:50 +0200 Subject: [PATCH 19/58] Refactor custom webhooks --- src/routes/voteOnSponsorTime.js | 83 ++++++++++++++------------------- src/utils/dispatchWebhooks.js | 22 +++++++++ 2 files changed, 56 insertions(+), 49 deletions(-) create mode 100644 src/utils/dispatchWebhooks.js diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index dea5a31..1ab96b3 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -4,7 +4,9 @@ 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') +var isUserTrustworthy = require('../utils/isUserTrustworthy.js'); +const dispatchWebhooks = require('../utils/dispatchWebhooks.js') + var databases = require('../databases/databases.js'); var db = databases.db; @@ -217,58 +219,41 @@ async function voteOnSponsorTime(req, res) { id: submissionInfoRow.videoID }, function (err, data) { if (err || data.items.length === 0) { - err && console.log(err); + err && logger.error(err); return; } - let isUpvote = incrementAmount > 0 + let isUpvote = incrementAmount > 0; // Send custom webhooks - if (config.webhooks.size !== 0) { - logger.debug("Dispatching webhooks"); - config.webhooks.forEach(customWebhook => { - let customWebhookURL = customWebhook.url; - let scopes = customWebhook.scopes; - let key = customWebhook.key; - if ((!isUpvote && !scopes.includes("vote.down")) || (isUpvote && !scopes.includes("vote.up"))) { - return; - } - request.post(customWebhookURL, { - json: { - "user": { - "status": userSubmissionCountRow.submissionCount === 0 ? "new" : (isVIP ? "vip" : "normal") - }, - "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": { - "id": UUID, - "views": row.views, - "category": category, - "startTime": submissionInfoRow.startTime, - "endTime": submissionInfoRow.endTime, - "user": { - "uuid": submissionInfoRow.userID, - "username": submissionInfoRow.userName, - "submissions": { - "total": submissionInfoRow.count, - "ignored": submissionInfoRow.disregarded - } - } - }, - "votes": { - "before": row.votes, - "after": (row.votes + incrementAmount - oldIncrementAmount) - } - }, - headers: { - "Authorization": key, - "Event-Type": isUpvote ? "upvote" : "downvote" + dispatchWebhooks(isUpvote ? "vote.up" : "vote.down", { + "user": { + "status": userSubmissionCountRow.submissionCount === 0 ? "new" : (isVIP ? "vip" : "normal") + }, + "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": { + "id": UUID, + "views": row.views, + "category": category, + "startTime": submissionInfoRow.startTime, + "endTime": submissionInfoRow.endTime, + "user": { + "UUID": submissionInfoRow.userID, + "username": submissionInfoRow.userName, + "submissions": { + "total": submissionInfoRow.count, + "ignored": submissionInfoRow.disregarded } - }); - }); - } + } + }, + "votes": { + "before": row.votes, + "after": (row.votes + incrementAmount - oldIncrementAmount) + } + }); // Send discord message if (webhookURL !== null && !isUpvote) { request.post(webhookURL, { diff --git a/src/utils/dispatchWebhooks.js b/src/utils/dispatchWebhooks.js new file mode 100644 index 0000000..4872ef7 --- /dev/null +++ b/src/utils/dispatchWebhooks.js @@ -0,0 +1,22 @@ +const config = require("../config.js"); +const logger = require('../utils/logger.js'); +const request = require('request'); + + +function dispatchEvent(scope, data) { + let webhooks = config.webhooks; + if (webhooks === undefined || webhooks.size === 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? + }}); + }); +} + +module.exports = dispatchEvent; \ No newline at end of file From 7ae97e4c6969cb4151184953a8ea41cf5093560a Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 21:45:39 +0200 Subject: [PATCH 20/58] Change submission id to UUID --- src/routes/voteOnSponsorTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 1ab96b3..e003872 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -235,7 +235,7 @@ async function voteOnSponsorTime(req, res) { "thumbnail": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "" }, "submission": { - "id": UUID, + "UUID": UUID, "views": row.views, "category": category, "startTime": submissionInfoRow.startTime, From 2597a57f3a590f27c3e9b28d6a66bc25261eaa8c Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 22:02:34 +0200 Subject: [PATCH 21/58] Update user status to include self remove --- src/routes/voteOnSponsorTime.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index e003872..84c7cf5 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -224,9 +224,20 @@ async function voteOnSponsorTime(req, res) { } let isUpvote = incrementAmount > 0; // Send custom webhooks + let userStatus; + if (isOwnSubmission) { + userStatus = "self"; + } else if (isVIP) { + userStatus = "vip"; + } else if (userSubmissionCountRow.submissionCount === 0) { + userStatus = "new"; + } else { + userStatus = "other"; + } dispatchWebhooks(isUpvote ? "vote.up" : "vote.down", { + "isOwnSubmission": isOwnSubmission, "user": { - "status": userSubmissionCountRow.submissionCount === 0 ? "new" : (isVIP ? "vip" : "normal") + "status": userStatus }, "video": { "id": submissionInfoRow.videoID, From 14a990f7eaf0c2c5808cc1797684545af2bfd8e0 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 22:09:02 +0200 Subject: [PATCH 22/58] Remove duplicate isOwnSubmission --- src/routes/voteOnSponsorTime.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 84c7cf5..8e29098 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -235,7 +235,6 @@ async function voteOnSponsorTime(req, res) { userStatus = "other"; } dispatchWebhooks(isUpvote ? "vote.up" : "vote.down", { - "isOwnSubmission": isOwnSubmission, "user": { "status": userStatus }, From 20d813d2ea1b097a6abaead1daeee5708c08d6e8 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 22:13:00 +0200 Subject: [PATCH 23/58] Fix missing semicolon --- src/routes/voteOnSponsorTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 8e29098..1cf75e5 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -5,7 +5,7 @@ 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 dispatchWebhooks = require('../utils/dispatchWebhooks.js') +const dispatchWebhooks = require('../utils/dispatchWebhooks.js'); var databases = require('../databases/databases.js'); From 9eddc330c5984dbb5b1e8abff43f1f9c494ef0a3 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 22:30:35 +0200 Subject: [PATCH 24/58] Move getVoteAuthor to webhookUtils and move user status (api) to webhookUtils --- src/routes/voteOnSponsorTime.js | 24 ++---------------------- src/utils/webhookUtils.js | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 src/utils/webhookUtils.js diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 1cf75e5..7b891a8 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -6,6 +6,7 @@ var getIP = require('../utils/getIP.js'); var getFormattedTime = require('../utils/getFormattedTime.js'); var isUserTrustworthy = require('../utils/isUserTrustworthy.js'); const dispatchWebhooks = require('../utils/dispatchWebhooks.js'); +const {getVoteAuthor, getVoteAuthorRaw} = require('../utils/webhookUtils.js'); var databases = require('../databases/databases.js'); @@ -15,17 +16,6 @@ var YouTubeAPI = require('../utils/youtubeAPI.js'); var request = require('request'); const logger = require('../utils/logger.js'); -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 categoryVote(UUID, userID, isVIP, category, hashedIP, res) { // Check if they've already made a vote @@ -224,19 +214,9 @@ async function voteOnSponsorTime(req, res) { } let isUpvote = incrementAmount > 0; // Send custom webhooks - let userStatus; - if (isOwnSubmission) { - userStatus = "self"; - } else if (isVIP) { - userStatus = "vip"; - } else if (userSubmissionCountRow.submissionCount === 0) { - userStatus = "new"; - } else { - userStatus = "other"; - } dispatchWebhooks(isUpvote ? "vote.up" : "vote.down", { "user": { - "status": userStatus + "status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, isVIP, isOwnSubmission) }, "video": { "id": submissionInfoRow.videoID, diff --git a/src/utils/webhookUtils.js b/src/utils/webhookUtils.js new file mode 100644 index 0000000..34f8651 --- /dev/null +++ b/src/utils/webhookUtils.js @@ -0,0 +1,26 @@ +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 ""; +} + +module.exports.getVoteAuthorRaw = getVoteAuthorRaw; +module.exports.getVoteAuthor = getVoteAuthor; \ No newline at end of file From a897c313fb987eb4970ef1e7871657afec059e43 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 22:33:06 +0200 Subject: [PATCH 25/58] Remove broken and unnecessary check --- src/routes/voteOnSponsorTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 7b891a8..9d9b458 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -203,7 +203,7 @@ async function voteOnSponsorTime(req, res) { webhookURL = config.discordCompletelyIncorrectReportWebhookURL; } - if (config.youtubeAPIKey !== null && (webhookURL !== null || config.webhooks.size !== 0)) { + if (config.youtubeAPIKey !== null) { YouTubeAPI.videos.list({ part: "snippet", id: submissionInfoRow.videoID From 16777c30ca92ee43c845b8d293f1e4ab7ec08e2a Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Sun, 23 Aug 2020 22:35:35 +0200 Subject: [PATCH 26/58] Change .size to .length --- src/utils/dispatchWebhooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/dispatchWebhooks.js b/src/utils/dispatchWebhooks.js index 4872ef7..625cbd7 100644 --- a/src/utils/dispatchWebhooks.js +++ b/src/utils/dispatchWebhooks.js @@ -5,7 +5,7 @@ const request = require('request'); function dispatchEvent(scope, data) { let webhooks = config.webhooks; - if (webhooks === undefined || webhooks.size === 0) return; + if (webhooks === undefined || webhooks.length === 0) return; logger.debug("Dispatching webhooks"); webhooks.forEach(webhook => { let webhookURL = webhook.url; From aca431835130806b18bbe4b39a084bea54f60e07 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Mon, 24 Aug 2020 09:06:47 +0200 Subject: [PATCH 27/58] Move dispatchEvent to webhookUtils --- src/routes/voteOnSponsorTime.js | 5 ++--- src/utils/dispatchWebhooks.js | 22 ---------------------- src/utils/webhookUtils.js | 27 +++++++++++++++++++++++++-- 3 files changed, 27 insertions(+), 27 deletions(-) delete mode 100644 src/utils/dispatchWebhooks.js diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 9d9b458..458c914 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -5,8 +5,7 @@ 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 dispatchWebhooks = require('../utils/dispatchWebhooks.js'); -const {getVoteAuthor, getVoteAuthorRaw} = require('../utils/webhookUtils.js'); +const {getVoteAuthor, getVoteAuthorRaw, dispatchEvent} = require('../utils/webhookUtils.js'); var databases = require('../databases/databases.js'); @@ -214,7 +213,7 @@ async function voteOnSponsorTime(req, res) { } let isUpvote = incrementAmount > 0; // Send custom webhooks - dispatchWebhooks(isUpvote ? "vote.up" : "vote.down", { + dispatchEvent(isUpvote ? "vote.up" : "vote.down", { "user": { "status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, isVIP, isOwnSubmission) }, diff --git a/src/utils/dispatchWebhooks.js b/src/utils/dispatchWebhooks.js deleted file mode 100644 index 625cbd7..0000000 --- a/src/utils/dispatchWebhooks.js +++ /dev/null @@ -1,22 +0,0 @@ -const config = require("../config.js"); -const logger = require('../utils/logger.js'); -const request = require('request'); - - -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? - }}); - }); -} - -module.exports = dispatchEvent; \ No newline at end of file diff --git a/src/utils/webhookUtils.js b/src/utils/webhookUtils.js index 34f8651..17383bf 100644 --- a/src/utils/webhookUtils.js +++ b/src/utils/webhookUtils.js @@ -1,3 +1,7 @@ +const config = require('../config.js'); +const logger = require('../utils/logger.js'); +const request = require('request'); + function getVoteAuthorRaw(submissionCount, isVIP, isOwnSubmission) { if (isOwnSubmission) { return "self"; @@ -22,5 +26,24 @@ function getVoteAuthor(submissionCount, isVIP, isOwnSubmission) { return ""; } -module.exports.getVoteAuthorRaw = getVoteAuthorRaw; -module.exports.getVoteAuthor = getVoteAuthor; \ No newline at end of file +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? + }}); + }); +} + +module.exports = { + getVoteAuthorRaw, + getVoteAuthor, + dispatchEvent +} \ No newline at end of file From 2c70f87b93005ad369736d44a68f9d5451a900dd Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Mon, 24 Aug 2020 13:14:04 +0200 Subject: [PATCH 28/58] Add webhooks to postSkipSegments --- src/routes/postSkipSegments.js | 121 +++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 45 deletions(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index 9086686..1aa513a 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -11,59 +11,90 @@ var isoDurations = require('iso8601-duration'); var getHash = require('../utils/getHash.js'); var getIP = require('../utils/getIP.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 sendWebhookNotification(userID, videoID, UUID, submissionCount, youtubeData, {submissionStart, submissionEnd}, segmentInfo) { + let row = db.prepare('get', "SELECT userName FROM userNames WHERE userID = ?", [userID]); + let userName = row !== undefined ? row.userName : null; + let video = youtubeData.items[0]; + + let scopeName = "submissions.other"; + 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 sendDiscordNotification(userID, videoID, UUID, segmentInfo) { //check if they are a first time user //if so, send a notification to discord - if (config.youtubeAPIKey !== null && config.discordFirstTimeSubmissionsWebhookURL !== null) { + if (config.youtubeAPIKey !== null) { 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({ - part: "snippet", - id: videoID - }, function (err, data) { - if (err || data.items.length === 0) { - err && logger.error(err); - return; - } - - let startTime = parseFloat(segmentInfo.segment[0]); - let endTime = parseFloat(segmentInfo.segment[1]); - - request.post(config.discordFirstTimeSubmissionsWebhookURL, { - json: { - "embeds": [{ - "title": data.items[0].snippet.title, - "url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2), - "description": "Submission ID: " + UUID + - "\n\nTimestamp: " + - getFormattedTime(startTime) + " to " + getFormattedTime(endTime) + - "\n\nCategory: " + segmentInfo.category, - "color": 10813440, - "author": { - "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"); + YouTubeAPI.videos.list({ + part: "snippet", + id: videoID + }, function (err, data) { + if (err || data.items.length === 0) { + 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); + + // If it is a first time submission + if (config.discordFirstTimeSubmissionsWebhookURL === null) return; + request.post(config.discordFirstTimeSubmissionsWebhookURL, { + json: { + "embeds": [{ + "title": data.items[0].snippet.title, + "url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2), + "description": "Submission ID: " + UUID + + "\n\nTimestamp: " + + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) + + "\n\nCategory: " + segmentInfo.category, + "color": 10813440, + "author": { + "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"); + } }); - } + }); } } From 5c9aa8c9cac54348d44baae4791090d551e5c927 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Mon, 24 Aug 2020 13:59:13 +0200 Subject: [PATCH 29/58] Cleaned up function names --- src/routes/postSkipSegments.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index 1aa513a..ec3342c 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -44,9 +44,7 @@ function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtube }); } -function sendDiscordNotification(userID, videoID, UUID, segmentInfo) { - //check if they are a first time user - //if so, send a notification to discord +function sendWebhooks(userID, videoID, UUID, segmentInfo) { if (config.youtubeAPIKey !== null) { let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [userID]); @@ -64,6 +62,7 @@ function sendDiscordNotification(userID, videoID, UUID, segmentInfo) { 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, { json: { @@ -293,7 +292,7 @@ module.exports = async function postSkipSegments(req, res) { //add to private db as well privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]); - } catch (err) { + } catch (err) {s //a DB change probably occurred res.sendStatus(502); logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " + @@ -302,8 +301,8 @@ module.exports = async function postSkipSegments(req, res) { return; } - // Discord notification - sendDiscordNotification(userID, videoID, UUID, segmentInfo); + // Webhooks + sendWebhooks(userID, videoID, UUID, segmentInfo); } } catch (err) { logger.error(err); From 3085b0e30c0d04c2a70a85845b135be2f5e2f217 Mon Sep 17 00:00:00 2001 From: TAG-Epic Date: Mon, 24 Aug 2020 22:45:47 +0200 Subject: [PATCH 30/58] Remove random s --- src/routes/postSkipSegments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index ec3342c..ffcb29d 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -292,7 +292,7 @@ module.exports = async function postSkipSegments(req, res) { //add to private db as well privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]); - } catch (err) {s + } catch (err) { //a DB change probably occurred res.sendStatus(502); logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " + From 75a62a5729f3c89fbdd96df36a70d4e7936b2d87 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 24 Aug 2020 22:04:18 +0100 Subject: [PATCH 31/58] make tests hit webhook code --- test.json | 9 ++++++++- test/mocks.js | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/test.json b/test.json index 0dbb51d..922bdd2 100644 --- a/test.json +++ b/test.json @@ -16,5 +16,12 @@ "privateDBSchema": "./databases/_private.db.sql", "mode": "test", "readOnly": false, - "webhooks": [] + "webhooks": [{ + "url": "http://127.0.0.1:8081/CustomWebhook", + "key": "superSecretKey", + "scopes": [ + "vote.up", + "vote.down" + ] + }] } diff --git a/test/mocks.js b/test/mocks.js index b58a47f..aeaed4b 100644 --- a/test/mocks.js +++ b/test/mocks.js @@ -15,6 +15,12 @@ app.post('/CompletelyIncorrectReportWebhook', (req, res) => { res.sendStatus(200); }); + +app.post('/CustomWebhook', (req, res) => { + res.sendStatus(200); +}); + + module.exports = function createMockServer(callback) { return app.listen(config.mockPort, callback); } \ No newline at end of file From 3066a077c7159227c7b6fdc176e373bc3b282194 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 24 Aug 2020 22:10:49 +0100 Subject: [PATCH 32/58] added failed webhook call to tests --- test.json | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/test.json b/test.json index 922bdd2..4c36531 100644 --- a/test.json +++ b/test.json @@ -16,12 +16,21 @@ "privateDBSchema": "./databases/_private.db.sql", "mode": "test", "readOnly": false, - "webhooks": [{ - "url": "http://127.0.0.1:8081/CustomWebhook", - "key": "superSecretKey", - "scopes": [ - "vote.up", - "vote.down" - ] - }] + "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" + ] + } +] } From eb9282c60aa027f4a24aab0ed020499beb2645cd Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 24 Aug 2020 22:22:02 +0100 Subject: [PATCH 33/58] add unresolvable host test - introduces tets faiulure --- test.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test.json b/test.json index 4c36531..e180354 100644 --- a/test.json +++ b/test.json @@ -31,6 +31,13 @@ "vote.up", "vote.down" ] + }, { + "url": "http://unresolvable.host:8081/FailedWebhook", + "key": "superSecretKey", + "scopes": [ + "vote.up", + "vote.down" + ] } -] + ] } From 3563924c6f3c2e21f355b3a778d63c510e34a2c0 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 24 Aug 2020 22:40:02 +0100 Subject: [PATCH 34/58] Fixed undesolved test error --- src/utils/logger.js | 2 ++ src/utils/webhookUtils.js | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/utils/logger.js b/src/utils/logger.js index dd3f62c..96f6360 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -45,6 +45,8 @@ const settings = { if (config.mode === 'development') { settings.INFO = true; settings.DEBUG = true; +} if (config.mode === 'test') { + settings.WARN = false; } function log(level, string) { diff --git a/src/utils/webhookUtils.js b/src/utils/webhookUtils.js index 17383bf..41a2b13 100644 --- a/src/utils/webhookUtils.js +++ b/src/utils/webhookUtils.js @@ -35,10 +35,13 @@ function dispatchEvent(scope, data) { let authKey = webhook.key; let scopes = webhook.scopes || []; if (!scopes.includes(scope.toLowerCase())) return; - request.post(webhookURL, {json: data, headers: { + let hookRequest = 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); + }); }); } From dc7dadd9a120ccf8d821d19bd99c9d977cde5948 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 24 Aug 2020 22:43:36 +0100 Subject: [PATCH 35/58] tidy undesolved fix --- src/utils/logger.js | 2 +- src/utils/webhookUtils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/logger.js b/src/utils/logger.js index 96f6360..731b5cf 100644 --- a/src/utils/logger.js +++ b/src/utils/logger.js @@ -45,7 +45,7 @@ const settings = { if (config.mode === 'development') { settings.INFO = true; settings.DEBUG = true; -} if (config.mode === 'test') { +} else if (config.mode === 'test') { settings.WARN = false; } diff --git a/src/utils/webhookUtils.js b/src/utils/webhookUtils.js index 41a2b13..6da1d62 100644 --- a/src/utils/webhookUtils.js +++ b/src/utils/webhookUtils.js @@ -35,7 +35,7 @@ function dispatchEvent(scope, data) { let authKey = webhook.key; let scopes = webhook.scopes || []; if (!scopes.includes(scope.toLowerCase())) return; - let hookRequest = request.post(webhookURL, {json: data, headers: { + request.post(webhookURL, {json: data, headers: { "Authorization": authKey, "Event-Type": scope // Maybe change this in the future? }}).on('error', (e) => { From 83ec23220c092c00099858ddc444db32bf357b03 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 24 Aug 2020 22:50:27 +0100 Subject: [PATCH 36/58] added wrong port test --- test.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test.json b/test.json index e180354..503aa5d 100644 --- a/test.json +++ b/test.json @@ -31,6 +31,13 @@ "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", From 2f2273b8a8bf0c875a9b910d1e5a436872c01362 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Mon, 24 Aug 2020 20:12:57 -0400 Subject: [PATCH 37/58] Added decimals to formatted time --- src/utils/getFormattedTime.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/getFormattedTime.js b/src/utils/getFormattedTime.js index 6f3ef40..37d22b6 100644 --- a/src/utils/getFormattedTime.js +++ b/src/utils/getFormattedTime.js @@ -1,8 +1,9 @@ //converts time in seconds to minutes:seconds module.exports = function getFormattedTime(seconds) { let minutes = Math.floor(seconds / 60); - let secondsDisplay = Math.round(seconds - minutes * 60); - if (secondsDisplay < 10) { + let seconds = seconds - minutes * 60; + let secondsDisplay = seconds.toFixed(3); + if (seconds < 10) { //add a zero secondsDisplay = "0" + secondsDisplay; } From f4b36867ffbb6a1fb627b26f39388d90e65bb534 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Mon, 24 Aug 2020 20:15:46 -0400 Subject: [PATCH 38/58] Fixed duplicate variable --- src/utils/getFormattedTime.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/getFormattedTime.js b/src/utils/getFormattedTime.js index 37d22b6..0d86271 100644 --- a/src/utils/getFormattedTime.js +++ b/src/utils/getFormattedTime.js @@ -1,7 +1,7 @@ //converts time in seconds to minutes:seconds -module.exports = function getFormattedTime(seconds) { - let minutes = Math.floor(seconds / 60); - let seconds = seconds - minutes * 60; +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 From f7bde024cb88a3201dc230a28e94b445fd55176c Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Mon, 24 Aug 2020 20:21:39 -0400 Subject: [PATCH 39/58] Allow self-upvotes on dead submissions --- src/routes/voteOnSponsorTime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index f2cf152..abe9262 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -116,7 +116,7 @@ async function voteOnSponsorTime(req, res) { return categoryVote(UUID, userID, isVIP, category, hashedIP, res); } - if (type == 1 && !isVIP) { + if (type == 1 && !isVIP && !isOwnSubmission) { // Check if upvoting hidden segment let voteInfo = db.prepare('get', "SELECT votes FROM sponsorTimes WHERE UUID = ?", [UUID]); From 19363c86f9017a2480f1b860f2f317039ba9164a Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Mon, 24 Aug 2020 20:51:04 -0400 Subject: [PATCH 40/58] New vote weight --- src/routes/getSkipSegments.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/routes/getSkipSegments.js b/src/routes/getSkipSegments.js index ba23262..a003a6c 100644 --- a/src/routes/getSkipSegments.js +++ b/src/routes/getSkipSegments.js @@ -21,11 +21,10 @@ function getWeightedRandomChoice(choices, amountOfChoices) { //assign a weight to each choice let totalWeight = 0; 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 - //https://www.desmos.com/calculator/ljftxolg9j + //https://www.desmos.com/calculator/c1duhfrmts //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; return { ...choice, weight }; From 67951b39d971ae3589ea93162cabef0bc0b48422 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 12:25:35 -0400 Subject: [PATCH 41/58] Change vote and username requirements --- src/routes/getTopUsers.js | 4 +++- src/routes/setUsername.js | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/routes/getTopUsers.js b/src/routes/getTopUsers.js index 2b452b6..a5baf09 100644 --- a/src/routes/getTopUsers.js +++ b/src/routes/getTopUsers.js @@ -42,11 +42,13 @@ module.exports = function getTopUsers (req, res) { let 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) ORDER BY " + sortBy + " DESC LIMIT 100", []); + "GROUP BY IFNULL(userName, sponsorTimes.userID) HAVING userVotes > 200 " + + "ORDER BY " + sortBy + " DESC LIMIT 100", []); for (let i = 0; i < rows.length; i++) { userNames[i] = rows[i].userName; diff --git a/src/routes/setUsername.js b/src/routes/setUsername.js index cc143fb..13e877f 100644 --- a/src/routes/setUsername.js +++ b/src/routes/setUsername.js @@ -18,6 +18,12 @@ module.exports = function setUsername(req, res) { return; } + if (userName.contains("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); From 84b143545db2fb7cf8538d0ae071c8805a0dbd26 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 12:35:24 -0400 Subject: [PATCH 42/58] Fix function name --- src/routes/setUsername.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/setUsername.js b/src/routes/setUsername.js index 13e877f..7165c07 100644 --- a/src/routes/setUsername.js +++ b/src/routes/setUsername.js @@ -18,7 +18,7 @@ module.exports = function setUsername(req, res) { return; } - if (userName.contains("discord")) { + if (userName.includes("discord")) { // Don't allow res.sendStatus(200); return; From f868939eb962f4fd7277223d3b01b51ecb6916f7 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 12:43:17 -0400 Subject: [PATCH 43/58] Remove dead submissions from total stats --- src/routes/getTotalStats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/getTotalStats.js b/src/routes/getTotalStats.js index 43be0b1..e4cfb67 100644 --- a/src/routes/getTotalStats.js +++ b/src/routes/getTotalStats.js @@ -9,7 +9,7 @@ var lastUserCountCheck = 0; module.exports = function getTotalStats (req, res) { 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) { //send this result From 407c38b4be7cf7e9773d071ea364b73f60501874 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 14:00:15 -0400 Subject: [PATCH 44/58] Hostfix basic ip ban --- src/routes/shadowBanUser.js | 65 +++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/src/routes/shadowBanUser.js b/src/routes/shadowBanUser.js index 4029885..ba5052e 100644 --- a/src/routes/shadowBanUser.js +++ b/src/routes/shadowBanUser.js @@ -8,6 +8,7 @@ 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; @@ -20,7 +21,7 @@ module.exports = async function shadowBanUser(req, res) { //if enabled is false and the old submissions should be made visible again let unHideOldSubmissions = req.query.unHideOldSubmissions !== "false"; - if (adminUserIDInput == undefined || userID == undefined) { + if (adminUserIDInput == undefined || (userID == undefined && hashedIP == undefined)) { //invalid request res.sendStatus(400); return; @@ -35,27 +36,57 @@ module.exports = async function shadowBanUser(req, res) { return; } - //check to see if this user is already shadowbanned - let row = privateDB.prepare('get', "SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?", [userID]); + 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 + 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]); + //add it to the table + privateDB.prepare('run', "INSERT INTO shadowBannedUsers VALUES(?)", [userID]); - //find all previous submissions and hide them - if (unHideOldSubmissions) { + //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]); + } + } 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]); - } + //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); From a1babfb2cea196e91039a156113ed88258c86964 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 15:48:31 -0400 Subject: [PATCH 45/58] Lower vote threshold --- src/routes/getTopUsers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/getTopUsers.js b/src/routes/getTopUsers.js index a5baf09..3df5c65 100644 --- a/src/routes/getTopUsers.js +++ b/src/routes/getTopUsers.js @@ -47,7 +47,7 @@ module.exports = function getTopUsers (req, res) { "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 > 200 " + + "GROUP BY IFNULL(userName, sponsorTimes.userID) HAVING userVotes > 50 " + "ORDER BY " + sortBy + " DESC LIMIT 100", []); for (let i = 0; i < rows.length; i++) { From 800a3dff9d565b3831f3b1346076ccc93ee7da55 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 19:22:44 -0400 Subject: [PATCH 46/58] Send webhooks after response --- src/routes/postSkipSegments.js | 10 +- src/routes/voteOnSponsorTime.js | 237 ++++++++++++++++++-------------- 2 files changed, 139 insertions(+), 108 deletions(-) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index ffcb29d..f007b6f 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -226,6 +226,9 @@ module.exports = async function postSkipSegments(req, res) { } } + // Will be filled when submitting + let UUIDs = []; + try { //check if this user is on the vip list let vipRow = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]); @@ -301,8 +304,7 @@ module.exports = async function postSkipSegments(req, res) { return; } - // Webhooks - sendWebhooks(userID, videoID, UUID, segmentInfo); + UUIDs.push(UUID); } } catch (err) { logger.error(err); @@ -313,4 +315,8 @@ module.exports = async function postSkipSegments(req, res) { } res.sendStatus(200); + + for (let i = 0; i < segments.length; i++) { + sendWebhooks(userID, videoID, UUIDs[i], segments[i]); + } } diff --git a/src/routes/voteOnSponsorTime.js b/src/routes/voteOnSponsorTime.js index 458c914..a2cefb3 100644 --- a/src/routes/voteOnSponsorTime.js +++ b/src/routes/voteOnSponsorTime.js @@ -7,7 +7,6 @@ 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; @@ -15,6 +14,125 @@ var YouTubeAPI = require('../utils/youtubeAPI.js'); var request = require('request'); 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) { // Check if they've already made a vote @@ -117,11 +235,6 @@ async function voteOnSponsorTime(req, res) { } } - let voteTypes = { - normal: 0, - incorrect: 1 - } - let voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect; try { @@ -185,106 +298,6 @@ async function voteOnSponsorTime(req, res) { } } - // 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) { - YouTubeAPI.videos.list({ - part: "snippet", - id: submissionInfoRow.videoID - }, function (err, data) { - if (err || data.items.length === 0) { - err && logger.error(err); - return; - } - let isUpvote = incrementAmount > 0; - // Send custom webhooks - dispatchEvent(isUpvote ? "vote.up" : "vote.down", { - "user": { - "status": getVoteAuthorRaw(userSubmissionCountRow.submissionCount, isVIP, 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": UUID, - "views": row.views, - "category": category, - "startTime": submissionInfoRow.startTime, - "endTime": submissionInfoRow.endTime, - "user": { - "UUID": submissionInfoRow.userID, - "username": submissionInfoRow.userName, - "submissions": { - "total": submissionInfoRow.count, - "ignored": submissionInfoRow.disregarded - } - } - }, - "votes": { - "before": row.votes, - "after": (row.votes + incrementAmount - 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": "**" + 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": getVoteAuthor(userSubmissionCountRow.submissionCount, isVIP, 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"); - } - }); - } - - }); - } - } - // 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 @@ -337,6 +350,18 @@ async function voteOnSponsorTime(req, res) { } res.sendStatus(200); + + sendWebhooks({ + UUID, + nonAnonUserID, + voteTypeEnum, + isVIP, + isOwnSubmission, + row, + category, + incrementAmount, + oldIncrementAmount + }); } catch (err) { logger.error(err); From 49cfbdd95fcb6ac607d8e4bdb324ae1b5a07f982 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 20:02:32 -0400 Subject: [PATCH 47/58] Lower minimum votes to 20 --- src/routes/getTopUsers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/getTopUsers.js b/src/routes/getTopUsers.js index 3df5c65..22b8ca9 100644 --- a/src/routes/getTopUsers.js +++ b/src/routes/getTopUsers.js @@ -47,7 +47,7 @@ module.exports = function getTopUsers (req, res) { "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 > 50 " + + "GROUP BY IFNULL(userName, sponsorTimes.userID) HAVING userVotes > 20 " + "ORDER BY " + sortBy + " DESC LIMIT 100", []); for (let i = 0; i < rows.length; i++) { From 262d3d3dfdc04471192b3200c5b53ad1a8d1c428 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sat, 29 Aug 2020 20:05:49 -0400 Subject: [PATCH 48/58] Add username length limit --- src/routes/setUsername.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/setUsername.js b/src/routes/setUsername.js index 7165c07..989e712 100644 --- a/src/routes/setUsername.js +++ b/src/routes/setUsername.js @@ -12,7 +12,7 @@ module.exports = function setUsername(req, res) { let adminUserIDInput = req.query.adminUserID; - if (userID == undefined || userName == undefined || userID === "undefined") { + if (userID == undefined || userName == undefined || userID === "undefined" || username.length > 50) { //invalid request res.sendStatus(400); return; From 92b0f917d69662514e465369e6012ebff922bea8 Mon Sep 17 00:00:00 2001 From: James Robinson Date: Sat, 29 Aug 2020 22:01:13 -0500 Subject: [PATCH 49/58] fix username variable in setUsername --- src/routes/setUsername.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/setUsername.js b/src/routes/setUsername.js index 989e712..e5b2865 100644 --- a/src/routes/setUsername.js +++ b/src/routes/setUsername.js @@ -12,7 +12,7 @@ module.exports = function setUsername(req, res) { let adminUserIDInput = req.query.adminUserID; - if (userID == undefined || userName == undefined || userID === "undefined" || username.length > 50) { + if (userID == undefined || userName == undefined || userID === "undefined" || userName.length > 50) { //invalid request res.sendStatus(400); return; From e46444f3b9aad0a2e989731fd2213cd54da6522d Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 30 Aug 2020 00:22:21 -0400 Subject: [PATCH 50/58] Added option to use new user counter --- src/app.js | 3 ++ src/middleware/userCounter.js | 11 ++++++ src/routes/getTotalStats.js | 68 +++++++++++++++++++++-------------- 3 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 src/middleware/userCounter.js diff --git a/src/app.js b/src/app.js index 1ce1674..c70115d 100644 --- a/src/app.js +++ b/src/app.js @@ -6,6 +6,7 @@ var config = require('./config.js'); // Middleware 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; @@ -31,6 +32,8 @@ app.use(corsMiddleware); app.use(loggerMiddleware); app.use(express.json()) +if (config.userCounterURL) app.use(userCounter); + // Setup pretty JSON if (config.mode === "development") app.set('json spaces', 2); diff --git a/src/middleware/userCounter.js b/src/middleware/userCounter.js new file mode 100644 index 0000000..183e1b4 --- /dev/null +++ b/src/middleware/userCounter.js @@ -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(); +} \ No newline at end of file diff --git a/src/routes/getTotalStats.js b/src/routes/getTotalStats.js index 43be0b1..ba521e3 100644 --- a/src/routes/getTotalStats.js +++ b/src/routes/getTotalStats.js @@ -1,11 +1,15 @@ -var db = require('../databases/databases.js').db; -var request = require('request'); +const db = require('../databases/databases.js').db; +const request = require('request'); +const config = require('../config.js'); // A cache of the number of chrome web store users -var chromeUsersCache = null; -var firefoxUsersCache = null; -var lastUserCountCheck = 0; +let chromeUsersCache = null; +let firefoxUsersCache = null; +// By the privacy friendly user counter +let apiUsersCache = null; + +let lastUserCountCheck = 0; module.exports = function getTotalStats (req, res) { let row = db.prepare('get', "SELECT COUNT(DISTINCT userID) as userCount, COUNT(*) as totalSubmissions, " + @@ -16,6 +20,7 @@ module.exports = function getTotalStats (req, res) { res.send({ userCount: row.userCount, activeUsers: chromeUsersCache + firefoxUsersCache, + apiUsers: apiUsersCache, viewCount: row.viewCount, totalSubmissions: row.totalSubmissions, minutesSaved: row.minutesSaved @@ -26,28 +31,37 @@ module.exports = function getTotalStats (req, res) { if (now - lastUserCountCheck > 5000000) { lastUserCountCheck = now; - // Get total users - request.get("https://addons.mozilla.org/api/v3/addons/addon/sponsorblock/", function (err, firefoxResponse, body) { - 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) { - if (body !== undefined) { - try { - chromeUsersCache = parseInt(body.match(/(?<=\)/)[0].replace(",", "")); - } catch (error) { - // Re-check later - lastUserCountCheck = 0; - } - } else { - lastUserCountCheck = 0; - } - }); - } catch (error) { - // Re-check later - lastUserCountCheck = 0; - } - }); + updateExtensionUsers(); } } +} + +function updateExtensionUsers() { + if (config.userCounterURL) { + request.get(config.userCounterURL + "/api/v1/userCount", (err, response, body) => { + apiUsersCache = JSON.parse(body).userCount; + }); + } + + request.get("https://addons.mozilla.org/api/v3/addons/addon/sponsorblock/", function (err, firefoxResponse, body) { + 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) { + if (body !== undefined) { + try { + chromeUsersCache = parseInt(body.match(/(?<=\)/)[0].replace(",", "")); + } catch (error) { + // Re-check later + lastUserCountCheck = 0; + } + } else { + lastUserCountCheck = 0; + } + }); + } catch (error) { + // Re-check later + lastUserCountCheck = 0; + } + }); } \ No newline at end of file From 89ee0afd619e3047082687175fe26d82ebe6e327 Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 30 Aug 2020 11:17:26 -0400 Subject: [PATCH 51/58] Only accept new user count if higher + add to example config --- config.json.example | 1 + src/routes/getTotalStats.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config.json.example b/config.json.example index 22971d9..57fa901 100644 --- a/config.json.example +++ b/config.json.example @@ -8,6 +8,7 @@ "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] "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) "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", diff --git a/src/routes/getTotalStats.js b/src/routes/getTotalStats.js index ba521e3..103617a 100644 --- a/src/routes/getTotalStats.js +++ b/src/routes/getTotalStats.js @@ -39,7 +39,7 @@ module.exports = function getTotalStats (req, res) { function updateExtensionUsers() { if (config.userCounterURL) { request.get(config.userCounterURL + "/api/v1/userCount", (err, response, body) => { - apiUsersCache = JSON.parse(body).userCount; + apiUsersCache = Math.max(apiUsersCache, JSON.parse(body).userCount); }); } From 5f23fdd590e69685c26943286900fb6d4148478e Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Sun, 30 Aug 2020 21:47:02 +0100 Subject: [PATCH 52/58] Added tests and finished no-segments interface. --- src/routes/postSkipSegments.js | 9 +- ...{postNoSegments.js => noSegmentRecords.js} | 103 +++++++++++++++++- 2 files changed, 107 insertions(+), 5 deletions(-) rename test/cases/{postNoSegments.js => noSegmentRecords.js} (67%) diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index 15ab309..08380fc 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -187,8 +187,7 @@ module.exports = async function postSkipSegments(req, res) { //hash the ip 5000 times so no one can get it from the database let hashedIP = getHash(getIP(req) + config.globalSalt); - let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]); - + let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]).map((list) => { return list.category }); // Check if all submissions are correct for (let i = 0; i < segments.length; i++) { if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) { @@ -200,7 +199,11 @@ module.exports = async function postSkipSegments(req, res) { // Reject segemnt if it's in the no segments list if (noSegmentList.indexOf(segments[i].category) !== -1) { // TODO: Do something about the fradulent submission - res.sendStatus(403); + 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; } diff --git a/test/cases/postNoSegments.js b/test/cases/noSegmentRecords.js similarity index 67% rename from test/cases/postNoSegments.js rename to test/cases/noSegmentRecords.js index 8489f61..d902478 100644 --- a/test/cases/postNoSegments.js +++ b/test/cases/noSegmentRecords.js @@ -4,9 +4,10 @@ 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('postNoSegments', () => { +describe('noSegmentRecords', () => { before(() => { db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser-noSegments") + "')"); @@ -15,9 +16,10 @@ describe('postNoSegments', () => { 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) => { + 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); @@ -223,4 +225,101 @@ describe('postNoSegments', () => { } }); }); + + /* + * 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); + } + }); + }); }); \ No newline at end of file From 36ce803828bd2dfec60037e5a871d0f815bd4a35 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 31 Aug 2020 01:44:30 +0100 Subject: [PATCH 53/58] Update src/routes/getIsUserVIP.js Co-authored-by: Ajay Ramachandran --- src/routes/getIsUserVIP.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/getIsUserVIP.js b/src/routes/getIsUserVIP.js index d38ef9d..63efd02 100644 --- a/src/routes/getIsUserVIP.js +++ b/src/routes/getIsUserVIP.js @@ -4,7 +4,7 @@ var getHash = require('../utils/getHash.js'); const logger = require('../utils/logger.js'); const isUserVIP = require('../utils/isUserVIP.js'); -module.exports = function getUsername (req, res) { +module.exports = (req, res) => { let userID = req.query.userID; if (userID == undefined) { @@ -28,4 +28,4 @@ module.exports = function getUsername (req, res) { return; } -} \ No newline at end of file +} From 1e643c1c0740d6b2c3f6849152bc28f4110c3c84 Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 31 Aug 2020 01:48:41 +0100 Subject: [PATCH 54/58] categorys -> categories --- src/routes/postNoSegments.js | 20 ++++++++++---------- test/cases/noSegmentRecords.js | 26 +++++++++++++------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/routes/postNoSegments.js b/src/routes/postNoSegments.js index 6494095..6b2b7aa 100644 --- a/src/routes/postNoSegments.js +++ b/src/routes/postNoSegments.js @@ -7,14 +7,14 @@ module.exports = (req, res) => { // Collect user input data let videoID = req.body.videoID; let userID = req.body.userID; - let categorys = req.body.categorys; + let categories = req.body.categories; // Check input data is valid if (!videoID || !userID - || !categorys - || !Array.isArray(categorys) - || categorys.length === 0 + || !categories + || !Array.isArray(categories) + || categories.length === 0 ) { res.status(400).json({ status: 400, @@ -45,20 +45,20 @@ module.exports = (req, res) => { }); } - // get user categorys not already submitted that match accepted format - let categorysToMark = categorys.filter((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 - categorysToMark = categorysToMark.filter((category, index) => { - return categorysToMark.indexOf(category) === index; + categoriesToMark = categoriesToMark.filter((category, index) => { + return categoriesToMark.indexOf(category) === index; }); // create database entry - categorysToMark.forEach((category) => { + categoriesToMark.forEach((category) => { try { db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]); } catch (err) { @@ -73,6 +73,6 @@ module.exports = (req, res) => { res.status(200).json({ status: 200, - submitted: categorysToMark + submitted: categoriesToMark }); }; \ No newline at end of file diff --git a/test/cases/noSegmentRecords.js b/test/cases/noSegmentRecords.js index d902478..a09264f 100644 --- a/test/cases/noSegmentRecords.js +++ b/test/cases/noSegmentRecords.js @@ -25,11 +25,11 @@ describe('noSegmentRecords', () => { else done('Version isn\'t greater that 1. Version is ' + version); }); - it('Should be able to submit categorys not in video (http response)', (done) => { + it('Should be able to submit categories not in video (http response)', (done) => { let json = { videoID: 'no-segments-video-id', userID: 'VIPUser-noSegments', - categorys: [ + categories: [ 'outro', 'shilling', 'shilling', @@ -64,11 +64,11 @@ describe('noSegmentRecords', () => { }); }); - it('Should be able to submit categorys not in video (sql check)', (done) => { + it('Should be able to submit categories not in video (sql check)', (done) => { let json = { videoID: 'no-segments-video-id-1', userID: 'VIPUser-noSegments', - categorys: [ + categories: [ 'outro', 'shilling', 'shilling', @@ -110,11 +110,11 @@ describe('noSegmentRecords', () => { }); }); - it('Should return 400 for no categorys', (done) => { + it('Should return 400 for no categories', (done) => { let json = { videoID: 'test', userID: 'test', - categorys: [] + categories: [] }; request.post(utils.getbaseURL() @@ -133,7 +133,7 @@ describe('noSegmentRecords', () => { let json = { videoID: 'test', userID: null, - categorys: ['sponsor'] + categories: ['sponsor'] }; request.post(utils.getbaseURL() @@ -152,7 +152,7 @@ describe('noSegmentRecords', () => { let json = { videoID: null, userID: 'test', - categorys: ['sponsor'] + categories: ['sponsor'] }; request.post(utils.getbaseURL() @@ -167,11 +167,11 @@ describe('noSegmentRecords', () => { }); }); - it('Should return 400 object categorys)', (done) => { + it('Should return 400 object categories)', (done) => { let json = { videoID: 'test', userID: 'test', - categorys: {} + categories: {} }; request.post(utils.getbaseURL() @@ -186,11 +186,11 @@ describe('noSegmentRecords', () => { }); }); - it('Should return 400 bad format categorys', (done) => { + it('Should return 400 bad format categories', (done) => { let json = { videoID: 'test', userID: 'test', - categorys: 'sponsor' + categories: 'sponsor' }; request.post(utils.getbaseURL() @@ -209,7 +209,7 @@ describe('noSegmentRecords', () => { let json = { videoID: 'test', userID: 'test', - categorys: [ + categories: [ 'sponsor' ] }; From 754d3762dfb81a42a0de974f1944f56f7cb7176b Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 31 Aug 2020 01:50:36 +0100 Subject: [PATCH 55/58] fixed test category name --- test/cases/getIsUserVIP.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/getIsUserVIP.js b/test/cases/getIsUserVIP.js index 0b25e7d..a971017 100644 --- a/test/cases/getIsUserVIP.js +++ b/test/cases/getIsUserVIP.js @@ -3,7 +3,7 @@ var utils = require('../utils.js'); var db = require('../../src/databases/databases.js').db; var getHash = require('../../src/utils/getHash.js'); -describe('getSavedTimeForUser', () => { +describe('getIsUserVIP', () => { before(() => { db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("supertestman") + "')"); }); From aa878482d31332a409deb072ff7348517f6c242c Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 31 Aug 2020 01:55:38 +0100 Subject: [PATCH 56/58] removed status from no segment responses --- src/routes/postNoSegments.js | 4 ---- test/cases/noSegmentRecords.js | 1 - 2 files changed, 5 deletions(-) diff --git a/src/routes/postNoSegments.js b/src/routes/postNoSegments.js index 6b2b7aa..6aab052 100644 --- a/src/routes/postNoSegments.js +++ b/src/routes/postNoSegments.js @@ -17,7 +17,6 @@ module.exports = (req, res) => { || categories.length === 0 ) { res.status(400).json({ - status: 400, message: 'Bad Format' }); return; @@ -29,7 +28,6 @@ module.exports = (req, res) => { if (!userIsVIP) { res.status(403).json({ - status: 403, message: 'Must be a VIP to mark videos.' }); return; @@ -65,14 +63,12 @@ module.exports = (req, res) => { logger.error("Error submitting 'noSegment' marker for category '" + category + "' for video '" + videoID + "'"); logger.error(err); res.status(500).json({ - status: 500, message: "Internal Server Error: Could not write marker to the database." }); } }); res.status(200).json({ - status: 200, submitted: categoriesToMark }); }; \ No newline at end of file diff --git a/test/cases/noSegmentRecords.js b/test/cases/noSegmentRecords.js index a09264f..3989258 100644 --- a/test/cases/noSegmentRecords.js +++ b/test/cases/noSegmentRecords.js @@ -40,7 +40,6 @@ describe('noSegmentRecords', () => { }; let expected = { - status: 200, submitted: [ 'outro', 'shilling' From 00534d91d46c343c294c59175349489bb89d908e Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Mon, 31 Aug 2020 03:09:47 +0100 Subject: [PATCH 57/58] added underscore to category format check --- src/routes/postNoSegments.js | 2 +- test/cases/noSegmentRecords.js | 84 ++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/routes/postNoSegments.js b/src/routes/postNoSegments.js index 6aab052..cdbfce9 100644 --- a/src/routes/postNoSegments.js +++ b/src/routes/postNoSegments.js @@ -45,7 +45,7 @@ module.exports = (req, res) => { // get user categories not already submitted that match accepted format let categoriesToMark = categories.filter((category) => { - return !!category.match(/^[a-zA-Z]+$/); + return !!category.match(/^[_a-zA-Z]+$/); }).filter((category) => { return noSegmentList.indexOf(category) === -1; }); diff --git a/test/cases/noSegmentRecords.js b/test/cases/noSegmentRecords.js index 3989258..d439b13 100644 --- a/test/cases/noSegmentRecords.js +++ b/test/cases/noSegmentRecords.js @@ -96,6 +96,90 @@ describe('noSegmentRecords', () => { }); }); + 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/postNoSegments", {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/postNoSegments", {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/postNoSegments", {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/postNoSegments", {json: {}}, From e33062feeb95df4c6bd6bf85ffd1d70e1b01016a Mon Sep 17 00:00:00 2001 From: Joe Dowd Date: Tue, 1 Sep 2020 21:37:54 +0100 Subject: [PATCH 58/58] updated endpoints --- src/app.js | 4 ++-- test/cases/getIsUserVIP.js | 8 ++++---- test/cases/noSegmentRecords.js | 24 ++++++++++++------------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/app.js b/src/app.js index 8cb937f..45715b7 100644 --- a/src/app.js +++ b/src/app.js @@ -92,10 +92,10 @@ app.get('/api/getTotalStats', getTotalStats); app.get('/api/getDaysSavedFormatted', getDaysSavedFormatted); //submit video containing no segments -app.post('/api/postNoSegments', postNoSegments); +app.post('/api/noSegments', postNoSegments); //get if user is a vip -app.get('/api/getIsUserVIP', getIsUserVIP); +app.get('/api/isUserVIP', getIsUserVIP); app.get('/database.db', function (req, res) { diff --git a/test/cases/getIsUserVIP.js b/test/cases/getIsUserVIP.js index a971017..1bbfbfe 100644 --- a/test/cases/getIsUserVIP.js +++ b/test/cases/getIsUserVIP.js @@ -10,7 +10,7 @@ describe('getIsUserVIP', () => { it('Should be able to get a 200', (done) => { request.get(utils.getbaseURL() - + "/api/getIsUserVIP?userID=supertestman", null, + + "/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); @@ -21,7 +21,7 @@ describe('getIsUserVIP', () => { it('Should get a 400 if no userID', (done) => { request.get(utils.getbaseURL() - + "/api/getIsUserVIP", null, + + "/api/isUserVIP", null, (err, res, body) => { if (err) done("couldn't call endpoint"); else if (res.statusCode !== 400) done("non 400: " + res.statusCode); @@ -31,7 +31,7 @@ describe('getIsUserVIP', () => { it('Should say a VIP is a VIP', (done) => { request.get(utils.getbaseURL() - + "/api/getIsUserVIP?userID=supertestman", null, + + "/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); @@ -44,7 +44,7 @@ describe('getIsUserVIP', () => { it('Should say a normal user is not a VIP', (done) => { request.get(utils.getbaseURL() - + "/api/getIsUserVIP?userID=regulartestman", null, + + "/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); diff --git a/test/cases/noSegmentRecords.js b/test/cases/noSegmentRecords.js index d439b13..91dea2a 100644 --- a/test/cases/noSegmentRecords.js +++ b/test/cases/noSegmentRecords.js @@ -47,7 +47,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -78,7 +78,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -106,7 +106,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -134,7 +134,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -162,7 +162,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -182,7 +182,7 @@ describe('noSegmentRecords', () => { it('Should return 400 for missing params', (done) => { request.post(utils.getbaseURL() - + "/api/postNoSegments", {json: {}}, + + "/api/noSegments", {json: {}}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 400) { @@ -201,7 +201,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 400) { @@ -220,7 +220,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 400) { @@ -239,7 +239,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 400) { @@ -258,7 +258,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 400) { @@ -277,7 +277,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 400) { @@ -298,7 +298,7 @@ describe('noSegmentRecords', () => { }; request.post(utils.getbaseURL() - + "/api/postNoSegments", {json}, + + "/api/noSegments", {json}, (err, res, body) => { if (err) done(err); else if (res.statusCode === 403) {