mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2026-01-29 05:40:49 +03:00
Merge pull request #81 from andrewzlee/nb-mod-fetch
Add NB to automod process
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -99,4 +99,7 @@ test/databases/sponsorTimes.db-wal
|
|||||||
test/databases/private.db
|
test/databases/private.db
|
||||||
|
|
||||||
# Config files
|
# Config files
|
||||||
config.json
|
config.json
|
||||||
|
|
||||||
|
# Mac files
|
||||||
|
.DS_Store
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
"discordReportChannelWebhookURL": null, //URL from discord if you would like notifications when someone makes a report [optional]
|
"discordReportChannelWebhookURL": null, //URL from discord if you would like notifications when someone makes a report [optional]
|
||||||
"discordFirstTimeSubmissionsWebhookURL": null, //URL from discord if you would like notifications when someone makes a first time submission [optional]
|
"discordFirstTimeSubmissionsWebhookURL": null, //URL from discord if you would like notifications when someone makes a first time submission [optional]
|
||||||
"discordCompletelyIncorrectReportWebhookURL": null, //URL from discord if you would like notifications when someone reports a submission as completely incorrect [optional]
|
"discordCompletelyIncorrectReportWebhookURL": null, //URL from discord if you would like notifications when someone reports a submission as completely incorrect [optional]
|
||||||
|
"neuralBlockURL": null, // URL to check submissions against neural block. Ex. https://ai.neuralblock.app
|
||||||
|
"discordNeuralBlockRejectWebhookURL": null, //URL from discord if you would like notifications when NeuralBlock rejects a submission [optional]
|
||||||
"userCounterURL": null, // For user counting. URL to instance of https://github.com/ajayyy/PrivacyUserCount
|
"userCounterURL": null, // For user counting. URL to instance of https://github.com/ajayyy/PrivacyUserCount
|
||||||
"proxySubmission": null, // Base url to proxy submissions to persist // e.g. https://sponsor.ajay.app (no trailing slash)
|
"proxySubmission": null, // Base url to proxy submissions to persist // e.g. https://sponsor.ajay.app (no trailing slash)
|
||||||
"behindProxy": "X-Forwarded-For", //Options: "X-Forwarded-For", "Cloudflare", "X-Real-IP", anything else will mean it is not behind a proxy. True defaults to "X-Forwarded-For"
|
"behindProxy": "X-Forwarded-For", //Options: "X-Forwarded-For", "Cloudflare", "X-Real-IP", anything else will mean it is not behind a proxy. True defaults to "X-Forwarded-For"
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"iso8601-duration": "^1.2.0",
|
"iso8601-duration": "^1.2.0",
|
||||||
"sync-mysql": "^3.0.1",
|
"sync-mysql": "^3.0.1",
|
||||||
"uuid": "^3.3.2",
|
"uuid": "^3.3.2",
|
||||||
"youtube-api": "^2.0.10"
|
"youtube-api": "^2.0.10",
|
||||||
|
"node-fetch": "^2.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^7.1.1",
|
"mocha": "^7.1.1",
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
var config = require('../config.js');
|
const config = require('../config.js');
|
||||||
|
|
||||||
var databases = require('../databases/databases.js');
|
const databases = require('../databases/databases.js');
|
||||||
var db = databases.db;
|
const db = databases.db;
|
||||||
var privateDB = databases.privateDB;
|
const privateDB = databases.privateDB;
|
||||||
var YouTubeAPI = require('../utils/youtubeAPI.js');
|
const YouTubeAPI = require('../utils/youtubeAPI.js');
|
||||||
var logger = require('../utils/logger.js');
|
const logger = require('../utils/logger.js');
|
||||||
var request = require('request');
|
const request = require('request');
|
||||||
var isoDurations = require('iso8601-duration');
|
const isoDurations = require('iso8601-duration');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
var getHash = require('../utils/getHash.js');
|
const getHash = require('../utils/getHash.js');
|
||||||
var getIP = require('../utils/getIP.js');
|
const getIP = require('../utils/getIP.js');
|
||||||
var getFormattedTime = require('../utils/getFormattedTime.js');
|
const getFormattedTime = require('../utils/getFormattedTime.js');
|
||||||
var isUserTrustworthy = require('../utils/isUserTrustworthy.js');
|
const isUserTrustworthy = require('../utils/isUserTrustworthy.js')
|
||||||
const { dispatchEvent } = require('../utils/webhookUtils.js');
|
const { dispatchEvent } = require('../utils/webhookUtils.js');
|
||||||
|
|
||||||
function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtubeData, {submissionStart, submissionEnd}, segmentInfo) {
|
function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtubeData, {submissionStart, submissionEnd}, segmentInfo) {
|
||||||
@@ -56,11 +57,11 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
|||||||
err && logger.error(err);
|
err && logger.error(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let startTime = parseFloat(segmentInfo.segment[0]);
|
let startTime = parseFloat(segmentInfo.segment[0]);
|
||||||
let endTime = parseFloat(segmentInfo.segment[1]);
|
let endTime = parseFloat(segmentInfo.segment[1]);
|
||||||
sendWebhookNotification(userID, videoID, UUID, userSubmissionCountRow.submissionCount, data, {submissionStart: startTime, submissionEnd: endTime}, segmentInfo);
|
sendWebhookNotification(userID, videoID, UUID, userSubmissionCountRow.submissionCount, data, {submissionStart: startTime, submissionEnd: endTime}, segmentInfo);
|
||||||
|
|
||||||
// If it is a first time submission
|
// If it is a first time submission
|
||||||
// Then send a notification to discord
|
// Then send a notification to discord
|
||||||
if (config.discordFirstTimeSubmissionsWebhookURL === null || userSubmissionCountRow.submissionCount > 1) return;
|
if (config.discordFirstTimeSubmissionsWebhookURL === null || userSubmissionCountRow.submissionCount > 1) return;
|
||||||
@@ -70,7 +71,7 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
|||||||
"title": data.items[0].snippet.title,
|
"title": data.items[0].snippet.title,
|
||||||
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2),
|
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2),
|
||||||
"description": "Submission ID: " + UUID +
|
"description": "Submission ID: " + UUID +
|
||||||
"\n\nTimestamp: " +
|
"\n\nTimestamp: " +
|
||||||
getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||||
"\n\nCategory: " + segmentInfo.category,
|
"\n\nCategory: " + segmentInfo.category,
|
||||||
"color": 10813440,
|
"color": 10813440,
|
||||||
@@ -82,7 +83,7 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}, (err, res) => {
|
}, (err, res) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error("Failed to send first time submission Discord hook.");
|
logger.error("Failed to send first time submission Discord hook.");
|
||||||
logger.error(JSON.stringify(err));
|
logger.error(JSON.stringify(err));
|
||||||
@@ -97,19 +98,66 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// submission: {videoID, startTime, endTime}
|
function sendWebhooksNB(userID, videoID, UUID, startTime, endTime, category, probability, ytData) {
|
||||||
|
let submissionInfoRow = db.prepare('get', "SELECT " +
|
||||||
|
"(select count(1) from sponsorTimes where userID = ?) count, " +
|
||||||
|
"(select count(1) from sponsorTimes where userID = ? and votes <= -2) disregarded, " +
|
||||||
|
"coalesce((select userName FROM userNames WHERE userID = ?), ?) userName",
|
||||||
|
[userID, userID, userID, userID]);
|
||||||
|
|
||||||
|
let submittedBy = "";
|
||||||
|
// If a userName was created then show both
|
||||||
|
if (submissionInfoRow.userName !== userID){
|
||||||
|
submittedBy = submissionInfoRow.userName + "\n " + userID;
|
||||||
|
} else {
|
||||||
|
submittedBy = userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send discord message
|
||||||
|
if (config.discordNeuralBlockRejectWebhookURL === null) return;
|
||||||
|
request.post(config.discordNeuralBlockRejectWebhookURL, {
|
||||||
|
json: {
|
||||||
|
"embeds": [{
|
||||||
|
"title": ytData.items[0].snippet.title,
|
||||||
|
"url": "https://www.youtube.com/watch?v=" + videoID + "&t=" + (startTime.toFixed(0) - 2),
|
||||||
|
"description": "**Submission ID:** " + UUID +
|
||||||
|
"\n**Timestamp:** " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) +
|
||||||
|
"\n**Predicted Probability:** " + probability +
|
||||||
|
"\n**Category:** " + category +
|
||||||
|
"\n**Submitted by:** "+ submittedBy +
|
||||||
|
"\n**Total User Submissions:** "+submissionInfoRow.count +
|
||||||
|
"\n**Ignored User Submissions:** "+submissionInfoRow.disregarded,
|
||||||
|
"color": 10813440,
|
||||||
|
"thumbnail": {
|
||||||
|
"url": ytData.items[0].snippet.thumbnails.maxres ? ytData.items[0].snippet.thumbnails.maxres.url : "",
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error("Failed to send NeuralBlock Discord hook.");
|
||||||
|
logger.error(JSON.stringify(err));
|
||||||
|
logger.error("\n");
|
||||||
|
} else if (res && res.statusCode >= 400) {
|
||||||
|
logger.error("Error sending NeuralBlock Discord hook");
|
||||||
|
logger.error(JSON.stringify(res));
|
||||||
|
logger.error("\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// callback: function(reject: "String containing reason the submission was rejected")
|
// callback: function(reject: "String containing reason the submission was rejected")
|
||||||
// returns: string when an error, false otherwise
|
// returns: string when an error, false otherwise
|
||||||
|
|
||||||
// Looks like this was broken for no defined youtube key - fixed but IMO we shouldn't return
|
// Looks like this was broken for no defined youtube key - fixed but IMO we shouldn't return
|
||||||
// false for a pass - it was confusing and lead to this bug - any use of this function in
|
// false for a pass - it was confusing and lead to this bug - any use of this function in
|
||||||
// the future could have the same problem.
|
// the future could have the same problem.
|
||||||
async function autoModerateSubmission(submission, callback) {
|
async function autoModerateSubmission(submission) {
|
||||||
// Get the video information from the youtube API
|
// Get the video information from the youtube API
|
||||||
if (config.youtubeAPIKey !== null) {
|
if (config.youtubeAPIKey !== null) {
|
||||||
let {err, data} = await new Promise((resolve, reject) => {
|
let {err, data} = await new Promise((resolve, reject) => {
|
||||||
YouTubeAPI.videos.list({
|
YouTubeAPI.videos.list({
|
||||||
part: "contentDetails",
|
part: "contentDetails,snippet",
|
||||||
id: submission.videoID
|
id: submission.videoID
|
||||||
}, (err, data) => resolve({err, data}));
|
}, (err, data) => resolve({err, data}));
|
||||||
});
|
});
|
||||||
@@ -121,20 +169,62 @@ async function autoModerateSubmission(submission, callback) {
|
|||||||
if (data.pageInfo.totalResults === 0) {
|
if (data.pageInfo.totalResults === 0) {
|
||||||
return "No video exists with id " + submission.videoID;
|
return "No video exists with id " + submission.videoID;
|
||||||
} else {
|
} else {
|
||||||
let duration = data.items[0].contentDetails.duration;
|
let segments = submission.segments;
|
||||||
duration = isoDurations.toSeconds(isoDurations.parse(duration));
|
let nbString = "";
|
||||||
if (duration == 0) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
// Allow submission if the duration is 0 (bug in youtube api)
|
let startTime = parseFloat(segments[i].segment[0]);
|
||||||
return false;
|
let endTime = parseFloat(segments[i].segment[1]);
|
||||||
} else if ((submission.endTime - submission.startTime) > (duration/100)*80) {
|
|
||||||
// Reject submission if over 80% of the video
|
let duration = data.items[0].contentDetails.duration;
|
||||||
return "Sponsor segment is over 80% of the video.";
|
duration = isoDurations.toSeconds(isoDurations.parse(duration));
|
||||||
|
if (duration == 0) {
|
||||||
|
// Allow submission if the duration is 0 (bug in youtube api)
|
||||||
|
return false;
|
||||||
|
} else if ((endTime - startTime) > (duration/100)*80) {
|
||||||
|
// Reject submission if over 80% of the video
|
||||||
|
return "One of your submitted segments is over 80% of the video.";
|
||||||
|
} else {
|
||||||
|
if (segments[i].category === "sponsor") {
|
||||||
|
//Prepare timestamps to send to NB all at once
|
||||||
|
nbString = nbString + segments[i].segment[0] + "," + segments[i].segment[1] + ";";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check NeuralBlock
|
||||||
|
let neuralBlockURL = config.neuralBlockURL;
|
||||||
|
if (!neuralBlockURL) return false;
|
||||||
|
let response = await fetch(neuralBlockURL + "/api/checkSponsorSegments?vid=" + submission.videoID +
|
||||||
|
"&segments=" + nbString.substring(0,nbString.length-1));
|
||||||
|
if (!response.ok) return false;
|
||||||
|
|
||||||
|
let nbPredictions = await response.json();
|
||||||
|
nbDecision = false;
|
||||||
|
let predictionIdx = 0; //Keep track because only sponsor categories were submitted
|
||||||
|
for (let i = 0; i < segments.length; i++){
|
||||||
|
if (segments[i].category === "sponsor"){
|
||||||
|
if (nbPredictions.probabilities[predictionIdx] < 0.70){
|
||||||
|
nbDecision = true; // At least one bad entry
|
||||||
|
startTime = parseFloat(segments[i].segment[0]);
|
||||||
|
endTime = parseFloat(segments[i].segment[1]);
|
||||||
|
|
||||||
|
let UUID = getHash("v2-categories" + submission.videoID + startTime +
|
||||||
|
endTime + segments[i].category + submission.userID, 1);
|
||||||
|
// Send to Discord
|
||||||
|
// Note, if this is too spammy. Consider sending all the segments as one Webhook
|
||||||
|
sendWebhooksNB(submission.userID, submission.videoID, UUID, startTime, endTime, segments[i].category, nbPredictions.probabilities[predictionIdx], data);
|
||||||
|
}
|
||||||
|
predictionIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (nbDecision){
|
||||||
|
return "Rejected based on NeuralBlock predictions.";
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logger.debug("Skipped YouTube API");
|
logger.debug("Skipped YouTube API");
|
||||||
|
|
||||||
@@ -187,6 +277,11 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
//hash the ip 5000 times so no one can get it from the database
|
//hash the ip 5000 times so no one can get it from the database
|
||||||
let hashedIP = getHash(getIP(req) + config.globalSalt);
|
let hashedIP = getHash(getIP(req) + config.globalSalt);
|
||||||
|
|
||||||
|
//check if this user is on the vip list
|
||||||
|
let isVIP = db.prepare("get", "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]).userCount > 0;
|
||||||
|
|
||||||
|
let decreaseVotes = 0;
|
||||||
|
|
||||||
// Check if all submissions are correct
|
// Check if all submissions are correct
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) {
|
if (segments[i] === undefined || segments[i].segment === undefined || segments[i].category === undefined) {
|
||||||
@@ -223,21 +318,27 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
res.sendStatus(409);
|
res.sendStatus(409);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let autoModerateResult = await autoModerateSubmission({videoID, startTime, endTime});
|
// Auto moderator check
|
||||||
if (autoModerateResult) {
|
if (!isVIP) {
|
||||||
res.status(403).send("Request rejected by auto moderator: " + autoModerateResult);
|
let autoModerateResult = await autoModerateSubmission({userID, videoID, segments});//startTime, endTime, category: segments[i].category});
|
||||||
|
if (autoModerateResult == "Rejected based on NeuralBlock predictions."){
|
||||||
|
// If NB automod rejects, the submission will start with -2 votes.
|
||||||
|
// Note, if one submission is bad all submissions will be affected.
|
||||||
|
// However, this behavior is consistent with other automod functions
|
||||||
|
// already in place.
|
||||||
|
//decreaseVotes = -2; //Disable for now
|
||||||
|
} else if (autoModerateResult) {
|
||||||
|
//Normal automod behavior
|
||||||
|
res.status(403).send("Request rejected by auto moderator: " + autoModerateResult + " If this is an issue, send a message on Discord.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will be filled when submitting
|
// Will be filled when submitting
|
||||||
let UUIDs = [];
|
let UUIDs = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//check if this user is on the vip list
|
|
||||||
let vipRow = db.prepare('get', "SELECT count(*) as userCount FROM vipUsers WHERE userID = ?", [userID]);
|
|
||||||
|
|
||||||
//get current time
|
//get current time
|
||||||
let timeSubmitted = Date.now();
|
let timeSubmitted = Date.now();
|
||||||
|
|
||||||
@@ -247,7 +348,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
if (false) {
|
if (false) {
|
||||||
//check to see if this ip has submitted too many sponsors today
|
//check to see if this ip has submitted too many sponsors today
|
||||||
let rateLimitCheckRow = privateDB.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?", [hashedIP, videoID, yesterday]);
|
let rateLimitCheckRow = privateDB.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?", [hashedIP, videoID, yesterday]);
|
||||||
|
|
||||||
if (rateLimitCheckRow.count >= 10) {
|
if (rateLimitCheckRow.count >= 10) {
|
||||||
//too many sponsors for the same video from the same ip address
|
//too many sponsors for the same video from the same ip address
|
||||||
res.sendStatus(429);
|
res.sendStatus(429);
|
||||||
@@ -260,7 +361,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
if (false) {
|
if (false) {
|
||||||
//check to see if the user has already submitted sponsors for this video
|
//check to see if the user has already submitted sponsors for this video
|
||||||
let duplicateCheckRow = db.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?", [userID, videoID]);
|
let duplicateCheckRow = db.prepare('get', "SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?", [userID, videoID]);
|
||||||
|
|
||||||
if (duplicateCheckRow.count >= 16) {
|
if (duplicateCheckRow.count >= 16) {
|
||||||
//too many sponsors for the same video from the same user
|
//too many sponsors for the same video from the same user
|
||||||
res.sendStatus(429);
|
res.sendStatus(429);
|
||||||
@@ -279,8 +380,8 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
shadowBanned = 1;
|
shadowBanned = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let startingVotes = 0;
|
let startingVotes = 0 + decreaseVotes;
|
||||||
if (vipRow.userCount > 0) {
|
if (isVIP) {
|
||||||
//this user is a vip, start them at a higher approval rating
|
//this user is a vip, start them at a higher approval rating
|
||||||
startingVotes = 10000;
|
startingVotes = 10000;
|
||||||
}
|
}
|
||||||
@@ -289,26 +390,29 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
//this can just be a hash of the data
|
//this can just be a hash of the data
|
||||||
//it's better than generating an actual UUID like what was used before
|
//it's better than generating an actual UUID like what was used before
|
||||||
//also better for duplication checking
|
//also better for duplication checking
|
||||||
let UUID = getHash("v2-categories" + videoID + segmentInfo.segment[0] +
|
let UUID = getHash("v2-categories" + videoID + segmentInfo.segment[0] +
|
||||||
segmentInfo.segment[1] + segmentInfo.category + userID, 1);
|
segmentInfo.segment[1] + segmentInfo.category + userID, 1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.prepare('run', "INSERT INTO sponsorTimes " +
|
db.prepare('run', "INSERT INTO sponsorTimes " +
|
||||||
"(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden)" +
|
"(videoID, startTime, endTime, votes, UUID, userID, timeSubmitted, views, category, shadowHidden)" +
|
||||||
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [videoID, segmentInfo.segment[0],
|
"VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [videoID, segmentInfo.segment[0],
|
||||||
segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned]);
|
segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned]);
|
||||||
|
|
||||||
//add to private db as well
|
//add to private db as well
|
||||||
privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]);
|
privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//a DB change probably occurred
|
//a DB change probably occurred
|
||||||
res.sendStatus(502);
|
res.sendStatus(502);
|
||||||
logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
logger.error("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
||||||
segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err);
|
segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discord notification
|
||||||
|
sendWebhooks(userID, videoID, UUID, segmentInfo);
|
||||||
|
|
||||||
UUIDs.push(UUID);
|
UUIDs.push(UUID);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
"globalSalt": "testSalt",
|
"globalSalt": "testSalt",
|
||||||
"adminUserID": "testUserId",
|
"adminUserID": "testUserId",
|
||||||
"youtubeAPIKey": "",
|
"youtubeAPIKey": "",
|
||||||
"discordReportChannelWebhookURL": "http://127.0.0.1:8081/ReportChannelWebhook",
|
"discordReportChannelWebhookURL": "http://127.0.0.1:8081/ReportChannelWebhook",
|
||||||
"discordFirstTimeSubmissionsWebhookURL": "http://127.0.0.1:8081/FirstTimeSubmissionsWebhook",
|
"discordFirstTimeSubmissionsWebhookURL": "http://127.0.0.1:8081/FirstTimeSubmissionsWebhook",
|
||||||
"discordCompletelyIncorrectReportWebhookURL": "http://127.0.0.1:8081/CompletelyIncorrectReportWebhook",
|
"discordCompletelyIncorrectReportWebhookURL": "http://127.0.0.1:8081/CompletelyIncorrectReportWebhook",
|
||||||
|
"discordNeuralBlockRejectWebhookURL": "http://127.0.0.1:8081/NeuralBlockRejectWebhook",
|
||||||
|
"neuralBlockURL": "http://127.0.0.1:8081/NeuralBlock",
|
||||||
"behindProxy": true,
|
"behindProxy": true,
|
||||||
"db": "./test/databases/sponsorTimes.db",
|
"db": "./test/databases/sponsorTimes.db",
|
||||||
"privateDB": "./test/databases/private.db",
|
"privateDB": "./test/databases/private.db",
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ var db = databases.db;
|
|||||||
|
|
||||||
describe('postSkipSegments', () => {
|
describe('postSkipSegments', () => {
|
||||||
it('Should be able to submit a single time (Params method)', (done) => {
|
it('Should be able to submit a single time (Params method)', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes?videoID=dQw4w9WgXcR&startTime=2&endTime=10&userID=test&category=sponsor", null,
|
+ "/api/postVideoSponsorTimes?videoID=dQw4w9WgXcR&startTime=2&endTime=10&userID=test&category=sponsor", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
else if (res.statusCode === 200) {
|
else if (res.statusCode === 200) {
|
||||||
@@ -26,7 +26,7 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should be able to submit a single time (JSON method)', (done) => {
|
it('Should be able to submit a single time (JSON method)', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
@@ -36,7 +36,7 @@ describe('postSkipSegments', () => {
|
|||||||
category: "sponsor"
|
category: "sponsor"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
else if (res.statusCode === 200) {
|
else if (res.statusCode === 200) {
|
||||||
@@ -53,7 +53,7 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should be able to submit multiple times (JSON method)', (done) => {
|
it('Should be able to submit multiple times (JSON method)', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
@@ -66,7 +66,7 @@ describe('postSkipSegments', () => {
|
|||||||
category: "intro"
|
category: "intro"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
else if (res.statusCode === 200) {
|
else if (res.statusCode === 200) {
|
||||||
@@ -88,11 +88,11 @@ describe('postSkipSegments', () => {
|
|||||||
done("Status code was " + res.statusCode);
|
done("Status code was " + res.statusCode);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}).timeout(5000);
|
||||||
|
|
||||||
it('Should be accepted if a non-sponsor is less than 1 second', (done) => {
|
it('Should be accepted if a non-sponsor is less than 1 second', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing&category=intro", null,
|
+ "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing&category=intro", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done("Couldn't call endpoint");
|
if (err) done("Couldn't call endpoint");
|
||||||
else if (res.statusCode === 200) done(); // pass
|
else if (res.statusCode === 200) done(); // pass
|
||||||
@@ -101,8 +101,8 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should be rejected if a sponsor is less than 1 second', (done) => {
|
it('Should be rejected if a sponsor is less than 1 second', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing", null,
|
+ "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done("Couldn't call endpoint");
|
if (err) done("Couldn't call endpoint");
|
||||||
else if (res.statusCode === 400) done(); // pass
|
else if (res.statusCode === 400) done(); // pass
|
||||||
@@ -111,8 +111,8 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should be rejected if over 80% of the video', (done) => {
|
it('Should be rejected if over 80% of the video', (done) => {
|
||||||
request.get(utils.getbaseURL()
|
request.get(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes?videoID=qqwerty&startTime=30&endTime=1000000&userID=testing", null,
|
+ "/api/postVideoSponsorTimes?videoID=qqwerty&startTime=30&endTime=1000000&userID=testing", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done("Couldn't call endpoint");
|
if (err) done("Couldn't call endpoint");
|
||||||
else if (res.statusCode === 403) done(); // pass
|
else if (res.statusCode === 403) done(); // pass
|
||||||
@@ -120,19 +120,29 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be allowed if youtube thinks duration is 0', (done) => {
|
it("Should be rejected if NB's predicted probability is <70%.", (done) => {
|
||||||
request.get(utils.getbaseURL()
|
request.get(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes?videoID=noDuration&startTime=30&endTime=10000&userID=testing", null,
|
+ "/api/postVideoSponsorTimes?videoID=LevkAjUE6d4&startTime=40&endTime=60&userID=testing", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done("Couldn't call endpoint");
|
if (err) done("Couldn't call endpoint");
|
||||||
else if (res.statusCode === 200) done(); // pass
|
else if (res.statusCode === 200) done(); // pass
|
||||||
else done("non 200 status code: " + res.statusCode + " ("+body+")");
|
else done("non 200 status code: " + res.statusCode + " ("+body+")");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should be allowed if youtube thinks duration is 0', (done) => {
|
||||||
|
request.get(utils.getbaseURL()
|
||||||
|
+ "/api/postVideoSponsorTimes?videoID=noDuration&startTime=30&endTime=10000&userID=testing", null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode === 200) done(); // pass
|
||||||
|
else done("non 200 status code: " + res.statusCode + " ("+body+")");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Should be rejected if not a valid videoID', (done) => {
|
it('Should be rejected if not a valid videoID', (done) => {
|
||||||
request.get(utils.getbaseURL()
|
request.get(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes?videoID=knownWrongID&startTime=30&endTime=1000000&userID=testing", null,
|
+ "/api/postVideoSponsorTimes?videoID=knownWrongID&startTime=30&endTime=1000000&userID=testing", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done("Couldn't call endpoint");
|
if (err) done("Couldn't call endpoint");
|
||||||
else if (res.statusCode === 403) done(); // pass
|
else if (res.statusCode === 403) done(); // pass
|
||||||
@@ -141,8 +151,8 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should return 400 for missing params (Params method)', (done) => {
|
it('Should return 400 for missing params (Params method)', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes?startTime=9&endTime=10&userID=test", null,
|
+ "/api/postVideoSponsorTimes?startTime=9&endTime=10&userID=test", null,
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(true);
|
if (err) done(true);
|
||||||
if (res.statusCode === 400) done();
|
if (res.statusCode === 400) done();
|
||||||
@@ -151,7 +161,7 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Should return 400 for missing params (JSON method) 1', (done) => {
|
it('Should return 400 for missing params (JSON method) 1', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
@@ -163,7 +173,7 @@ describe('postSkipSegments', () => {
|
|||||||
category: "intro"
|
category: "intro"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(true);
|
if (err) done(true);
|
||||||
else if (res.statusCode === 400) done();
|
else if (res.statusCode === 400) done();
|
||||||
@@ -171,13 +181,13 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('Should return 400 for missing params (JSON method) 2', (done) => {
|
it('Should return 400 for missing params (JSON method) 2', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
videoID: "dQw4w9WgXcQ"
|
videoID: "dQw4w9WgXcQ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(true);
|
if (err) done(true);
|
||||||
else if (res.statusCode === 400) done();
|
else if (res.statusCode === 400) done();
|
||||||
@@ -185,7 +195,7 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('Should return 400 for missing params (JSON method) 3', (done) => {
|
it('Should return 400 for missing params (JSON method) 3', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
@@ -198,7 +208,7 @@ describe('postSkipSegments', () => {
|
|||||||
category: "intro"
|
category: "intro"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(true);
|
if (err) done(true);
|
||||||
else if (res.statusCode === 400) done();
|
else if (res.statusCode === 400) done();
|
||||||
@@ -206,7 +216,7 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('Should return 400 for missing params (JSON method) 4', (done) => {
|
it('Should return 400 for missing params (JSON method) 4', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
@@ -218,7 +228,7 @@ describe('postSkipSegments', () => {
|
|||||||
category: "intro"
|
category: "intro"
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(true);
|
if (err) done(true);
|
||||||
else if (res.statusCode === 400) done();
|
else if (res.statusCode === 400) done();
|
||||||
@@ -226,17 +236,17 @@ describe('postSkipSegments', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('Should return 400 for missing params (JSON method) 5', (done) => {
|
it('Should return 400 for missing params (JSON method) 5', (done) => {
|
||||||
request.post(utils.getbaseURL()
|
request.post(utils.getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
json: {
|
json: {
|
||||||
userID: "test",
|
userID: "test",
|
||||||
videoID: "dQw4w9WgXcQ"
|
videoID: "dQw4w9WgXcQ"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err, res, body) => {
|
(err, res, body) => {
|
||||||
if (err) done(true);
|
if (err) done(true);
|
||||||
else if (res.statusCode === 400) done();
|
else if (res.statusCode === 400) done();
|
||||||
else done(true);
|
else done(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,12 +15,37 @@ app.post('/CompletelyIncorrectReportWebhook', (req, res) => {
|
|||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Testing NeuralBlock
|
||||||
|
app.post('/NeuralBlockRejectWebhook', (req, res) => {
|
||||||
|
res.sendStatus(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/NeuralBlock/api/checkSponsorSegments', (req, res) => {
|
||||||
|
if (req.query.vid === "LevkAjUE6d4") {
|
||||||
|
res.json({
|
||||||
|
probabilities: [0.69]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
//getSponsorSegments is no longer being used for automod
|
||||||
|
app.get('/NeuralBlock/api/getSponsorSegments', (req, res) => {
|
||||||
|
if (req.query.vid === "LevkAjUE6d4") {
|
||||||
|
res.json({
|
||||||
|
sponsorSegments: [[0.47,7.549],[264.023,317.293]]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.sendStatus(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Testing webhooks
|
||||||
app.post('/CustomWebhook', (req, res) => {
|
app.post('/CustomWebhook', (req, res) => {
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = function createMockServer(callback) {
|
module.exports = function createMockServer(callback) {
|
||||||
return app.listen(config.mockPort, callback);
|
return app.listen(config.mockPort, callback);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user