Merge branch 'get-ip-fixes' of https://github.com/ajayyy/SponsorBlockServer into nb-mod-fetch

# Conflicts:
#	src/routes/postSkipSegments.js
This commit is contained in:
Ajay Ramachandran
2020-04-30 19:04:23 -04:00
17 changed files with 488 additions and 204 deletions

View File

@@ -7,10 +7,12 @@
"youtubeAPIKey": null, //get this from Google Cloud Platform [optional]
"discordReportChannelWebhookURL": null, //URL from discord if you would like notifications when someone makes a report [optional]
"discordFirstTimeSubmissionsWebhookURL": null, //URL from discord if you would like notifications when someone makes a first time submission [optional]
"behindProxy": true,
"discordCompletelyIncorrectReportWebhookURL": null, //URL from discord if you would like notifications when someone reports a submission as completely incorrect [optional]
"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",
"privateDB": "./databases/private.db",
"createDatabaseIfNotExist": true, //This will run on startup every time (unless readOnly is true) - so ensure "create table if not exists" is used in the schema
"schemaFolder": "./databases",
"dbSchema": "./databases/_sponsorTimes.db.sql",
"privateDBSchema": "./databases/_private.db.sql",
"mode": "development",

View File

@@ -1,18 +1,30 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "shadowBannedUsers" (
"userID" TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "votes" (
"UUID" TEXT NOT NULL,
"userID" INTEGER NOT NULL,
"hashedIP" INTEGER NOT NULL,
"userID" TEXT NOT NULL,
"hashedIP" TEXT NOT NULL,
"type" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "categoryVotes" (
"UUID" TEXT NOT NULL,
"userID" TEXT NOT NULL,
"hashedIP" TEXT NOT NULL,
"category" TEXT NOT NULL,
"timeSubmitted" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "sponsorTimes" (
"videoID" TEXT NOT NULL,
"hashedIP" TEXT NOT NULL,
"timeSubmitted" INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS sponsorTimes_hashedIP on sponsorTimes(hashedIP);
CREATE INDEX IF NOT EXISTS votes_userID on votes(UUID);
COMMIT;

View File

@@ -1,4 +1,5 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "vipUsers" (
"userID" TEXT NOT NULL
);
@@ -18,6 +19,17 @@ CREATE TABLE IF NOT EXISTS "userNames" (
"userID" TEXT NOT NULL,
"userName" TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "categoryVotes" (
"UUID" TEXT NOT NULL,
"category" TEXT NOT NULL,
"votes" INTEGER NOT NULL default '0'
);
CREATE TABLE IF NOT EXISTS "version" (
"code" INTEGER NOT NULL default '0'
);
CREATE INDEX IF NOT EXISTS sponsorTimes_videoID on sponsorTimes(videoID);
CREATE INDEX IF NOT EXISTS sponsorTimes_UUID on sponsorTimes(UUID);
COMMIT;

25
databases/_upgrade_0.sql Normal file
View File

@@ -0,0 +1,25 @@
BEGIN TRANSACTION;
/* Add incorrectVotes field */
CREATE TABLE "sqlb_temp_table_1" (
"videoID" TEXT NOT NULL,
"startTime" REAL NOT NULL,
"endTime" REAL NOT NULL,
"votes" INTEGER NOT NULL,
"incorrectVotes" INTEGER NOT NULL default '1',
"UUID" TEXT NOT NULL UNIQUE,
"userID" TEXT NOT NULL,
"timeSubmitted" INTEGER NOT NULL,
"views" INTEGER NOT NULL,
"category" TEXT NOT NULL DEFAULT "sponsor",
"shadowHidden" INTEGER NOT NULL
);
INSERT INTO sqlb_temp_table_1 SELECT videoID,startTime,endTime,votes,"1",UUID,userID,timeSubmitted,views,category,shadowHidden FROM sponsorTimes;
DROP TABLE sponsorTimes;
ALTER TABLE sqlb_temp_table_1 RENAME TO "sponsorTimes";
/* Increase version number */
INSERT INTO version VALUES(1);
COMMIT;

5
package-lock.json generated
View File

@@ -1398,6 +1398,11 @@
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"iso8601-duration": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/iso8601-duration/-/iso8601-duration-1.2.0.tgz",
"integrity": "sha512-ErTBd++b17E8nmWII1K1uZtBgD1E8RjyvwmxlCjPHNqHMD7gmcMHOw0E8Ro/6+QT4PhHRSnnMo7bxa1vFPkwhg=="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",

View File

@@ -1,12 +1,21 @@
var config = require('../config.js');
var Sqlite3 = require('better-sqlite3');
var fs = require('fs');
var path = require('path');
let options = {
readonly: config.readOnly,
fileMustExist: !config.createDatabaseIfNotExist
};
// Make dirs if required
if (!fs.existsSync(path.join(config.db, "../"))) {
fs.mkdirSync(path.join(config.db, "../"));
}
if (!fs.existsSync(path.join(config.privateDB, "../"))) {
fs.mkdirSync(path.join(config.privateDB, "../"));
}
var db = new Sqlite3(config.db, options);
var privateDB = new Sqlite3(config.privateDB, options);
@@ -15,6 +24,18 @@ if (config.createDatabaseIfNotExist && !config.readOnly) {
if (fs.existsSync(config.privateDBSchema)) privateDB.exec(fs.readFileSync(config.privateDBSchema).toString());
}
// Upgrade database if required
if (!config.readOnly) {
let versionCode = db.prepare("SELECT code FROM version").get() || 0;
let path = config.schemaFolder + "/_upgrade_" + versionCode + ".sql";
while (fs.existsSync(path)) {
db.exec(fs.readFileSync(path).toString());
versionCode = db.prepare("SELECT code FROM version").get();
path = config.schemaFolder + "/_upgrade_" + versionCode + ".sql";
}
}
// Enable WAL mode checkpoint number
if (!config.readOnly && config.mode === "production") {
db.exec("PRAGMA journal_mode=WAL;");

View File

@@ -265,7 +265,9 @@ module.exports = async function postSkipSegments(req, res) {
segmentInfo.segment[1] + segmentInfo.category + userID, 1);
try {
db.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, segmentInfo.segment[0],
db.prepare("INSERT INTO sponsorTimes " +
"(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden)" +
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, segmentInfo.segment[0],
segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned);
//add to private db as well

View File

@@ -11,169 +11,265 @@ var privateDB = databases.privateDB;
var YouTubeAPI = require('../utils/youtubeAPI.js');
var request = require('request');
function categoryVote(UUID, userID, isVIP, category, hashedIP, res) {
// Check if they've already made a vote
let previousVoteInfo = privateDB.prepare("select count(*) as votes, category from categoryVotes where UUID = ? and userID = ?").get(UUID, userID);
if (previousVoteInfo > 0 && previousVoteInfo.category === category) {
// Double vote, ignore
res.sendStatus(200);
return;
}
let timeSubmitted = Date.now();
let voteAmount = isVIP ? 500 : 1;
// Add the vote
if (db.prepare("select count(*) as count from categoryVotes where UUID = ? and category = ?").get(UUID, category).count > 0) {
// Update the already existing db entry
db.prepare("update categoryVotes set votes = votes + ? where UUID = ? and category = ?").run(voteAmount, UUID, category);
} else {
// Add a db entry
db.prepare("insert into categoryVotes (UUID, category, votes) values (?, ?, ?)").run(UUID, category, voteAmount);
}
// Add the info into the private db
if (previousVoteInfo > 0) {
// Reverse the previous vote
db.prepare("update categoryVotes set votes -= 1 where UUID = ? and category = ?").run(UUID, previousVoteInfo.category);
privateDB.prepare("update categoryVotes set category = ?, timeSubmitted = ?, hashedIP = ?").run(category, timeSubmitted, hashedIP)
} else {
privateDB.prepare("insert into categoryVotes (UUID, userID, hashedIP, category, timeSubmitted) values (?, ?, ?, ?, ?)").run(UUID, userID, hashedIP, category, timeSubmitted);
}
// See if the submissions categort is ready to change
let currentCategory = db.prepare("select category from sponsorTimes where UUID = ?").get(UUID);
let currentCategoryInfo = db.prepare("select votes from categoryVotes where UUID = ? and category = ?").get(UUID, currentCategory.category);
// Change this value from 1 in the future to make it harder to change categories
// Done this way without ORs incase the value is zero
let currentCategoryCount = (currentCategoryInfo === undefined || currentCategoryInfo === null) ? 1 : currentCategoryInfo.votes;
let nextCategoryCount = (previousVoteInfo.votes || 0) + 1;
//TODO: In the future, raise this number from zero to make it harder to change categories
// VIPs change it every time
if (nextCategoryCount - currentCategoryCount >= 0 || isVIP) {
// Replace the category
db.prepare("update sponsorTimes set category = ? where UUID = ?").run(category, UUID);
}
res.sendStatus(200);
}
module.exports = async function voteOnSponsorTime(req, res) {
let UUID = req.query.UUID;
let userID = req.query.userID;
let type = req.query.type;
let UUID = req.query.UUID;
let userID = req.query.userID;
let type = req.query.type;
let category = req.query.category;
if (UUID == undefined || userID == undefined || type == undefined) {
//invalid request
res.sendStatus(400);
return;
}
if (UUID === undefined || userID === undefined || (type === undefined && category === undefined)) {
//invalid request
res.sendStatus(400);
return;
}
//hash the userID
let nonAnonUserID = getHash(userID);
userID = getHash(userID + UUID);
//hash the userID
let nonAnonUserID = getHash(userID);
userID = getHash(userID + UUID);
//x-forwarded-for if this server is behind a proxy
let ip = getIP(req);
//x-forwarded-for if this server is behind a proxy
let ip = getIP(req);
//hash the ip 5000 times so no one can get it from the database
let hashedIP = getHash(ip + config.globalSalt);
//hash the ip 5000 times so no one can get it from the database
let hashedIP = getHash(ip + config.globalSalt);
try {
//check if vote has already happened
let votesRow = privateDB.prepare("SELECT type FROM votes WHERE userID = ? AND UUID = ?").get(userID, UUID);
//-1 for downvote, 1 for upvote. Maybe more depending on reputation in the future
let incrementAmount = 0;
let oldIncrementAmount = 0;
//check if this user is on the vip list
let isVIP = db.prepare("SELECT count(*) as userCount FROM vipUsers WHERE userID = ?").get(nonAnonUserID).userCount > 0;
if (type == 1) {
//upvote
incrementAmount = 1;
} else if (type == 0) {
//downvote
incrementAmount = -1;
} else {
//unrecongnised type of vote
res.sendStatus(400);
return;
}
if (votesRow != undefined) {
if (votesRow.type == 1) {
//upvote
oldIncrementAmount = 1;
} else if (votesRow.type == 0) {
//downvote
oldIncrementAmount = -1;
} else if (votesRow.type == 2) {
//extra downvote
oldIncrementAmount = -4;
} else if (votesRow.type < 0) {
//vip downvote
oldIncrementAmount = votesRow.type;
}
}
if (type === undefined && category !== undefined) {
return categoryVote(UUID, userID, isVIP, category, hashedIP, res);
}
//check if this user is on the vip list
let vipRow = db.prepare("SELECT count(*) as userCount FROM vipUsers WHERE userID = ?").get(nonAnonUserID);
let voteTypes = {
normal: 0,
incorrect: 1
}
//check if the increment amount should be multiplied (downvotes have more power if there have been many views)
let row = db.prepare("SELECT votes, views FROM sponsorTimes WHERE UUID = ?").get(UUID);
if (vipRow.userCount != 0 && incrementAmount < 0) {
//this user is a vip and a downvote
incrementAmount = - (row.votes + 2 - oldIncrementAmount);
type = incrementAmount;
} else if (row !== undefined && (row.votes > 8 || row.views > 15) && incrementAmount < 0) {
//increase the power of this downvote
incrementAmount = -Math.abs(Math.min(10, row.votes + 2 - oldIncrementAmount));
type = incrementAmount;
}
let voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect;
// Send discord message
if (type != 1) {
// Get video ID
let submissionInfoRow = db.prepare("SELECT s.videoID, s.userID, s.startTime, s.endTime, 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 inner join userNames u on s.userID = u.userID where s.UUID=?"
).get(UUID);
try {
//check if vote has already happened
let votesRow = privateDB.prepare("SELECT type FROM votes WHERE userID = ? AND UUID = ?").get(userID, UUID);
//-1 for downvote, 1 for upvote. Maybe more depending on reputation in the future
let incrementAmount = 0;
let oldIncrementAmount = 0;
let userSubmissionCountRow = db.prepare("SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?").get(nonAnonUserID);
if (type == 1 || type == 11) {
//upvote
incrementAmount = 1;
} else if (type == 0 || type == 10) {
//downvote
incrementAmount = -1;
} else {
//unrecongnised type of vote
res.sendStatus(400);
return;
}
if (votesRow != undefined) {
if (votesRow.type === 1 || type === 11) {
//upvote
oldIncrementAmount = 1;
} else if (votesRow.type === 0 || type === 10) {
//downvote
oldIncrementAmount = -1;
} else if (votesRow.type === 2) {
//extra downvote
oldIncrementAmount = -4;
} else if (votesRow.type < 0) {
//vip downvote
oldIncrementAmount = votesRow.type;
} else if (votesRow.type === 12) {
// VIP downvote for completely incorrect
oldIncrementAmount = -500;
} else if (votesRow.type === 13) {
// VIP upvote for completely incorrect
oldIncrementAmount = 500;
}
}
if (config.youtubeAPIKey !== null && config.discordReportChannelWebhookURL !== null) {
YouTubeAPI.videos.list({
part: "snippet",
id: submissionInfoRow.videoID
}, function (err, data) {
if (err || data.items.length === 0) {
err && console.log(err);
return;
}
request.post(config.discordReportChannelWebhookURL, {
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\n**Submitted by:** "+submissionInfoRow.userName+"\n " + submissionInfoRow.userID
+ "\n\n**Total User Submissions:** "+submissionInfoRow.count
+ "\n**Ignored User Submissions:** "+submissionInfoRow.disregarded
+"\n\n**Timestamp:** " +
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
"color": 10813440,
"author": {
"name": userSubmissionCountRow.submissionCount === 0 ? "Report by New User" : (vipRow.userCount !== 0 ? "Report by VIP User" : "")
},
"thumbnail": {
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
}
}]
}
}, (err, res) => {
if (err) {
console.log("Failed to send reported submission Discord hook.");
console.log(JSON.stringify(err));
console.log("\n");
} else if (res && res.statusCode >= 400) {
console.log("Error sending reported submission Discord hook");
console.log(JSON.stringify(res));
console.log("\n");
//check if the increment amount should be multiplied (downvotes have more power if there have been many views)
let row = db.prepare("SELECT votes, views FROM sponsorTimes WHERE UUID = ?").get(UUID);
if (voteTypeEnum === voteTypes.normal) {
if (isVIP && incrementAmount < 0) {
//this user is a vip and a downvote
incrementAmount = - (row.votes + 2 - oldIncrementAmount);
type = incrementAmount;
} else if (row !== undefined && (row.votes > 8 || row.views > 15) && incrementAmount < 0) {
//increase the power of this downvote
incrementAmount = -Math.abs(Math.min(10, row.votes + 2 - oldIncrementAmount));
type = incrementAmount;
}
} else if (voteTypeEnum == voteTypes.incorrect) {
if (isVIP) {
//this user is a vip and a downvote
incrementAmount = 500 * incrementAmount;
type = incrementAmount < 0 ? 12 : 13;
}
}
// Send discord message
if (incrementAmount < 0) {
// Get video ID
let submissionInfoRow = db.prepare("SELECT s.videoID, s.userID, s.startTime, s.endTime, 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=?"
).get(UUID);
let userSubmissionCountRow = db.prepare("SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?").get(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;
}
});
});
}
}
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\n**Submitted by:** "+submissionInfoRow.userName+"\n " + submissionInfoRow.userID
+ "\n\n**Total User Submissions:** "+submissionInfoRow.count
+ "\n**Ignored User Submissions:** "+submissionInfoRow.disregarded
+"\n\n**Timestamp:** " +
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
"color": 10813440,
"author": {
"name": userSubmissionCountRow.submissionCount === 0 ? "Report by New User" : (isVIP ? "Report by VIP User" : "")
},
"thumbnail": {
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
}
}]
}
}, (err, res) => {
if (err) {
console.log("Failed to send reported submission Discord hook.");
console.log(JSON.stringify(err));
console.log("\n");
} else if (res && res.statusCode >= 400) {
console.log("Error sending reported submission Discord hook");
console.log(JSON.stringify(res));
console.log("\n");
}
});
});
}
}
}
//update the votes table
if (votesRow != undefined) {
privateDB.prepare("UPDATE votes SET type = ? WHERE userID = ? AND UUID = ?").run(type, userID, UUID);
} else {
privateDB.prepare("INSERT INTO votes VALUES(?, ?, ?, ?)").run(UUID, userID, hashedIP, type);
}
//update the votes table
if (votesRow != undefined) {
privateDB.prepare("UPDATE votes SET type = ? WHERE userID = ? AND UUID = ?").run(type, userID, UUID);
} else {
privateDB.prepare("INSERT INTO votes VALUES(?, ?, ?, ?)").run(UUID, userID, hashedIP, type);
}
//update the vote count on this sponsorTime
//oldIncrementAmount will be zero is row is null
db.prepare("UPDATE sponsorTimes SET votes = votes + ? WHERE UUID = ?").run(incrementAmount - oldIncrementAmount, UUID);
let columnName = "";
if (voteTypeEnum === voteTypes.normal) {
columnName = "votes";
} else if (voteTypeEnum === voteTypes.incorrect) {
columnName = "incorrectVotes";
}
//for each positive vote, see if a hidden submission can be shown again
if (incrementAmount > 0) {
//find the UUID that submitted the submission that was voted on
let submissionUserID = db.prepare("SELECT userID FROM sponsorTimes WHERE UUID = ?").get(UUID).userID;
//update the vote count on this sponsorTime
//oldIncrementAmount will be zero is row is null
db.prepare("UPDATE sponsorTimes SET " + columnName + " = " + columnName + " + ? WHERE UUID = ?").run(incrementAmount - oldIncrementAmount, UUID);
//check if any submissions are hidden
let hiddenSubmissionsRow = db.prepare("SELECT count(*) as hiddenSubmissions FROM sponsorTimes WHERE userID = ? AND shadowHidden > 0").get(submissionUserID);
//for each positive vote, see if a hidden submission can be shown again
if (incrementAmount > 0 && voteTypeEnum === voteTypes.normal) {
//find the UUID that submitted the submission that was voted on
let submissionUserID = db.prepare("SELECT userID FROM sponsorTimes WHERE UUID = ?").get(UUID).userID;
if (hiddenSubmissionsRow.hiddenSubmissions > 0) {
//see if some of this users submissions should be visible again
if (await isUserTrustworthy(submissionUserID)) {
//they are trustworthy again, show 2 of their submissions again, if there are two to show
db.prepare("UPDATE sponsorTimes SET shadowHidden = 0 WHERE ROWID IN (SELECT ROWID FROM sponsorTimes WHERE userID = ? AND shadowHidden = 1 LIMIT 2)").run(submissionUserID)
}
}
}
//check if any submissions are hidden
let hiddenSubmissionsRow = db.prepare("SELECT count(*) as hiddenSubmissions FROM sponsorTimes WHERE userID = ? AND shadowHidden > 0").get(submissionUserID);
//added to db
res.sendStatus(200);
} catch (err) {
console.error(err);
res.status(500).json({error: 'Internal error creating segment vote'});
}
if (hiddenSubmissionsRow.hiddenSubmissions > 0) {
//see if some of this users submissions should be visible again
if (await isUserTrustworthy(submissionUserID)) {
//they are trustworthy again, show 2 of their submissions again, if there are two to show
db.prepare("UPDATE sponsorTimes SET shadowHidden = 0 WHERE ROWID IN (SELECT ROWID FROM sponsorTimes WHERE userID = ? AND shadowHidden = 1 LIMIT 2)").run(submissionUserID)
}
}
}
//added to db
res.sendStatus(200);
} catch (err) {
console.error(err);
res.status(500).json({error: 'Internal error creating segment vote'});
}
}

