diff --git a/.gitignore b/.gitignore index e329b30..3362a8d 100644 --- a/.gitignore +++ b/.gitignore @@ -99,4 +99,7 @@ test/databases/sponsorTimes.db-wal test/databases/private.db # Config files -config.json \ No newline at end of file +config.json + +# Mac files +.DS_Store \ No newline at end of file diff --git a/config.json.example b/config.json.example index 337c317..a2eee23 100644 --- a/config.json.example +++ b/config.json.example @@ -8,6 +8,8 @@ "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] + "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 "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" diff --git a/package.json b/package.json index 9c4e7b5..ffd1a63 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "iso8601-duration": "^1.2.0", "sync-mysql": "^3.0.1", "uuid": "^3.3.2", - "youtube-api": "^2.0.10" + "youtube-api": "^2.0.10", + "node-fetch": "^2.6.0" }, "devDependencies": { "mocha": "^7.1.1", diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js index 78345b8..ab80d25 100644 --- a/src/routes/postSkipSegments.js +++ b/src/routes/postSkipSegments.js @@ -1,17 +1,18 @@ -var config = require('../config.js'); +const config = require('../config.js'); -var databases = require('../databases/databases.js'); -var db = databases.db; -var privateDB = databases.privateDB; -var YouTubeAPI = require('../utils/youtubeAPI.js'); -var logger = require('../utils/logger.js'); -var request = require('request'); -var isoDurations = require('iso8601-duration'); +const databases = require('../databases/databases.js'); +const db = databases.db; +const privateDB = databases.privateDB; +const YouTubeAPI = require('../utils/youtubeAPI.js'); +const logger = require('../utils/logger.js'); +const request = require('request'); +const isoDurations = require('iso8601-duration'); +const fetch = require('node-fetch'); -var getHash = require('../utils/getHash.js'); -var getIP = require('../utils/getIP.js'); -var getFormattedTime = require('../utils/getFormattedTime.js'); -var isUserTrustworthy = require('../utils/isUserTrustworthy.js'); +const getHash = require('../utils/getHash.js'); +const getIP = require('../utils/getIP.js'); +const getFormattedTime = require('../utils/getFormattedTime.js'); +const isUserTrustworthy = require('../utils/isUserTrustworthy.js') const { dispatchEvent } = require('../utils/webhookUtils.js'); function sendWebhookNotification(userID, videoID, UUID, submissionCount, youtubeData, {submissionStart, submissionEnd}, segmentInfo) { @@ -56,11 +57,11 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) { 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 || userSubmissionCountRow.submissionCount > 1) return; @@ -70,7 +71,7 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) { "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: " + + "\n\nTimestamp: " + getFormattedTime(startTime) + " to " + getFormattedTime(endTime) + "\n\nCategory: " + segmentInfo.category, "color": 10813440, @@ -82,7 +83,7 @@ function sendWebhooks(userID, videoID, UUID, segmentInfo) { } }] } - }, (err, res) => { + }, (err, res) => { if (err) { logger.error("Failed to send first time submission Discord hook."); 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") // returns: string when an error, false otherwise -// 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 -// the future could have the same problem. -async function autoModerateSubmission(submission, callback) { +// 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 +// the future could have the same problem. +async function autoModerateSubmission(submission) { // Get the video information from the youtube API if (config.youtubeAPIKey !== null) { let {err, data} = await new Promise((resolve, reject) => { YouTubeAPI.videos.list({ - part: "contentDetails", + part: "contentDetails,snippet", id: submission.videoID }, (err, data) => resolve({err, data})); }); @@ -121,20 +169,62 @@ async function autoModerateSubmission(submission, callback) { if (data.pageInfo.totalResults === 0) { return "No video exists with id " + submission.videoID; } else { - let duration = data.items[0].contentDetails.duration; - duration = isoDurations.toSeconds(isoDurations.parse(duration)); - if (duration == 0) { - // Allow submission if the duration is 0 (bug in youtube api) - return false; - } else if ((submission.endTime - submission.startTime) > (duration/100)*80) { - // Reject submission if over 80% of the video - return "Sponsor segment is over 80% of the video."; + let segments = submission.segments; + let nbString = ""; + for (let i = 0; i < segments.length; i++) { + let startTime = parseFloat(segments[i].segment[0]); + let endTime = parseFloat(segments[i].segment[1]); + + let duration = data.items[0].contentDetails.duration; + 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 { return false; } } } - + } else { 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 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 for (let i = 0; i < segments.length; i++) { 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); return; } + } - let autoModerateResult = await autoModerateSubmission({videoID, startTime, endTime}); - if (autoModerateResult) { - res.status(403).send("Request rejected by auto moderator: " + autoModerateResult); + // Auto moderator check + if (!isVIP) { + 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; } } - // 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]); - //get current time let timeSubmitted = Date.now(); @@ -247,7 +348,7 @@ module.exports = async function postSkipSegments(req, res) { if (false) { //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]); - + if (rateLimitCheckRow.count >= 10) { //too many sponsors for the same video from the same ip address res.sendStatus(429); @@ -260,7 +361,7 @@ module.exports = async function postSkipSegments(req, res) { if (false) { //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]); - + if (duplicateCheckRow.count >= 16) { //too many sponsors for the same video from the same user res.sendStatus(429); @@ -279,8 +380,8 @@ module.exports = async function postSkipSegments(req, res) { shadowBanned = 1; } - let startingVotes = 0; - if (vipRow.userCount > 0) { + let startingVotes = 0 + decreaseVotes; + if (isVIP) { //this user is a vip, start them at a higher approval rating startingVotes = 10000; } @@ -289,26 +390,29 @@ module.exports = async function postSkipSegments(req, res) { //this can just be a hash of the data //it's better than generating an actual UUID like what was used before //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); try { - db.prepare('run', "INSERT INTO sponsorTimes " + + db.prepare('run', "INSERT INTO sponsorTimes " + "(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]); - + //add to private db as well privateDB.prepare('run', "INSERT INTO sponsorTimes VALUES(?, ?, ?)", [videoID, hashedIP, timeSubmitted]); } catch (err) { //a DB change probably occurred 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); - + return; } - + + // Discord notification + sendWebhooks(userID, videoID, UUID, segmentInfo); + UUIDs.push(UUID); } } catch (err) { diff --git a/test.json b/test.json index 0c67e5c..8905d83 100644 --- a/test.json +++ b/test.json @@ -4,9 +4,11 @@ "globalSalt": "testSalt", "adminUserID": "testUserId", "youtubeAPIKey": "", - "discordReportChannelWebhookURL": "http://127.0.0.1:8081/ReportChannelWebhook", - "discordFirstTimeSubmissionsWebhookURL": "http://127.0.0.1:8081/FirstTimeSubmissionsWebhook", - "discordCompletelyIncorrectReportWebhookURL": "http://127.0.0.1:8081/CompletelyIncorrectReportWebhook", + "discordReportChannelWebhookURL": "http://127.0.0.1:8081/ReportChannelWebhook", + "discordFirstTimeSubmissionsWebhookURL": "http://127.0.0.1:8081/FirstTimeSubmissionsWebhook", + "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, "db": "./test/databases/sponsorTimes.db", "privateDB": "./test/databases/private.db", diff --git a/test/cases/postSkipSegments.js b/test/cases/postSkipSegments.js index 825f788..a2e2c97 100644 --- a/test/cases/postSkipSegments.js +++ b/test/cases/postSkipSegments.js @@ -8,8 +8,8 @@ var db = databases.db; describe('postSkipSegments', () => { it('Should be able to submit a single time (Params method)', (done) => { - request.post(utils.getbaseURL() - + "/api/postVideoSponsorTimes?videoID=dQw4w9WgXcR&startTime=2&endTime=10&userID=test&category=sponsor", null, + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes?videoID=dQw4w9WgXcR&startTime=2&endTime=10&userID=test&category=sponsor", null, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -26,7 +26,7 @@ describe('postSkipSegments', () => { }); it('Should be able to submit a single time (JSON method)', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", @@ -36,7 +36,7 @@ describe('postSkipSegments', () => { category: "sponsor" }] } - }, + }, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -53,7 +53,7 @@ describe('postSkipSegments', () => { }); it('Should be able to submit multiple times (JSON method)', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", @@ -66,7 +66,7 @@ describe('postSkipSegments', () => { category: "intro" }] } - }, + }, (err, res, body) => { if (err) done(err); else if (res.statusCode === 200) { @@ -88,11 +88,11 @@ describe('postSkipSegments', () => { done("Status code was " + res.statusCode); } }); - }); + }).timeout(5000); it('Should be accepted if a non-sponsor is less than 1 second', (done) => { - request.post(utils.getbaseURL() - + "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing&category=intro", null, + request.post(utils.getbaseURL() + + "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing&category=intro", null, (err, res, body) => { if (err) done("Couldn't call endpoint"); 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) => { - request.post(utils.getbaseURL() - + "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing", null, + request.post(utils.getbaseURL() + + "/api/skipSegments?videoID=qqwerty&startTime=30&endTime=30.5&userID=testing", null, (err, res, body) => { if (err) done("Couldn't call endpoint"); else if (res.statusCode === 400) done(); // pass @@ -111,8 +111,8 @@ describe('postSkipSegments', () => { }); it('Should be rejected if over 80% of the video', (done) => { - request.get(utils.getbaseURL() - + "/api/postVideoSponsorTimes?videoID=qqwerty&startTime=30&endTime=1000000&userID=testing", null, + request.get(utils.getbaseURL() + + "/api/postVideoSponsorTimes?videoID=qqwerty&startTime=30&endTime=1000000&userID=testing", null, (err, res, body) => { if (err) done("Couldn't call endpoint"); else if (res.statusCode === 403) done(); // pass @@ -120,19 +120,29 @@ describe('postSkipSegments', () => { }); }); - 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, + it("Should be rejected if NB's predicted probability is <70%.", (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 === 200) done(); // pass 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) => { - request.get(utils.getbaseURL() - + "/api/postVideoSponsorTimes?videoID=knownWrongID&startTime=30&endTime=1000000&userID=testing", null, + request.get(utils.getbaseURL() + + "/api/postVideoSponsorTimes?videoID=knownWrongID&startTime=30&endTime=1000000&userID=testing", null, (err, res, body) => { if (err) done("Couldn't call endpoint"); else if (res.statusCode === 403) done(); // pass @@ -141,8 +151,8 @@ describe('postSkipSegments', () => { }); it('Should return 400 for missing params (Params method)', (done) => { - request.post(utils.getbaseURL() - + "/api/postVideoSponsorTimes?startTime=9&endTime=10&userID=test", null, + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes?startTime=9&endTime=10&userID=test", null, (err, res, body) => { if (err) done(true); if (res.statusCode === 400) done(); @@ -151,7 +161,7 @@ describe('postSkipSegments', () => { }); it('Should return 400 for missing params (JSON method) 1', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", @@ -163,7 +173,7 @@ describe('postSkipSegments', () => { category: "intro" }] } - }, + }, (err, res, body) => { if (err) done(true); else if (res.statusCode === 400) done(); @@ -171,13 +181,13 @@ describe('postSkipSegments', () => { }); }); it('Should return 400 for missing params (JSON method) 2', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", videoID: "dQw4w9WgXcQ" } - }, + }, (err, res, body) => { if (err) done(true); else if (res.statusCode === 400) done(); @@ -185,7 +195,7 @@ describe('postSkipSegments', () => { }); }); it('Should return 400 for missing params (JSON method) 3', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", @@ -198,7 +208,7 @@ describe('postSkipSegments', () => { category: "intro" }] } - }, + }, (err, res, body) => { if (err) done(true); else if (res.statusCode === 400) done(); @@ -206,7 +216,7 @@ describe('postSkipSegments', () => { }); }); it('Should return 400 for missing params (JSON method) 4', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", @@ -218,7 +228,7 @@ describe('postSkipSegments', () => { category: "intro" }] } - }, + }, (err, res, body) => { if (err) done(true); else if (res.statusCode === 400) done(); @@ -226,17 +236,17 @@ describe('postSkipSegments', () => { }); }); it('Should return 400 for missing params (JSON method) 5', (done) => { - request.post(utils.getbaseURL() + request.post(utils.getbaseURL() + "/api/postVideoSponsorTimes", { json: { userID: "test", videoID: "dQw4w9WgXcQ" } - }, + }, (err, res, body) => { if (err) done(true); else if (res.statusCode === 400) done(); else done(true); }); }); -}); \ No newline at end of file +}); diff --git a/test/mocks.js b/test/mocks.js index aeaed4b..ddf7f19 100644 --- a/test/mocks.js +++ b/test/mocks.js @@ -15,12 +15,37 @@ app.post('/CompletelyIncorrectReportWebhook', (req, res) => { 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) => { res.sendStatus(200); }); - module.exports = function createMockServer(callback) { return app.listen(config.mockPort, callback); -} \ No newline at end of file +}