mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-13 06:57:05 +03:00
Added automod check with NB
This commit is contained in:
@@ -17,7 +17,8 @@
|
|||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"iso8601-duration": "^1.2.0",
|
"iso8601-duration": "^1.2.0",
|
||||||
"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",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ var isoDurations = require('iso8601-duration');
|
|||||||
var getHash = require('../utils/getHash.js');
|
var getHash = require('../utils/getHash.js');
|
||||||
var getIP = require('../utils/getIP.js');
|
var getIP = require('../utils/getIP.js');
|
||||||
var getFormattedTime = require('../utils/getFormattedTime.js');
|
var getFormattedTime = require('../utils/getFormattedTime.js');
|
||||||
|
const fetch = require('node-fetch');
|
||||||
|
|
||||||
// TODO: might need to be a util
|
// TODO: might need to be a util
|
||||||
//returns true if the user is considered trustworthy
|
//returns true if the user is considered trustworthy
|
||||||
@@ -21,8 +22,8 @@ async function isUserTrustworthy(userID) {
|
|||||||
if (totalSubmissionsRow.totalSubmissions > 5) {
|
if (totalSubmissionsRow.totalSubmissions > 5) {
|
||||||
//check if they have a high downvote ratio
|
//check if they have a high downvote ratio
|
||||||
let downvotedSubmissionsRow = db.prepare("SELECT count(*) as downvotedSubmissions FROM sponsorTimes WHERE userID = ? AND (votes < 0 OR shadowHidden > 0)").get(userID);
|
let downvotedSubmissionsRow = db.prepare("SELECT count(*) as downvotedSubmissions FROM sponsorTimes WHERE userID = ? AND (votes < 0 OR shadowHidden > 0)").get(userID);
|
||||||
|
|
||||||
return (downvotedSubmissionsRow.downvotedSubmissions / totalSubmissionsRow.totalSubmissions) < 0.6 ||
|
return (downvotedSubmissionsRow.downvotedSubmissions / totalSubmissionsRow.totalSubmissions) < 0.6 ||
|
||||||
(totalSubmissionsRow.voteSum > downvotedSubmissionsRow.downvotedSubmissions);
|
(totalSubmissionsRow.voteSum > downvotedSubmissionsRow.downvotedSubmissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,14 +49,14 @@ function sendDiscordNotification(userID, videoID, UUID, segmentInfo) {
|
|||||||
|
|
||||||
let startTime = parseFloat(segmentInfo.segment[0]);
|
let startTime = parseFloat(segmentInfo.segment[0]);
|
||||||
let endTime = parseFloat(segmentInfo.segment[1]);
|
let endTime = parseFloat(segmentInfo.segment[1]);
|
||||||
|
|
||||||
request.post(config.discordFirstTimeSubmissionsWebhookURL, {
|
request.post(config.discordFirstTimeSubmissionsWebhookURL, {
|
||||||
json: {
|
json: {
|
||||||
"embeds": [{
|
"embeds": [{
|
||||||
"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,
|
||||||
@@ -110,11 +111,30 @@ async function autoModerateSubmission(submission, callback) {
|
|||||||
if ((submission.endTime - submission.startTime) > (duration/100)*80) {
|
if ((submission.endTime - submission.startTime) > (duration/100)*80) {
|
||||||
return "Sponsor segment is over 80% of the video.";
|
return "Sponsor segment is over 80% of the video.";
|
||||||
} else {
|
} else {
|
||||||
return false;
|
let overlap = false;
|
||||||
|
nb_predictions = fetch("https://ai.neuralblock.app/api/getSponsorSegments?vid=" + submission.videoID).then().then();
|
||||||
|
for (nb_seg in nb_predictions.sponsorSegments){
|
||||||
|
let head = 0;
|
||||||
|
let tail = 0;
|
||||||
|
// If there's an overlap, find the percentage of overlap.
|
||||||
|
if (submission.startTime <= nb_seg[1] && nb_seg[0] <= submission.endTime){
|
||||||
|
head = Math.max(submission.startTime, nb_seg[0]);
|
||||||
|
tail = Math.min(submission.endTime, nb_seg[1]);
|
||||||
|
}
|
||||||
|
if ((tail-head)/(nb_seg[1]-nb_seg[0]) > 0.65){
|
||||||
|
overlap = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (overlap){
|
||||||
|
return "Sponsor segment has passed checks.";
|
||||||
|
} else{
|
||||||
|
return "Sponsor segment doesn't have at least 65% match.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log("Skipped YouTube API");
|
console.log("Skipped YouTube API");
|
||||||
|
|
||||||
@@ -195,7 +215,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
|
|
||||||
//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("SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?").get([hashedIP, videoID, yesterday]);
|
let rateLimitCheckRow = privateDB.prepare("SELECT COUNT(*) as count FROM sponsorTimes WHERE hashedIP = ? AND videoID = ? AND timeSubmitted > ?").get([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);
|
||||||
@@ -205,7 +225,7 @@ module.exports = async function postSkipSegments(req, res) {
|
|||||||
|
|
||||||
//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("SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?").get([userID, videoID]);
|
let duplicateCheckRow = db.prepare("SELECT COUNT(*) as count FROM sponsorTimes WHERE userID = ? and videoID = ?").get([userID, videoID]);
|
||||||
|
|
||||||
if (duplicateCheckRow.count >= 8) {
|
if (duplicateCheckRow.count >= 8) {
|
||||||
//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);
|
||||||
@@ -233,24 +253,24 @@ 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("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, segmentInfo.segment[0],
|
db.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(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("INSERT INTO sponsorTimes VALUES(?, ?, ?)").run(videoID, hashedIP, timeSubmitted);
|
privateDB.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?)").run(videoID, hashedIP, timeSubmitted);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
//a DB change probably occurred
|
//a DB change probably occurred
|
||||||
res.sendStatus(502);
|
res.sendStatus(502);
|
||||||
console.log("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " +
|
console.log("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
|
// Discord notification
|
||||||
sendDiscordNotification(userID, videoID, UUID, segmentInfo);
|
sendDiscordNotification(userID, videoID, UUID, segmentInfo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
@@ -91,18 +91,28 @@ 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
|
||||||
else done("non 403 status code: " + res.statusCode + " ("+body+")");
|
else done("non 403 status code: " + res.statusCode + " ("+body+")");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should be rejected if there\'s not at least 65% overlap with NB' , (done) => {
|
||||||
|
request.get(utils.getbaseURL()
|
||||||
|
+ "/api/postVideoSponsorTimes?videoID=LevkAjUE6d4&startTime=40&endTime=60&userID=testing", null,
|
||||||
|
(err, res, body) => {
|
||||||
|
if (err) done("Couldn't call endpoint");
|
||||||
|
else if (res.statusCode === 403) done(); // pass
|
||||||
|
else done("non 403 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
|
||||||
@@ -111,8 +121,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();
|
||||||
@@ -121,7 +131,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",
|
||||||
@@ -133,7 +143,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();
|
||||||
@@ -141,13 +151,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();
|
||||||
@@ -155,7 +165,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",
|
||||||
@@ -168,7 +178,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();
|
||||||
@@ -176,7 +186,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",
|
||||||
@@ -188,7 +198,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();
|
||||||
@@ -196,17 +206,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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user