View File

@@ -2,5 +2,16 @@ var fs = require('fs');
var config = require('../config.js');
module.exports = function getIP(req) {
return config.behindProxy ? req.headers['x-forwarded-for'] : req.connection.remoteAddress;
if (config.behindProxy === true) config.behindProxy = "X-Forwarded-For";
switch (config.behindProxy) {
case "X-Forwarded-For":
return req.headers['X-Forwarded-For'];
case "Cloudflare":
return req.headers['CF-Connecting-IP'];
case "X-Real-IP":
return req.headers['X-Real-IP'];
default:
return req.connection.remoteAddress;
}
}

View File

@@ -6,12 +6,14 @@
"youtubeAPIKey": "",
"discordReportChannelWebhookURL": "http://127.0.0.1:8081/ReportChannelWebhook",
"discordFirstTimeSubmissionsWebhookURL": "http://127.0.0.1:8081/FirstTimeSubmissionsWebhook",
"discordCompletelyIncorrectReportWebhookURL": "http://127.0.0.1:8081/CompletelyIncorrectReportWebhook",
"behindProxy": true,
"db": "./test/databases/sponsorTimes.db",
"privateDB": "./test/databases/private.db",
"createDatabaseIfNotExist": true,
"dbSchema": "./test/databases/_sponsorTimes.db.sql",
"privateDBSchema": "./test/databases/_private.db.sql",
"schemaFolder": "./databases",
"dbSchema": "./databases/_sponsorTimes.db.sql",
"privateDBSchema": "./databases/_private.db.sql",
"mode": "test",
"readOnly": false
}

