mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2024-11-13 02:14:32 +01:00
Merge pull request #3 from Joe-Dowd/no-segments
fixed db update, started no segments
This commit is contained in:
commit
6dd735eab0
28 changed files with 1155 additions and 221 deletions
|
@ -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",
|
||||
|
@ -17,5 +18,6 @@
|
|||
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
||||
"privateDBSchema": "./databases/_private.db.sql",
|
||||
"mode": "development",
|
||||
"readOnly": false
|
||||
"readOnly": false,
|
||||
"webhooks": []
|
||||
}
|
||||
|
|
13
databases/_upgrade_sponsorTimes_2.sql
Normal file
13
databases/_upgrade_sponsorTimes_2.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
BEGIN TRANSACTION;
|
||||
|
||||
/* Add new table: noSegments */
|
||||
CREATE TABLE "noSegments" (
|
||||
"videoID" TEXT NOT NULL,
|
||||
"userID" TEXT NOT NULL,
|
||||
"category" TEXT NOT NULL
|
||||
);
|
||||
|
||||
/* Add version to config */
|
||||
UPDATE config SET value = 2 WHERE key = 'version';
|
||||
|
||||
COMMIT;
|
|
@ -5,33 +5,20 @@ cd /usr/src/app
|
|||
cp /etc/sponsorblock/config.json . || cat <<EOF > config.json
|
||||
{
|
||||
"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
|
||||
}
|
||||
|
|
3
index.js
3
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 + ".");
|
||||
});
|
||||
|
|
12
src/app.js
12
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;
|
||||
|
@ -21,6 +22,8 @@ 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');
|
||||
var getIsUserVIP = require('./routes/getIsUserVIP.js');
|
||||
|
||||
// Old Routes
|
||||
var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js');
|
||||
|
@ -31,6 +34,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);
|
||||
|
||||
|
@ -86,6 +91,13 @@ 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/noSegments', postNoSegments);
|
||||
|
||||
//get if user is a vip
|
||||
app.get('/api/isUserVIP', getIsUserVIP);
|
||||
|
||||
|
||||
app.get('/database.db', function (req, res) {
|
||||
res.sendFile("./databases/sponsorTimes.db", { root: "./" });
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
11
src/middleware/userCounter.js
Normal file
11
src/middleware/userCounter.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
var request = require('request');
|
||||
|
||||
var config = require('../config.js');
|
||||
var getIP = require('../utils/getIP.js');
|
||||
const getHash = require('../utils/getHash.js');
|
||||
|
||||
module.exports = function userCounter(req, res, next) {
|
||||
request.post(config.userCounterURL + "/api/v1/addIP?hashedIP=" + getHash(getIP(req), 1));
|
||||
|
||||
next();
|
||||
}
|
31
src/routes/getIsUserVIP.js
Normal file
31
src/routes/getIsUserVIP.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
var db = require('../databases/databases.js').db;
|
||||
|
||||
var getHash = require('../utils/getHash.js');
|
||||
const logger = require('../utils/logger.js');
|
||||
const isUserVIP = require('../utils/isUserVIP.js');
|
||||
|
||||
module.exports = (req, res) => {
|
||||
let userID = req.query.userID;
|
||||
|
||||
if (userID == undefined) {
|
||||
//invalid request
|
||||
res.sendStatus(400);
|
||||
return;
|
||||
}
|
||||
|
||||
//hash the userID
|
||||
userID = getHash(userID);
|
||||
|
||||
try {
|
||||
let vipState = isUserVIP(userID);
|
||||
res.status(200).json({
|
||||
hashedUserID: userID,
|
||||
vip: vipState
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
res.sendStatus(500);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
var fs = require('fs');
|
||||
var config = require('../config.js');
|
||||
|
||||
var databases = require('../databases/databases.js');
|
||||
|
@ -21,11 +20,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 };
|
||||
|
|
|
@ -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 > 20 " +
|
||||
"ORDER BY " + sortBy + " DESC LIMIT 100", []);
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
userNames[i] = rows[i].userName;
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
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, " +
|
||||
"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
|
||||
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(/(?<=\<span class=\"e-f-ih\" title=\").*?(?= users\">)/)[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 = Math.max(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(/(?<=\<span class=\"e-f-ih\" title=\").*?(?= users\">)/)[0].replace(",", ""));
|
||||
} catch (error) {
|
||||
// Re-check later
|
||||
lastUserCountCheck = 0;
|
||||
}
|
||||
} else {
|
||||
lastUserCountCheck = 0;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// Re-check later
|
||||
lastUserCountCheck = 0;
|
||||
}
|
||||
});
|
||||
}
|
74
src/routes/postNoSegments.js
Normal file
74
src/routes/postNoSegments.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
const db = require('../databases/databases.js').db;
|
||||
const getHash = require('../utils/getHash.js');
|
||||
const isUserVIP = require('../utils/isUserVIP.js');
|
||||
const logger = require('../utils/logger.js');
|
||||
|
||||
module.exports = (req, res) => {
|
||||
// Collect user input data
|
||||
let videoID = req.body.videoID;
|
||||
let userID = req.body.userID;
|
||||
let categories = req.body.categories;
|
||||
|
||||
// Check input data is valid
|
||||
if (!videoID
|
||||
|| !userID
|
||||
|| !categories
|
||||
|| !Array.isArray(categories)
|
||||
|| categories.length === 0
|
||||
) {
|
||||
res.status(400).json({
|
||||
message: 'Bad Format'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if user is VIP
|
||||
userID = getHash(userID);
|
||||
let userIsVIP = isUserVIP(userID);
|
||||
|
||||
if (!userIsVIP) {
|
||||
res.status(403).json({
|
||||
message: 'Must be a VIP to mark videos.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Get existing no segment markers
|
||||
let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]);
|
||||
if (!noSegmentList || noSegmentList.length === 0) {
|
||||
noSegmentList = [];
|
||||
} else {
|
||||
noSegmentList = noSegmentList.map((obj) => {
|
||||
return obj.category;
|
||||
});
|
||||
}
|
||||
|
||||
// get user categories not already submitted that match accepted format
|
||||
let categoriesToMark = categories.filter((category) => {
|
||||
return !!category.match(/^[_a-zA-Z]+$/);
|
||||
}).filter((category) => {
|
||||
return noSegmentList.indexOf(category) === -1;
|
||||
});
|
||||
|
||||
// remove any duplicates
|
||||
categoriesToMark = categoriesToMark.filter((category, index) => {
|
||||
return categoriesToMark.indexOf(category) === index;
|
||||
});
|
||||
|
||||
// create database entry
|
||||
categoriesToMark.forEach((category) => {
|
||||
try {
|
||||
db.prepare('run', "INSERT INTO noSegments (videoID, userID, category) VALUES(?, ?, ?)", [videoID, userID, category]);
|
||||
} catch (err) {
|
||||
logger.error("Error submitting 'noSegment' marker for category '" + category + "' for video '" + videoID + "'");
|
||||
logger.error(err);
|
||||
res.status(500).json({
|
||||
message: "Internal Server Error: Could not write marker to the database."
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
submitted: categoriesToMark
|
||||
});
|
||||
};
|
|
@ -11,59 +11,89 @@ 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 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) {
|
||||
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 sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
||||
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
|
||||
// Then send a notification to discord
|
||||
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");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,9 +148,9 @@ function proxySubmission(req) {
|
|||
request.post(config.proxySubmission + '/api/skipSegments?userID='+req.query.userID+'&videoID='+req.query.videoID, {json: req.body}, (err, result) => {
|
||||
if (config.mode === 'development') {
|
||||
if (!err) {
|
||||
logger.error('Proxy Submission: ' + result.statusCode + ' ('+result.body+')');
|
||||
logger.debug('Proxy Submission: ' + result.statusCode + ' ('+result.body+')');
|
||||
} else {
|
||||
logger.debug("Proxy Submission: Failed to make call");
|
||||
logger.error("Proxy Submission: Failed to make call");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -157,6 +187,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
//hash the ip 5000 times so no one can get it from the database
|
||||
let hashedIP = getHash(getIP(req) + config.globalSalt);
|
||||
|
||||
let noSegmentList = db.prepare('all', 'SELECT category from noSegments where videoID = ?', [videoID]).map((list) => { return list.category });
|
||||
// Check if all submissions are correct
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) {
|
||||
|
@ -165,11 +196,23 @@ 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
|
||||
logger.warn("Caught a no-segment submission. userID: '" + userID + "', videoID: '" + videoID + "', category: '" + segments[i].category + "'");
|
||||
res.status(403).send(
|
||||
"Request rejected by auto moderator: This video has been reported as not containing any segments with the category '"
|
||||
+ segments[i].category + "'. If you believe this is incorrect, contact someone on Discord."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let startTime = parseFloat(segments[i].segment[0]);
|
||||
let 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;
|
||||
|
@ -196,6 +239,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]);
|
||||
|
@ -271,8 +317,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Discord notification
|
||||
sendDiscordNotification(userID, videoID, UUID, segmentInfo);
|
||||
UUIDs.push(UUID);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
|
@ -283,4 +328,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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,18 @@ 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;
|
||||
}
|
||||
|
||||
if (userName.includes("discord")) {
|
||||
// Don't allow
|
||||
res.sendStatus(200);
|
||||
return;
|
||||
}
|
||||
|
||||
if (adminUserIDInput != undefined) {
|
||||
//this is the admin controlling the other users account, don't hash the controling account's ID
|
||||
adminUserIDInput = getHash(adminUserIDInput);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -4,7 +4,8 @@ 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 { getVoteAuthor, getVoteAuthorRaw, dispatchEvent } = require('../utils/webhookUtils.js');
|
||||
|
||||
var databases = require('../databases/databases.js');
|
||||
var db = databases.db;
|
||||
|
@ -13,6 +14,126 @@ 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
|
||||
let previousVoteInfo = privateDB.prepare('get', "select count(*) as votes, category from categoryVotes where UUID = ? and userID = ?", [UUID, userID]);
|
||||
|
@ -97,11 +218,14 @@ async function voteOnSponsorTime(req, res) {
|
|||
//check if this user is on the vip list
|
||||
let isVIP = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [nonAnonUserID]).userCount > 0;
|
||||
|
||||
//check if user voting on own submission
|
||||
let isOwnSubmission = db.prepare("get", "SELECT UUID as submissionCount FROM sponsorTimes where userID = ? AND UUID = ?", [nonAnonUserID, UUID]) !== undefined;
|
||||
|
||||
if (type === undefined && category !== undefined) {
|
||||
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]);
|
||||
|
||||
|
@ -111,11 +235,6 @@ async function voteOnSponsorTime(req, res) {
|
|||
}
|
||||
}
|
||||
|
||||
let voteTypes = {
|
||||
normal: 0,
|
||||
incorrect: 1
|
||||
}
|
||||
|
||||
let voteTypeEnum = (type == 0 || type == 1) ? voteTypes.normal : voteTypes.incorrect;
|
||||
|
||||
try {
|
||||
|
@ -166,87 +285,19 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Send discord message
|
||||
if (incrementAmount < 0) {
|
||||
// Get video ID
|
||||
let submissionInfoRow = db.prepare('get', "SELECT s.videoID, s.userID, s.startTime, s.endTime, s.category, u.userName, " +
|
||||
"(select count(1) from sponsorTimes where userID = s.userID) count, " +
|
||||
"(select count(1) from sponsorTimes where userID = s.userID and votes <= -2) disregarded " +
|
||||
"FROM sponsorTimes s left join userNames u on s.userID = u.userID where s.UUID=?",
|
||||
[UUID]);
|
||||
|
||||
let userSubmissionCountRow = db.prepare('get', "SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]);
|
||||
|
||||
if (submissionInfoRow !== undefined && userSubmissionCountRow != undefined) {
|
||||
let webhookURL = null;
|
||||
if (voteTypeEnum === voteTypes.normal) {
|
||||
webhookURL = config.discordReportChannelWebhookURL;
|
||||
} else if (voteTypeEnum === voteTypes.incorrect) {
|
||||
webhookURL = config.discordCompletelyIncorrectReportWebhookURL;
|
||||
}
|
||||
|
||||
if (config.youtubeAPIKey !== null && webhookURL !== null) {
|
||||
YouTubeAPI.videos.list({
|
||||
part: "snippet",
|
||||
id: submissionInfoRow.videoID
|
||||
}, function (err, data) {
|
||||
if (err || data.items.length === 0) {
|
||||
err && logger.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
request.post(webhookURL, {
|
||||
json: {
|
||||
"embeds": [{
|
||||
"title": data.items[0].snippet.title,
|
||||
"url": "https://www.youtube.com/watch?v=" + submissionInfoRow.videoID
|
||||
+ "&t=" + (submissionInfoRow.startTime.toFixed(0) - 2),
|
||||
"description": "**" + row.votes + " Votes Prior | " + (row.votes + incrementAmount - oldIncrementAmount) + " Votes Now | " + row.views
|
||||
+ " Views**\n\n**Submission ID:** " + UUID
|
||||
+ "\n**Category:** " + submissionInfoRow.category
|
||||
+ "\n\n**Submitted by:** "+submissionInfoRow.userName+"\n " + submissionInfoRow.userID
|
||||
+ "\n\n**Total User Submissions:** "+submissionInfoRow.count
|
||||
+ "\n**Ignored User Submissions:** "+submissionInfoRow.disregarded
|
||||
+"\n\n**Timestamp:** " +
|
||||
getFormattedTime(submissionInfoRow.startTime) + " to " + getFormattedTime(submissionInfoRow.endTime),
|
||||
"color": 10813440,
|
||||
"author": {
|
||||
"name": userSubmissionCountRow.submissionCount === 0 ? "Report by New User" : (isVIP ? "Report by VIP User" : "")
|
||||
},
|
||||
"thumbnail": {
|
||||
"url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "",
|
||||
}
|
||||
}]
|
||||
}
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
logger.error("Failed to send reported submission Discord hook.");
|
||||
logger.error(JSON.stringify(err));
|
||||
logger.error("\n");
|
||||
} else if (res && res.statusCode >= 400) {
|
||||
logger.error("Error sending reported submission Discord hook");
|
||||
logger.error(JSON.stringify(res));
|
||||
logger.error("\n");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only change the database if they have made a submission before and haven't voted recently
|
||||
let ableToVote = isVIP
|
||||
|| (db.prepare("get", "SELECT userID FROM sponsorTimes WHERE userID = ?", [nonAnonUserID]) !== undefined
|
||||
|
@ -299,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);
|
||||
|
||||
|
@ -311,4 +374,4 @@ module.exports = {
|
|||
endpoint: function (req, res) {
|
||||
voteOnSponsorTime(req, res);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
module.exports = function getFormattedTime(totalSeconds) {
|
||||
let minutes = Math.floor(totalSeconds / 60);
|
||||
let seconds = totalSeconds - minutes * 60;
|
||||
let secondsDisplay = seconds.toFixed(3);
|
||||
if (seconds < 10) {
|
||||
//add a zero
|
||||
secondsDisplay = "0" + secondsDisplay;
|
||||
}
|
||||
|
|
8
src/utils/isUserVIP.js
Normal file
8
src/utils/isUserVIP.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
const databases = require('../databases/databases.js');
|
||||
const db = databases.db;
|
||||
|
||||
module.exports = (userID) => {
|
||||
return db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
||||
}
|
||||
|
||||
|
|
@ -7,6 +7,34 @@ const levels = {
|
|||
DEBUG: "DEBUG"
|
||||
};
|
||||
|
||||
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,
|
||||
|
@ -17,12 +45,18 @@ const settings = {
|
|||
if (config.mode === 'development') {
|
||||
settings.INFO = true;
|
||||
settings.DEBUG = true;
|
||||
} else if (config.mode === 'test') {
|
||||
settings.WARN = false;
|
||||
}
|
||||
|
||||
function log(level, string) {
|
||||
if (!!settings[level]) {
|
||||
let color = colors.Bright;
|
||||
if (level === levels.ERROR) color = colors.FgRed;
|
||||
if (level === levels.WARN) color = colors.FgYellow;
|
||||
|
||||
if (level.length === 4) {level = level + " "}; // ensure logs are aligned
|
||||
console.log(level + " " + new Date().toISOString() + " : " + string);
|
||||
console.log(colors.Dim, level + " " + new Date().toISOString() + ": ", color, string, colors.Reset);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
52
src/utils/webhookUtils.js
Normal file
52
src/utils/webhookUtils.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
const config = require('../config.js');
|
||||
const logger = require('../utils/logger.js');
|
||||
const request = require('request');
|
||||
|
||||
function getVoteAuthorRaw(submissionCount, isVIP, isOwnSubmission) {
|
||||
if (isOwnSubmission) {
|
||||
return "self";
|
||||
} else if (isVIP) {
|
||||
return "vip";
|
||||
} else if (submissionCount === 0) {
|
||||
return "new";
|
||||
} else {
|
||||
return "other";
|
||||
};
|
||||
};
|
||||
|
||||
function getVoteAuthor(submissionCount, isVIP, isOwnSubmission) {
|
||||
if (submissionCount === 0) {
|
||||
return "Report by New User";
|
||||
} else if (isVIP) {
|
||||
return "Report by VIP User";
|
||||
} else if (isOwnSubmission) {
|
||||
return "Report by Submitter";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function dispatchEvent(scope, data) {
|
||||
let webhooks = config.webhooks;
|
||||
if (webhooks === undefined || webhooks.length === 0) return;
|
||||
logger.debug("Dispatching webhooks");
|
||||
webhooks.forEach(webhook => {
|
||||
let webhookURL = webhook.url;
|
||||
let authKey = webhook.key;
|
||||
let scopes = webhook.scopes || [];
|
||||
if (!scopes.includes(scope.toLowerCase())) return;
|
||||
request.post(webhookURL, {json: data, headers: {
|
||||
"Authorization": authKey,
|
||||
"Event-Type": scope // Maybe change this in the future?
|
||||
}}).on('error', (e) => {
|
||||
logger.warn('Couldn\'t send webhook to ' + webhook.url);
|
||||
logger.warn(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getVoteAuthorRaw,
|
||||
getVoteAuthor,
|
||||
dispatchEvent
|
||||
}
|
5
test.js
5
test.js
|
@ -9,6 +9,7 @@ if (fs.existsSync(config.privateDB)) fs.unlinkSync(config.privateDB);
|
|||
|
||||
var createServer = require('./src/app.js');
|
||||
var 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();
|
||||
|
|
33
test.json
33
test.json
|
@ -15,5 +15,36 @@
|
|||
"dbSchema": "./databases/_sponsorTimes.db.sql",
|
||||
"privateDBSchema": "./databases/_private.db.sql",
|
||||
"mode": "test",
|
||||
"readOnly": false
|
||||
"readOnly": false,
|
||||
"webhooks": [
|
||||
{
|
||||
"url": "http://127.0.0.1:8081/CustomWebhook",
|
||||
"key": "superSecretKey",
|
||||
"scopes": [
|
||||
"vote.up",
|
||||
"vote.down"
|
||||
]
|
||||
}, {
|
||||
"url": "http://127.0.0.1:8081/FailedWebhook",
|
||||
"key": "superSecretKey",
|
||||
"scopes": [
|
||||
"vote.up",
|
||||
"vote.down"
|
||||
]
|
||||
}, {
|
||||
"url": "http://127.0.0.1:8099/WrongPort",
|
||||
"key": "superSecretKey",
|
||||
"scopes": [
|
||||
"vote.up",
|
||||
"vote.down"
|
||||
]
|
||||
}, {
|
||||
"url": "http://unresolvable.host:8081/FailedWebhook",
|
||||
"key": "superSecretKey",
|
||||
"scopes": [
|
||||
"vote.up",
|
||||
"vote.down"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
57
test/cases/getIsUserVIP.js
Normal file
57
test/cases/getIsUserVIP.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
var request = require('request');
|
||||
var utils = require('../utils.js');
|
||||
var db = require('../../src/databases/databases.js').db;
|
||||
var getHash = require('../../src/utils/getHash.js');
|
||||
|
||||
describe('getIsUserVIP', () => {
|
||||
before(() => {
|
||||
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("supertestman") + "')");
|
||||
});
|
||||
|
||||
it('Should be able to get a 200', (done) => {
|
||||
request.get(utils.getbaseURL()
|
||||
+ "/api/isUserVIP?userID=supertestman", null,
|
||||
(err, res, body) => {
|
||||
if (err) done("couldn't call endpoint");
|
||||
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
||||
else done(); // pass
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('Should get a 400 if no userID', (done) => {
|
||||
request.get(utils.getbaseURL()
|
||||
+ "/api/isUserVIP", null,
|
||||
(err, res, body) => {
|
||||
if (err) done("couldn't call endpoint");
|
||||
else if (res.statusCode !== 400) done("non 400: " + res.statusCode);
|
||||
else done(); // pass
|
||||
});
|
||||
});
|
||||
|
||||
it('Should say a VIP is a VIP', (done) => {
|
||||
request.get(utils.getbaseURL()
|
||||
+ "/api/isUserVIP?userID=supertestman", null,
|
||||
(err, res, body) => {
|
||||
if (err) done("couldn't call endpoint");
|
||||
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
||||
else {
|
||||
if (JSON.parse(body).vip === true) done(); // pass
|
||||
else done("Result was non-vip when should have been vip");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should say a normal user is not a VIP', (done) => {
|
||||
request.get(utils.getbaseURL()
|
||||
+ "/api/isUserVIP?userID=regulartestman", null,
|
||||
(err, res, body) => {
|
||||
if (err) done("couldn't call endpoint");
|
||||
else if (res.statusCode !== 200) done("non 200: " + res.statusCode);
|
||||
else {
|
||||
if (JSON.parse(body).vip === false) done(); // pass
|
||||
else done("Result was vip when should have been non-vip");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
408
test/cases/noSegmentRecords.js
Normal file
408
test/cases/noSegmentRecords.js
Normal file
|
@ -0,0 +1,408 @@
|
|||
var request = require('request');
|
||||
|
||||
var utils = require('../utils.js');
|
||||
const getHash = require('../../src/utils/getHash.js');
|
||||
|
||||
var databases = require('../../src/databases/databases.js');
|
||||
const logger = require('../../src/utils/logger.js');
|
||||
var db = databases.db;
|
||||
|
||||
describe('noSegmentRecords', () => {
|
||||
before(() => {
|
||||
db.exec("INSERT INTO vipUsers (userID) VALUES ('" + getHash("VIPUser-noSegments") + "')");
|
||||
|
||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id', 'sponsor')");
|
||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id', 'intro')");
|
||||
|
||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id-1', 'sponsor')");
|
||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'no-segments-video-id-1', 'intro')");
|
||||
db.exec("INSERT INTO noSegments (userID, videoID, category) VALUES ('" + getHash("VIPUser-noSegments") + "', 'noSubmitVideo', 'sponsor')");
|
||||
});
|
||||
|
||||
it('Should update the database version when starting the application', (done) => {
|
||||
let version = db.prepare('get', 'SELECT key, value FROM config where key = ?', ['version']).value;
|
||||
if (version > 1) done();
|
||||
else done('Version isn\'t greater that 1. Version is ' + version);
|
||||
});
|
||||
|
||||
it('Should be able to submit categories not in video (http response)', (done) => {
|
||||
let json = {
|
||||
videoID: 'no-segments-video-id',
|
||||
userID: 'VIPUser-noSegments',
|
||||
categories: [
|
||||
'outro',
|
||||
'shilling',
|
||||
'shilling',
|
||||
'shil ling',
|
||||
'',
|
||||
'intro'
|
||||
]
|
||||
};
|
||||
|
||||
let expected = {
|
||||
submitted: [
|
||||
'outro',
|
||||
'shilling'
|
||||
]
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
if (JSON.stringify(body) === JSON.stringify(expected)) {
|
||||
done();
|
||||
} else {
|
||||
done("Incorrect response: expected " + JSON.stringify(expected) + " got " + JSON.stringify(body));
|
||||
}
|
||||
} else {
|
||||
console.log(body);
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to submit categories not in video (sql check)', (done) => {
|
||||
let json = {
|
||||
videoID: 'no-segments-video-id-1',
|
||||
userID: 'VIPUser-noSegments',
|
||||
categories: [
|
||||
'outro',
|
||||
'shilling',
|
||||
'shilling',
|
||||
'shil ling',
|
||||
'',
|
||||
'intro'
|
||||
]
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['no-segments-video-id-1']);
|
||||
if (result.length !== 4) {
|
||||
console.log(result);
|
||||
done("Expected 4 entrys in db, got " + result.length);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
console.log(body);
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to submit categories with _ in the category', (done) => {
|
||||
let json = {
|
||||
videoID: 'underscore',
|
||||
userID: 'VIPUser-noSegments',
|
||||
categories: [
|
||||
'word_word',
|
||||
]
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['underscore']);
|
||||
if (result.length !== 1) {
|
||||
console.log(result);
|
||||
done("Expected 1 entrys in db, got " + result.length);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
console.log(body);
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to submit categories with upper and lower case in the category', (done) => {
|
||||
let json = {
|
||||
videoID: 'bothCases',
|
||||
userID: 'VIPUser-noSegments',
|
||||
categories: [
|
||||
'wordWord',
|
||||
]
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['bothCases']);
|
||||
if (result.length !== 1) {
|
||||
console.log(result);
|
||||
done("Expected 1 entrys in db, got " + result.length);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
console.log(body);
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not be able to submit categories with $ in the category', (done) => {
|
||||
let json = {
|
||||
videoID: 'specialChar',
|
||||
userID: 'VIPUser-noSegments',
|
||||
categories: [
|
||||
'word&word',
|
||||
]
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
let result = db.prepare('all', 'SELECT * FROM noSegments WHERE videoID = ?', ['specialChar']);
|
||||
if (result.length !== 0) {
|
||||
console.log(result);
|
||||
done("Expected 0 entrys in db, got " + result.length);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
} else {
|
||||
console.log(body);
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 400 for missing params', (done) => {
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json: {}},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 400) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 400 for no categories', (done) => {
|
||||
let json = {
|
||||
videoID: 'test',
|
||||
userID: 'test',
|
||||
categories: []
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 400) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 400 for no userID', (done) => {
|
||||
let json = {
|
||||
videoID: 'test',
|
||||
userID: null,
|
||||
categories: ['sponsor']
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 400) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 400 for no videoID', (done) => {
|
||||
let json = {
|
||||
videoID: null,
|
||||
userID: 'test',
|
||||
categories: ['sponsor']
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 400) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 400 object categories)', (done) => {
|
||||
let json = {
|
||||
videoID: 'test',
|
||||
userID: 'test',
|
||||
categories: {}
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 400) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 400 bad format categories', (done) => {
|
||||
let json = {
|
||||
videoID: 'test',
|
||||
userID: 'test',
|
||||
categories: 'sponsor'
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 400) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return 403 if user is not VIP', (done) => {
|
||||
let json = {
|
||||
videoID: 'test',
|
||||
userID: 'test',
|
||||
categories: [
|
||||
'sponsor'
|
||||
]
|
||||
};
|
||||
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/noSegments", {json},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 403) {
|
||||
done();
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* Submission tests in this file do not check database records, only status codes.
|
||||
* To test the submission code properly see ./test/cases/postSkipSegments.js
|
||||
*/
|
||||
|
||||
it('Should not be able to submit a segment to a video with a no-segment record (single submission)', (done) => {
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/postVideoSponsorTimes", {
|
||||
json: {
|
||||
userID: "testman42",
|
||||
videoID: "noSubmitVideo",
|
||||
segments: [{
|
||||
segment: [20, 40],
|
||||
category: "sponsor"
|
||||
}]
|
||||
}
|
||||
},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 403) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not be able to submit segments to a video where any of the submissions with a no-segment record', (done) => {
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/postVideoSponsorTimes", {
|
||||
json: {
|
||||
userID: "testman42",
|
||||
videoID: "noSubmitVideo",
|
||||
segments: [{
|
||||
segment: [20, 40],
|
||||
category: "sponsor"
|
||||
},{
|
||||
segment: [50, 60],
|
||||
category: "intro"
|
||||
}]
|
||||
}
|
||||
},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 403) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('Should be able to submit a segment to a video with a different no-segment record', (done) => {
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/postVideoSponsorTimes", {
|
||||
json: {
|
||||
userID: "testman42",
|
||||
videoID: "noSubmitVideo",
|
||||
segments: [{
|
||||
segment: [20, 40],
|
||||
category: "intro"
|
||||
}]
|
||||
}
|
||||
},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to submit a segment to a video with no no-segment records', (done) => {
|
||||
request.post(utils.getbaseURL()
|
||||
+ "/api/postVideoSponsorTimes", {
|
||||
json: {
|
||||
userID: "testman42",
|
||||
videoID: "normalVideo",
|
||||
segments: [{
|
||||
segment: [20, 40],
|
||||
category: "intro"
|
||||
}]
|
||||
}
|
||||
},
|
||||
(err, res, body) => {
|
||||
if (err) done(err);
|
||||
else if (res.statusCode === 200) {
|
||||
done()
|
||||
} else {
|
||||
done("Status code was " + res.statusCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -20,6 +20,8 @@ describe('voteOnSponsorTime', () => {
|
|||
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-9', '" + getHash("randomID2") + "', 0, 50, 'sponsor', 0)");
|
||||
db.exec(startOfQuery + "('voter-submitter2', 1, 11, 2, 'vote-uuid-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,
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in a new issue