View File

@@ -5,7 +5,8 @@ var getHash = require('../../src/utils/getHash.js');
describe('getSavedTimeForUser', () => {
before(() => {
db.exec("INSERT INTO sponsorTimes VALUES ('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0)");
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES";
db.exec(startOfQuery + "('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0)");
});
it('Should be able to get a 200', (done) => {

View File

@@ -18,13 +18,14 @@ var utils = require('../utils.js');
describe('getSkipSegments', () => {
before(() => {
db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0)");
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES";
db.exec(startOfQuery + "('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0)");
db.exec(startOfQuery + "('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0)");
db.exec(startOfQuery + "('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0)");
});

View File

@@ -19,8 +19,9 @@ var utils = require('../utils.js');
describe('getVideoSponsorTime (Old get method)', () => {
before(() => {
db.exec("INSERT INTO sponsorTimes VALUES ('old-testtesttest', 1, 11, 2, 'uuid-0', 'testman', 0, 50, 'sponsor', 0)");
db.exec("INSERT INTO sponsorTimes VALUES ('old-testtesttest,test', 1, 11, 2, 'uuid-1', 'testman', 0, 50, 'sponsor', 0)");
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES";
db.exec(startOfQuery + "('old-testtesttest', 1, 11, 2, 'uuid-0', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('old-testtesttest,test', 1, 11, 2, 'uuid-1', 'testman', 0, 50, 'sponsor', 0)");
});
it('Should be able to get a time', (done) => {

View File

@@ -0,0 +1,130 @@
var request = require('request');
var db = require('../../src/databases/databases.js').db;
var utils = require('../utils.js');
var getHash = require('../../src/utils/getHash.js')
describe('voteOnSponsorTime', () => {
before(() => {
let startOfQuery = "INSERT INTO sponsorTimes (videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden) VALUES";
db.exec(startOfQuery + "('vote-testtesttest', 1, 11, 2, 'vote-uuid-0', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('vote-testtesttest', 20, 33, 10, 'vote-uuid-2', 'testman', 0, 50, 'intro', 0)");
db.exec(startOfQuery + "('vote-testtesttest,test', 1, 11, 100, 'vote-uuid-3', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('vote-test3', 1, 11, 2, 'vote-uuid-4', 'testman', 0, 50, 'sponsor', 0)");
db.exec(startOfQuery + "('vote-test3', 7, 22, -3, 'vote-uuid-5', 'testman', 0, 50, 'intro', 0)");
db.exec(startOfQuery + "('vote-multiple', 1, 11, 2, 'vote-uuid-6', 'testman', 0, 50, 'intro', 0)");
db.exec(startOfQuery + "('vote-multiple', 20, 33, 2, 'vote-uuid-7', 'testman', 0, 50, 'intro', 0)");
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser") + "')");
});
it('Should be able to upvote a segment', (done) => {
request.get(utils.getbaseURL()
+ "/api/voteOnSponsorTime?userID=randomID&UUID=vote-uuid-0&type=1", null,
(err, res, body) => {
if (err) done(err);
else if (res.statusCode === 200) {
let row = db.prepare("SELECT votes FROM sponsorTimes WHERE UUID = ?").get("vote-uuid-0");
if (row.votes === 3) {
done()
} else {
done("Vote did not succeed. Submission went from 2 votes to " + row.votes);
}
} else {
done("Status code was " + res.statusCode);
}
});
});
it('Should be able to downvote a segment', (done) => {
request.get(utils.getbaseURL()
+ "/api/voteOnSponsorTime?userID=randomID2&UUID=vote-uuid-2&type=0", null,
(err, res, body) => {
if (err) done(err);
else if (res.statusCode === 200) {
let row = db.prepare("SELECT votes FROM sponsorTimes WHERE UUID = ?").get("vote-uuid-2");
if (row.votes < 10) {
done()
} else {
done("Vote did not succeed. Submission went from 10 votes to " + row.votes);
}
} else {
done("Status code was " + res.statusCode);
}
});
});
it('VIP should be able to completely downvote a segment', (done) => {
request.get(utils.getbaseURL()
+ "/api/voteOnSponsorTime?userID=VIPUser&UUID=vote-uuid-3&type=0", null,
(err, res, body) => {
if (err) done(err);
else if (res.statusCode === 200) {
let row = db.prepare("SELECT votes FROM sponsorTimes WHERE UUID = ?").get("vote-uuid-3");
if (row.votes <= -2) {
done()
} else {
done("Vote did not succeed. Submission went from 100 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,
(err, res, body) => {
if (err) done(err);
else if (res.statusCode === 200) {
let row = db.prepare("SELECT category FROM sponsorTimes WHERE UUID = ?").get("vote-uuid-4");
if (row.category === "intro") {
done()
} else {
done("Vote did not succeed. Submission went from sponsor to " + row.category);
}
} else {
done("Status code was " + res.statusCode);
}
});
});
it('Should be able to change your 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=outro", null,
(err, res, body) => {
if (err) done(err);
else if (res.statusCode === 200) {
let row = db.prepare("SELECT category FROM sponsorTimes WHERE UUID = ?").get("vote-uuid-4");
if (row.category === "outro") {
done()
} else {
done("Vote did not succeed. Submission went from intro to " + row.category);
}
} else {
done("Status code was " + res.statusCode);
}
});
});
it('VIP should be able to vote for a category and it should immediately change', (done) => {
request.get(utils.getbaseURL()
+ "/api/voteOnSponsorTime?userID=VIPUser&UUID=vote-uuid-5&category=outro", null,
(err, res, body) => {
if (err) done(err);
else if (res.statusCode === 200) {
let row = db.prepare("SELECT category FROM sponsorTimes WHERE UUID = ?").get("vote-uuid-5");
let row2 = db.prepare("SELECT votes FROM categoryVotes WHERE UUID = ? and category = ?").get("vote-uuid-5", "outro");
if (row.category === "outro" && row2.votes === 500) {
done()
} else {
done("Vote did not succeed. Submission went from intro to " + row.category + ". Category votes are " + row2.votes + " and should be 500.");
}
} else {
done("Status code was " + res.statusCode);
}
});
});
});

View File

@@ -1,18 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "shadowBannedUsers" (
"userID" TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "votes" (
"UUID" TEXT NOT NULL,
"userID" INTEGER NOT NULL,
"hashedIP" INTEGER NOT NULL,
"type" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "sponsorTimes" (
"videoID" TEXT NOT NULL,
"hashedIP" TEXT NOT NULL,
"timeSubmitted" INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS sponsorTimes_hashedIP on sponsorTimes(hashedIP);
CREATE INDEX IF NOT EXISTS votes_userID on votes(UUID);
COMMIT;

View File

@@ -1,23 +0,0 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "vipUsers" (
"userID" TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS "sponsorTimes" (
"videoID" TEXT NOT NULL,
"startTime" REAL NOT NULL,
"endTime" REAL NOT NULL,
"votes" INTEGER NOT NULL,
"UUID" TEXT NOT NULL UNIQUE,
"userID" TEXT NOT NULL,
"timeSubmitted" INTEGER NOT NULL,
"views" INTEGER NOT NULL,
"category" TEXT NOT NULL,
"shadowHidden" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "userNames" (
"userID" TEXT NOT NULL,
"userName" TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS sponsorTimes_videoID on sponsorTimes(videoID);
CREATE INDEX IF NOT EXISTS sponsorTimes_UUID on sponsorTimes(UUID);
COMMIT;

View File

@@ -11,6 +11,10 @@ app.post('/FirstTimeSubmissionsWebhook', (req, res) => {
res.sendStatus(200);
});
app.post('/CompletelyIncorrectReportWebhook', (req, res) => {
res.sendStatus(200);
});
module.exports = function createMockServer(callback) {
return app.listen(config.mockPort, callback);
}