diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f1cbd7d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,18 @@ +name: CI + +on: [push, pull_request] + +jobs: + + build: + name: Run Tests + runs-on: ubuntu-latest + + steps: + # Initialization + - uses: actions/checkout@v1 + - uses: actions/setup-node@v1 + - run: npm install + + - name: Run Tests + run: npm test diff --git a/databases/_sponsorTimes.db.sql b/databases/_sponsorTimes.db.sql index 675ab5e..dfca8bf 100644 --- a/databases/_sponsorTimes.db.sql +++ b/databases/_sponsorTimes.db.sql @@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS "sponsorTimes" ( "userID" TEXT NOT NULL, "timeSubmitted" INTEGER NOT NULL, "views" INTEGER NOT NULL, + "category" TEXT NOT NULL, "shadowHidden" INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS "userNames" ( diff --git a/src/app.js b/src/app.js index 5924715..3c7ec8a 100644 --- a/src/app.js +++ b/src/app.js @@ -8,8 +8,8 @@ var corsMiddleware = require('./middleware/cors.js'); var loggerMiddleware = require('./middleware/logger.js'); // Routes -var getVideoSponsorTimes = require('./routes/getVideoSponsorTimes.js'); -var submitSponsorTimes = require('./routes/submitSponsorTimes.js'); +var getSkipSegments = require('./routes/getSkipSegments.js').endpoint; +var postSkipSegments = require('./routes/postSkipSegments.js'); var voteOnSponsorTime = require('./routes/voteOnSponsorTime.js'); var viewedVideoSponsorTime = require('./routes/viewedVideoSponsorTime.js'); var setUsername = require('./routes/setUsername.js'); @@ -22,17 +22,29 @@ var getTopUsers = require('./routes/getTopUsers.js'); var getTotalStats = require('./routes/getTotalStats.js'); var getDaysSavedFormatted = require('./routes/getDaysSavedFormatted.js'); +// Old Routes +var oldGetVideoSponsorTimes = require('./routes/oldGetVideoSponsorTimes.js'); +var oldSubmitSponsorTimes = require('./routes/oldSubmitSponsorTimes.js'); + //setup CORS correctly app.use(corsMiddleware); app.use(loggerMiddleware); +app.use(express.json()) + +// Setup pretty JSON +if (config.mode === "development") app.set('json spaces', 2); //add the get function -app.get('/api/getVideoSponsorTimes', getVideoSponsorTimes); +app.get('/api/getVideoSponsorTimes', oldGetVideoSponsorTimes); -//add the post function -app.get('/api/postVideoSponsorTimes', submitSponsorTimes); -app.post('/api/postVideoSponsorTimes', submitSponsorTimes); +//add the oldpost function +app.get('/api/postVideoSponsorTimes', oldSubmitSponsorTimes); +app.post('/api/postVideoSponsorTimes', oldSubmitSponsorTimes); + +//add the skip segments functions +app.get('/api/skipSegments', getSkipSegments); +app.post('/api/skipSegments', postSkipSegments); //voting endpoint app.get('/api/voteOnSponsorTime', voteOnSponsorTime); diff --git a/src/routes/getVideoSponsorTimes.js b/src/routes/getSkipSegments.js similarity index 70% rename from src/routes/getVideoSponsorTimes.js rename to src/routes/getSkipSegments.js index 67447bc..4925af3 100644 --- a/src/routes/getVideoSponsorTimes.js +++ b/src/routes/getSkipSegments.js @@ -200,71 +200,109 @@ function getVoteOrganisedSponsorTimes(sponsorTimes, votes, UUIDs) { }; } +/** + * + * Returns what would be sent to the client. + * Will resond with errors if required. Returns false if it errors. + * + * @param req + * @param res + * + * @returns + */ +function handleGetSegments(req, res) { + const videoID = req.body.videoID || req.query.videoID; + // Default to sponsor + // If using params instead of JSON, only one category can be pulled + const categories = req.body.categories || (req.query.category ? [req.query.category] : ["sponsor"]); + + /** + * @type {Array<{ + * segment: number[], + * category: string, + * UUID: string + * }> + * } + */ + let segments = []; + + let hashedIP = getHash(getIP(req) + config.globalSalt); + + try { + for (const category of categories) { + let rows = db.prepare("SELECT startTime, endTime, votes, UUID, shadowHidden FROM sponsorTimes WHERE videoID = ? and category = ? ORDER BY startTime") + .all(videoID, category); + + let sponsorTimes = []; + let votes = [] + let UUIDs = []; + + for (let i = 0; i < rows.length; i++) { + //check if votes are above -1 + if (rows[i].votes < -1) { + //too untrustworthy, just ignore it + continue; + } + + //check if shadowHidden + //this means it is hidden to everyone but the original ip that submitted it + if (rows[i].shadowHidden == 1) { + //get the ip + //await the callback + let hashedIPRow = privateDB.prepare("SELECT hashedIP FROM sponsorTimes WHERE videoID = ?").all(videoID); + + if (!hashedIPRow.some((e) => e.hashedIP === hashedIP)) { + //this isn't their ip, don't send it to them + continue; + } + } + + sponsorTimes.push([rows[i].startTime, rows[i].endTime]); + votes.push(rows[i].votes); + UUIDs.push(rows[i].UUID); + } + + if (sponsorTimes.length == 0) { + res.sendStatus(404); + return false; + } + + organisedData = getVoteOrganisedSponsorTimes(sponsorTimes, votes, UUIDs); + sponsorTimes = organisedData.sponsorTimes; + UUIDs = organisedData.UUIDs; + + for (let i = 0; i < sponsorTimes.length; i++) { + segments.push({ + segment: sponsorTimes[i], + category: category, + UUID: UUIDs[i] + }); + } + } + } catch(error) { + console.error(error); + res.send(500); + + return false; + } + + if (segments.length == 0) { + res.sendStatus(404); + return false; + } + + return segments; +} -module.exports = function (req, res) { - let videoID = req.query.videoID; +module.exports = { + handleGetSegments, + endpoint: function (req, res) { + let segments = handleGetSegments(req, res); - let sponsorTimes = []; - let votes = [] - let UUIDs = []; - - let hashedIP = getHash(getIP(req) + config.globalSalt); - - try { - let rows = db.prepare("SELECT startTime, endTime, votes, UUID, shadowHidden FROM sponsorTimes WHERE videoID = ? ORDER BY startTime").all(videoID); - - for (let i = 0; i < rows.length; i++) { - //check if votes are above -1 - if (rows[i].votes < -1) { - //too untrustworthy, just ignore it - continue; - } - - //check if shadowHidden - //this means it is hidden to everyone but the original ip that submitted it - if (rows[i].shadowHidden == 1) { - //get the ip - //await the callback - let hashedIPRow = privateDB.prepare("SELECT hashedIP FROM sponsorTimes WHERE videoID = ?").all(videoID); - - if (!hashedIPRow.some((e) => e.hashedIP === hashedIP)) { - //this isn't their ip, don't send it to them - continue; - } - } - - sponsorTimes.push([]); - - let index = sponsorTimes.length - 1; - - sponsorTimes[index][0] = rows[i].startTime; - sponsorTimes[index][1] = rows[i].endTime; - - votes[index] = rows[i].votes; - UUIDs[index] = rows[i].UUID; - } - - if (sponsorTimes.length == 0) { - res.sendStatus(404); - return; - } - - organisedData = getVoteOrganisedSponsorTimes(sponsorTimes, votes, UUIDs); - sponsorTimes = organisedData.sponsorTimes; - UUIDs = organisedData.UUIDs; - - if (sponsorTimes.length == 0) { - res.sendStatus(404); - } else { - //send result - res.send({ - sponsorTimes: sponsorTimes, - UUIDs: UUIDs - }) - } - } catch(error) { - console.error(error); - res.send(500); - } + if (segments) { + //send result + res.send(segments) + } + } } \ No newline at end of file diff --git a/src/routes/oldGetVideoSponsorTimes.js b/src/routes/oldGetVideoSponsorTimes.js new file mode 100644 index 0000000..d898d38 --- /dev/null +++ b/src/routes/oldGetVideoSponsorTimes.js @@ -0,0 +1,26 @@ +var getSkipSegments = require("./getSkipSegments.js") + + +module.exports = function (req, res) { + let videoID = req.query.videoID; + + let segments = getSkipSegments.handleGetSegments(req, res); + + if (segments) { + // Convert to old outputs + let sponsorTimes = []; + let UUIDs = []; + + for (const segment of segments) { + sponsorTimes.push(segment.segment); + UUIDs.push(segment.UUID); + } + + res.send({ + sponsorTimes, + UUIDs + }) + } + + // Error has already been handled in the other method +} \ No newline at end of file diff --git a/src/routes/oldSubmitSponsorTimes.js b/src/routes/oldSubmitSponsorTimes.js new file mode 100644 index 0000000..b818a24 --- /dev/null +++ b/src/routes/oldSubmitSponsorTimes.js @@ -0,0 +1,9 @@ +var config = require('../config.js'); + +var postSkipSegments = require('./postSkipSegments.js'); + +module.exports = async function submitSponsorTimes(req, res) { + req.query.category = "sponsor"; + + return postSkipSegments(req, res); +} diff --git a/src/routes/postSkipSegments.js b/src/routes/postSkipSegments.js new file mode 100644 index 0000000..d8fe2a3 --- /dev/null +++ b/src/routes/postSkipSegments.js @@ -0,0 +1,266 @@ +var 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 request = require('request'); +var isoDurations = require('iso8601-duration'); + +var getHash = require('../utils/getHash.js'); +var getIP = require('../utils/getIP.js'); +var getFormattedTime = require('../utils/getFormattedTime.js'); + +// TODO: might need to be a util +//returns true if the user is considered trustworthy +//this happens after a user has made 5 submissions and has less than 60% downvoted submissions +async function isUserTrustworthy(userID) { + //check to see if this user how many submissions this user has submitted + let totalSubmissionsRow = db.prepare("SELECT count(*) as totalSubmissions, sum(votes) as voteSum FROM sponsorTimes WHERE userID = ?").get(userID); + + if (totalSubmissionsRow.totalSubmissions > 5) { + //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); + + return (downvotedSubmissionsRow.downvotedSubmissions / totalSubmissionsRow.totalSubmissions) < 0.6 || + (totalSubmissionsRow.voteSum > downvotedSubmissionsRow.downvotedSubmissions); + } + + return true; +} + +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) { + let userSubmissionCountRow = db.prepare("SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?").get(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 && console.log(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) { + console.log("Failed to send first time submission Discord hook."); + console.log(JSON.stringify(err)); + console.log("\n"); + } else if (res && res.statusCode >= 400) { + console.log("Error sending first time submission Discord hook"); + console.log(JSON.stringify(res)); + console.log("\n"); + } + }); + }); + } + } +} + +// submission: {videoID, startTime, endTime} +// callback: function(reject: "String containing reason the submission was rejected") +// returns: string when an error, false otherwise +async function autoModerateSubmission(submission, callback) { + // Get the video information from the youtube API + if (config.youtubeAPI !== null) { + let {err, data} = await new Promise((resolve, reject) => { + YouTubeAPI.videos.list({ + part: "contentDetails", + id: submission.videoID + }, (err, data) => resolve({err, data})); + }); + + if (err) { + return "Couldn't get video information."; + } else { + // Check to see if video exists + if (data.pageInfo.totalResults === 0) { + callback("No video exists with id " + submission.videoID); + } else { + let duration = data.items[0].contentDetails.duration; + duration = isoDurations.toSeconds(isoDurations.parse(duration)); + + // Reject submission if over 80% of the video + if ((submission.endTime - submission.startTime) > (duration/100)*80) { + return "Sponsor segment is over 80% of the video."; + } else { + return false; + } + } + } + + } else { + console.log("Skipped YouTube API"); + + // Can't moderate the submission without calling the youtube API + // so allow by default. + return; + } +} + +module.exports = async function postSkipSegments(req, res) { + let videoID = req.query.videoID || req.body.videoID; + let userID = req.query.userID || req.body.userID; + + let segments = req.body.segments; + + if (segments === undefined) { + // Use query instead + segments = [{ + segment: [req.query.startTime, req.query.endTime], + category: req.query.category + }]; + } + + //check if all correct inputs are here and the length is 1 second or more + if (videoID == undefined || userID == undefined || segments == undefined || segments.length < 1) { + //invalid request + res.sendStatus(400); + return; + } + + //hash the userID + userID = getHash(userID); + + //hash the ip 5000 times so no one can get it from the database + let hashedIP = getHash(getIP(req) + config.globalSalt); + + // 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) { + //invalid request + res.sendStatus(400); + return; + } + + let startTime = parseFloat(segments[i].segment[0]); + let endTime = parseFloat(segments[i].segment[1]); + + if (Math.abs(startTime - endTime) < 1 || isNaN(startTime) || isNaN(endTime) + || startTime === Infinity || endTime === Infinity || startTime > endTime) { + //invalid request + res.sendStatus(400); + return; + } + + //check if this info has already been submitted before + let duplicateCheck2Row = db.prepare("SELECT COUNT(*) as count FROM sponsorTimes WHERE startTime = ? " + + "and endTime = ? and category = ? and videoID = ?").get(startTime, endTime, segments[i].category, videoID); + if (duplicateCheck2Row.count > 0) { + res.sendStatus(409); + return; + } + + let autoModerateResult = await autoModerateSubmission({videoID, startTime, endTime}); + if (autoModerateResult) { + res.status(403).send("Request rejected by auto moderator: " + autoModerateResult); + return; + } + } + + try { + //check if this user is on the vip list + let vipRow = db.prepare("SELECT count(*) as userCount FROM vipUsers WHERE userID = ?").get(userID); + + //get current time + let timeSubmitted = Date.now(); + + let yesterday = timeSubmitted - 86400000; + + //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]); + + if (rateLimitCheckRow.count >= 10) { + //too many sponsors for the same video from the same ip address + res.sendStatus(429); + + return; + } + + //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]); + + if (duplicateCheckRow.count >= 8) { + //too many sponsors for the same video from the same user + res.sendStatus(429); + + return; + } + + //check to see if this user is shadowbanned + let shadowBanRow = privateDB.prepare("SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?").get(userID); + + let shadowBanned = shadowBanRow.userCount; + + if (!(await isUserTrustworthy(userID))) { + //hide this submission as this user is untrustworthy + shadowBanned = 1; + } + + let startingVotes = 0; + if (vipRow.userCount > 0) { + //this user is a vip, start them at a higher approval rating + startingVotes = 10; + } + + for (const segmentInfo of segments) { + //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] + + segmentInfo.segment[1] + segmentInfo.category + userID, 1); + + try { + db.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, segmentInfo.segment[0], + segmentInfo.segment[1], startingVotes, UUID, userID, timeSubmitted, 0, segmentInfo.category, shadowBanned); + + //add to private db as well + privateDB.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?)").run(videoID, hashedIP, timeSubmitted); + } catch (err) { + //a DB change probably occurred + res.sendStatus(502); + console.log("Error when putting sponsorTime in the DB: " + videoID + ", " + segmentInfo.segment[0] + ", " + + segmentInfo.segment[1] + ", " + userID + ", " + segmentInfo.category + ". " + err); + + return; + } + + // Discord notification + sendDiscordNotification(userID, videoID, UUID, segmentInfo); + } + } catch (err) { + console.error(err); + + res.sendStatus(500); + + return; + } + + res.sendStatus(200); +} diff --git a/src/routes/submitSponsorTimes.js b/src/routes/submitSponsorTimes.js deleted file mode 100644 index 8221fa1..0000000 --- a/src/routes/submitSponsorTimes.js +++ /dev/null @@ -1,233 +0,0 @@ -var 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 request = require('request'); -var isoDurations = require('iso8601-duration'); - -var getHash = require('../utils/getHash.js'); -var getIP = require('../utils/getIP.js'); -var getFormattedTime = require('../utils/getFormattedTime.js'); - -// TODO: might need to be a util -//returns true if the user is considered trustworthy -//this happens after a user has made 5 submissions and has less than 60% downvoted submissions -async function isUserTrustworthy(userID) { - //check to see if this user how many submissions this user has submitted - let totalSubmissionsRow = db.prepare("SELECT count(*) as totalSubmissions, sum(votes) as voteSum FROM sponsorTimes WHERE userID = ?").get(userID); - - if (totalSubmissionsRow.totalSubmissions > 5) { - //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); - - return (downvotedSubmissionsRow.downvotedSubmissions / totalSubmissionsRow.totalSubmissions) < 0.6 || - (totalSubmissionsRow.voteSum > downvotedSubmissionsRow.downvotedSubmissions); - } - - return true; -} - -module.exports = async function submitSponsorTimes(req, res) { - let videoID = req.query.videoID; - let startTime = req.query.startTime; - let endTime = req.query.endTime; - let userID = req.query.userID; - - //check if all correct inputs are here and the length is 1 second or more - if (videoID == undefined || startTime == undefined || endTime == undefined || userID == undefined - || Math.abs(startTime - endTime) < 1) { - //invalid request - res.sendStatus(400); - return; - } - - //hash the userID - userID = getHash(userID); - - //hash the ip 5000 times so no one can get it from the database - let hashedIP = getHash(getIP(req) + config.globalSalt); - - startTime = parseFloat(startTime); - endTime = parseFloat(endTime); - - if (isNaN(startTime) || isNaN(endTime)) { - //invalid request - res.sendStatus(400); - return; - } - - if (startTime === Infinity || endTime === Infinity) { - //invalid request - res.sendStatus(400); - return; - } - - if (startTime > endTime) { - //time can't go backwards - res.sendStatus(400); - return; - } - - try { - //check if this user is on the vip list - let vipRow = db.prepare("SELECT count(*) as userCount FROM vipUsers WHERE userID = ?").get(userID); - - //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(videoID + startTime + endTime + userID, 1); - - //get current time - let timeSubmitted = Date.now(); - - let yesterday = timeSubmitted - 86400000; - - //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]); - - if (rateLimitCheckRow.count >= 10) { - //too many sponsors for the same video from the same ip address - res.sendStatus(429); - } else { - //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]); - - if (duplicateCheckRow.count >= 8) { - //too many sponsors for the same video from the same user - res.sendStatus(429); - } else { - //check if this info has already been submitted first - let duplicateCheck2Row = db.prepare("SELECT UUID FROM sponsorTimes WHERE startTime = ? and endTime = ? and videoID = ?").get([startTime, endTime, videoID]); - - //check to see if this user is shadowbanned - let shadowBanRow = privateDB.prepare("SELECT count(*) as userCount FROM shadowBannedUsers WHERE userID = ?").get(userID); - - let shadowBanned = shadowBanRow.userCount; - - if (!(await isUserTrustworthy(userID))) { - //hide this submission as this user is untrustworthy - shadowBanned = 1; - } - - let startingVotes = 0; - if (vipRow.userCount > 0) { - //this user is a vip, start them at a higher approval rating - startingVotes = 10; - } - - if (duplicateCheck2Row == null) { - //not a duplicate - - autoModerateSubmission({videoID, startTime, endTime}, (reject) => { - if (!reject) { - try { - db.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)").run(videoID, startTime, endTime, startingVotes, UUID, userID, timeSubmitted, 0, shadowBanned); - - //add to private db as well - privateDB.prepare("INSERT INTO sponsorTimes VALUES(?, ?, ?)").run(videoID, hashedIP, timeSubmitted); - res.sendStatus(200); - } catch (err) { - //a DB change probably occurred - console.log("Error when putting sponsorTime in the DB: " + videoID + ", " + startTime + ", " + "endTime" + ", " + userID); - res.sendStatus(502); - } - } else { - res.status(403).send("Request rejected by auto moderator: " + reject); - } - }); - } else { - res.sendStatus(409); - } - } - } - } catch (err) { - console.error(err); - res.send(500); - } -} - -// submission: {videoID, startTime, endTime} -// callback: function(reject: "String containing reason the submission was rejected") -const autoModerateSubmission = (submission, callback) => { - // Get the video information from the youtube API - if (config.youtubeAPI !== null) { - YouTubeAPI.videos.list({ - part: "contentDetails", - id: submission.videoID - }, (err, data) => { - if (err) callback("Couldn't get video information."); - else { - // Check to see if video exists - if (data.pageInfo.totalResults === 0) { - callback("No video exists with id " + submission.videoID); - } else { - let duration = data.items[0].contentDetails.duration; - duration = isoDurations.toSeconds(isoDurations.parse(duration)); - - // Reject submission if over 80% of the video - if ((submission.endTime - submission.startTime) > (duration/100)*80) { - callback ("Sponsor segment is over 80% of the video."); - } else { - callback(); - } - } - } - }); - } else { - console.log("skip youtube api"); - // Can't moderate the submission without calling the youtube API - // so allow by default. - callback(); - } -} -/* - //check if they are a first time user - //if so, send a notification to discord - if (config.youtubeAPIKey !== null && config.discordFirstTimeSubmissionsWebhookURL !== null && duplicateCheck2Row == null) { - let userSubmissionCountRow = db.prepare("SELECT count(*) as submissionCount FROM sponsorTimes WHERE userID = ?").get(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 && console.log(err); - return; - } - - console.log(JSON.stringify(data)); - - 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), - "color": 10813440, - "author": { - "name": userID - }, - "thumbnail": { - "url": data.items[0].snippet.thumbnails.maxres ? data.items[0].snippet.thumbnails.maxres.url : "", - } - }] - } - }, (err, res) => { - if (err) { - console.log("Failed to send first time submission Discord hook."); - console.log(JSON.stringify(err)); - console.log("\n"); - } else if (res && res.statusCode >= 400) { - console.log("Error sending first time submission Discord hook"); - console.log(JSON.stringify(res)); - console.log("\n"); - } - }); - }); - } - }*/ \ No newline at end of file diff --git a/src/utils/getIP.js b/src/utils/getIP.js index 8379ec0..78244fa 100644 --- a/src/utils/getIP.js +++ b/src/utils/getIP.js @@ -1,5 +1,5 @@ var fs = require('fs'); -var config = JSON.parse(fs.readFileSync('config.json')); +var config = require('../config.js'); module.exports = function getIP(req) { return config.behindProxy ? req.headers['x-forwarded-for'] : req.connection.remoteAddress; diff --git a/test.js b/test.js index 0241386..3a13418 100644 --- a/test.js +++ b/test.js @@ -2,6 +2,11 @@ var Mocha = require('mocha'), fs = require('fs'), path = require('path'); +var config = require('./src/config.js'); +// delete old test database +if (fs.existsSync(config.db)) fs.unlinkSync(config.db); +if (fs.existsSync(config.privateDB)) fs.unlinkSync(config.privateDB); + var createServer = require('./src/app.js'); var createMockServer = require('./test/mocks.js'); diff --git a/test/cases/getSavedTimeForUser.js b/test/cases/getSavedTimeForUser.js index 184b017..e3976c1 100644 --- a/test/cases/getSavedTimeForUser.js +++ b/test/cases/getSavedTimeForUser.js @@ -5,7 +5,7 @@ var getHash = require('../../src/utils/getHash.js'); describe('getSavedTimeForUser', () => { before(() => { - db.exec("INSERT INTO sponsorTimes VALUES ('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '"+getHash("testman")+"', 0, 50, 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('getSavedTimeForUser', 1, 11, 2, 'abc1239999', '" + getHash("testman") + "', 0, 50, 'sponsor', 0)"); }); it('Should be able to get a 200', (done) => { diff --git a/test/cases/getSkipSegments.js b/test/cases/getSkipSegments.js new file mode 100644 index 0000000..5338311 --- /dev/null +++ b/test/cases/getSkipSegments.js @@ -0,0 +1,248 @@ +var request = require('request'); +var db = require('../../src/databases/databases.js').db; +var utils = require('../utils.js'); + +/* + *CREATE TABLE IF NOT EXISTS "sponsorTimes" ( + "videoID" TEXT NOT NULL, + "startTime" REAL NOT NULL, + "endTime" REAL NOT NULL, + "votes" INTEGER NOT NULL, + "UUID" TEXT NOT NULL UNIQUE, + "userID" TEXT NOT NULL, + "timeSubmitted" INTEGER NOT NULL, + "views" INTEGER NOT NULL, + "shadowHidden" INTEGER NOT NULL +); + */ + +describe('getSkipSegments', () => { + before(() => { + db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest', 1, 11, 2, '1-uuid-0', 'testman', 0, 50, 'sponsor', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest', 20, 33, 2, '1-uuid-2', 'testman', 0, 50, 'intro', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest,test', 1, 11, 2, '1-uuid-1', 'testman', 0, 50, 'sponsor', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('test3', 1, 11, 2, '1-uuid-4', 'testman', 0, 50, 'sponsor', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('test3', 7, 22, -3, '1-uuid-5', 'testman', 0, 50, 'sponsor', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('multiple', 1, 11, 2, '1-uuid-6', 'testman', 0, 50, 'intro', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('multiple', 20, 33, 2, '1-uuid-7', 'testman', 0, 50, 'intro', 0)"); + }); + + + it('Should be able to get a time by category (Query Method) 1', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments?videoID=testtesttest&category=sponsor", null, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = JSON.parse(res.body); + if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11 + && data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") { + done(); + } else { + done("Received incorrect body: " + res.body); + } + } + }); + }); + + it('Should be able to get a time by category (Query Method) 2', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments?videoID=testtesttest&category=intro", null, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = JSON.parse(res.body); + if (data.length === 1 && data[0].segment[0] === 20 && data[0].segment[1] === 33 + && data[0].category === "intro" && data[0].UUID === "1-uuid-2") { + done(); + } else { + done("Received incorrect body: " + res.body); + } + } + }); + }); + + it('Should be able to get a time by category (JSON Method) 1', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments", { + json: { + videoID: "testtesttest", + categories: ["sponsor"] + } + }, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = res.body; + if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11 + && data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") { + done(); + } else { + done("Received incorrect body: " + JSON.stringify(res.body)); + } + } + }); + }); + + it('Should be able to get a time by category (JSON Method) 2', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments", { + json: { + videoID: "testtesttest", + categories: ["intro"] + } + }, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = res.body; + if (data.length === 1 && data[0].segment[0] === 20 && data[0].segment[1] === 33 + && data[0].category === "intro" && data[0].UUID === "1-uuid-2") { + done(); + } else { + done("Received incorrect body: " + JSON.stringify(res.body)); + } + } + }); + }); + + it('Should be able to get multiple times by category (JSON Method) 1', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments", { + json: { + videoID: "multiple", + categories: ["intro"] + } + }, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = res.body; + if (data.length === 2) { + + let success = true; + for (const segment of data) { + if ((segment.segment[0] !== 20 || segment.segment[1] !== 33 + || segment.category !== "intro" || segment.UUID !== "1-uuid-7") && + (segment.segment[0] !== 1 || segment.segment[1] !== 11 + || segment.category !== "intro" || segment.UUID !== "1-uuid-6")) { + success = false; + break; + } + } + + if (success) done(); + else done("Received incorrect body: " + JSON.stringify(res.body)); + } else { + done("Received incorrect body: " + JSON.stringify(res.body)); + } + } + }); + }); + + it('Should be able to get multiple times by multiple categories (JSON Method)', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments", { + json: { + videoID: "testtesttest", + categories: ["sponsor", "intro"] + } + }, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = res.body; + if (data.length === 2) { + + let success = true; + for (const segment of data) { + if ((segment.segment[0] !== 20 || segment.segment[1] !== 33 + || segment.category !== "intro" || segment.UUID !== "1-uuid-2") && + (segment.segment[0] !== 1 || segment.segment[1] !== 11 + || segment.category !== "sponsor" || segment.UUID !== "1-uuid-0")) { + success = false; + break; + } + } + + if (success) done(); + else done("Received incorrect body: " + JSON.stringify(res.body)); + } else { + done("Received incorrect body: " + JSON.stringify(res.body)); + } + } + }); + }); + + it('Should be possible to send unexpected query parameters', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments?videoID=testtesttest&fakeparam=hello&category=sponsor", null, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = JSON.parse(res.body); + if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11 + && data[0].category === "sponsor" && data[0].UUID === "1-uuid-0") { + done(); + } else { + done("Received incorrect body: " + res.body); + } + } + }); + }); + + it('Low voted submissions should be hidden', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments?videoID=test3&category=sponsor", null, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = JSON.parse(res.body); + if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11 + && data[0].category === "sponsor" && data[0].UUID === "1-uuid-4") { + done(); + } else { + done("Received incorrect body: " + res.body); + } + } + }); + }); + + it('Should return 404 if no segment found', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments?videoID=notarealvideo", null, + (err, res, body) => { + if (err) done("couldn't call endpoint"); + else if (res.statusCode !== 404) done("non 404 respone code: " + res.statusCode); + else done(); // pass + }); + }); + + + it('Should be able send a comma in a query param', (done) => { + request.get(utils.getbaseURL() + + "/api/skipSegments?videoID=testtesttest,test&category=sponsor", null, + (err, res, body) => { + if (err) done("Couldn't call endpoint"); + else if (res.statusCode !== 200) done("Status code was: " + res.statusCode); + else { + let data = JSON.parse(res.body); + if (data.length === 1 && data[0].segment[0] === 1 && data[0].segment[1] === 11 + && data[0].category === "sponsor" && data[0].UUID === "1-uuid-1") { + done(); + } else { + done("Received incorrect body: " + res.body); + } + } + }); + }); + +}); \ No newline at end of file diff --git a/test/cases/getSponsorTime.js b/test/cases/oldGetSponsorTime.js similarity index 80% rename from test/cases/getSponsorTime.js rename to test/cases/oldGetSponsorTime.js index be33143..ffb1264 100644 --- a/test/cases/getSponsorTime.js +++ b/test/cases/oldGetSponsorTime.js @@ -17,15 +17,15 @@ var utils = require('../utils.js'); ); */ -describe('getVideoSponsorTime', () => { +describe('getVideoSponsorTime (Old get method)', () => { before(() => { - db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest', 1, 11, 2, 'uuid-0', 'testman', 0, 50, 0)"); - db.exec("INSERT INTO sponsorTimes VALUES ('testtesttest,test', 1, 11, 2, 'uuid-1', 'testman', 0, 50, 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('old-testtesttest', 1, 11, 2, 'uuid-0', 'testman', 0, 50, 'sponsor', 0)"); + db.exec("INSERT INTO sponsorTimes VALUES ('old-testtesttest,test', 1, 11, 2, 'uuid-1', 'testman', 0, 50, 'sponsor', 0)"); }); it('Should be able to get a time', (done) => { request.get(utils.getbaseURL() - + "/api/getVideoSponsorTimes?videoID=testtesttest", null, + + "/api/getVideoSponsorTimes?videoID=old-testtesttest", null, (err, res, body) => { if (err) done("Couldn't call endpoint"); else if (res.statusCode !== 200) done("non 200"); @@ -46,7 +46,7 @@ describe('getVideoSponsorTime', () => { it('Should be possible to send unexpected query parameters', (done) => { request.get(utils.getbaseURL() - + "/api/getVideoSponsorTimes?videoID=testtesttest&fakeparam=hello", null, + + "/api/getVideoSponsorTimes?videoID=old-testtesttest&fakeparam=hello", null, (err, res, body) => { if (err) done("couldn't callendpoint"); else if (res.statusCode !== 200) done("non 200"); @@ -56,7 +56,7 @@ describe('getVideoSponsorTime', () => { it('Should be able send a comma in a query param', (done) => { request.get(utils.getbaseURL() - + "/api/getVideoSponsorTimes?videoID=testtesttest,test", null, + + "/api/getVideoSponsorTimes?videoID=old-testtesttest,test", null, (err, res, body) => { if (err) done("couln't call endpoint"); else if (res.statusCode !== 200) done("non 200 response: " + res.statusCode); @@ -67,7 +67,7 @@ describe('getVideoSponsorTime', () => { it('Should be able to get the correct time', (done) => { request.get(utils.getbaseURL() - + "/api/getVideoSponsorTimes?videoID=testtesttest", null, + + "/api/getVideoSponsorTimes?videoID=old-testtesttest", null, (err, res, body) => { if (err) done("couldn't call endpoint"); else if (res.statusCode !== 200) done("non 200"); diff --git a/test/cases/oldSubmitSponsorTimes.js b/test/cases/oldSubmitSponsorTimes.js new file mode 100644 index 0000000..bf3ac7a --- /dev/null +++ b/test/cases/oldSubmitSponsorTimes.js @@ -0,0 +1,55 @@ +var assert = require('assert'); +var request = require('request'); + +var utils = require('../utils.js'); + +var databases = require('../../src/databases/databases.js'); +var db = databases.db; + +describe('postVideoSponsorTime (Old submission method)', () => { + it('Should be able to submit a time (GET)', (done) => { + request.get(utils.getbaseURL() + + "/api/postVideoSponsorTimes?videoID=dQw4w9WgXcQ&startTime=1&endTime=10&userID=test", null, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + let row = db.prepare("SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?").get("dQw4w9WgXcQ"); + if (row.startTime === 1 && row.endTime === 10 && row.category === "sponsor") { + done() + } else { + done("Submitted times were not saved. Actual submission: " + JSON.stringify(row)); + } + } else { + done("Status code was " + res.statusCode); + } + }); + }); + + it('Should be able to submit a time (POST)', (done) => { + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes?videoID=dQw4w9WgXcE&startTime=1&endTime=11&userID=test", null, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + let row = db.prepare("SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?").get("dQw4w9WgXcE"); + if (row.startTime === 1 && row.endTime === 11 && row.category === "sponsor") { + done() + } else { + done("Submitted times were not saved. Actual submission: " + JSON.stringify(row)); + } + } else { + done("Status code was " + res.statusCode); + } + }); + }); + + it('Should return 400 for missing params', (done) => { + request.get(utils.getbaseURL() + + "/api/postVideoSponsorTimes?startTime=1&endTime=10&userID=test", null, + (err, res, body) => { + if (err) done(err); + if (res.statusCode === 400) done(); + else done("Status code was: " + res.statusCode); + }); + }); +}); \ No newline at end of file diff --git a/test/cases/postSkipSegments.js b/test/cases/postSkipSegments.js new file mode 100644 index 0000000..05c7374 --- /dev/null +++ b/test/cases/postSkipSegments.js @@ -0,0 +1,212 @@ +var assert = require('assert'); +var request = require('request'); + +var utils = require('../utils.js'); + +var databases = require('../../src/databases/databases.js'); +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, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + let row = db.prepare("SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?").get("dQw4w9WgXcR"); + if (row.startTime === 2 && row.endTime === 10 && row.category === "sponsor") { + done() + } else { + done("Submitted times were not saved. Actual submission: " + JSON.stringify(row)); + } + } else { + done("Status code was " + res.statusCode); + } + }); + }); + + it('Should be able to submit a single time (JSON method)', (done) => { + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes", { + json: { + userID: "test", + videoID: "dQw4w9WgXcF", + segments: [{ + segment: [0, 10], + category: "sponsor" + }] + } + }, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + let row = db.prepare("SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?").get("dQw4w9WgXcF"); + if (row.startTime === 0 && row.endTime === 10 && row.category === "sponsor") { + done() + } else { + done("Submitted times were not saved. Actual submission: " + JSON.stringify(row)); + } + } else { + done("Status code was " + res.statusCode); + } + }); + }); + + it('Should be able to submit multiple times (JSON method)', (done) => { + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes", { + json: { + userID: "test", + videoID: "dQw4w9WgXcQ", + segments: [{ + segment: [3, 10], + category: "sponsor" + }, { + segment: [30, 60], + category: "intro" + }] + } + }, + (err, res, body) => { + if (err) done(err); + else if (res.statusCode === 200) { + let rows = db.prepare("SELECT startTime, endTime, category FROM sponsorTimes WHERE videoID = ?").all("dQw4w9WgXcR"); + let success = true; + if (rows.length === 2) { + for (const row of rows) { + if ((row.startTime !== 3 || row.endTime !== 10 || row.category !== "sponsor") && + (row.startTime !== 30 || row.endTime !== 60 || row.category !== "intro")) { + success = false; + break; + } + } + } + + if (success) done(); + else done("Submitted times were not saved. Actual submissions: " + JSON.stringify(row)); + } else { + done("Status code was " + res.statusCode); + } + }); + }); + + 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, + (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) => { + 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 + else done("non 403 status code: " + res.statusCode + " ("+body+")"); + }); + }); + + it('Should return 400 for missing params (Params method)', (done) => { + 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(); + else done(true); + }); + }); + + it('Should return 400 for missing params (JSON method) 1', (done) => { + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes", { + json: { + userID: "test", + segments: [{ + segment: [9, 10], + category: "sponsor" + }, { + segment: [31, 60], + category: "intro" + }] + } + }, + (err, res, body) => { + if (err) done(true); + else if (res.statusCode === 400) done(); + else done(true); + }); + }); + it('Should return 400 for missing params (JSON method) 2', (done) => { + 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); + }); + }); + it('Should return 400 for missing params (JSON method) 3', (done) => { + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes", { + json: { + userID: "test", + videoID: "dQw4w9WgXcQ", + segments: [{ + segment: [0], + category: "sponsor" + }, { + segment: [31, 60], + category: "intro" + }] + } + }, + (err, res, body) => { + if (err) done(true); + else if (res.statusCode === 400) done(); + else done(true); + }); + }); + it('Should return 400 for missing params (JSON method) 4', (done) => { + request.post(utils.getbaseURL() + + "/api/postVideoSponsorTimes", { + json: { + userID: "test", + videoID: "dQw4w9WgXcQ", + segments: [{ + segment: [9, 10] + }, { + segment: [31, 60], + category: "intro" + }] + } + }, + (err, res, body) => { + if (err) done(true); + else if (res.statusCode === 400) done(); + else done(true); + }); + }); + it('Should return 400 for missing params (JSON method) 5', (done) => { + 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/cases/submitSponsorTimes.js b/test/cases/submitSponsorTimes.js deleted file mode 100644 index ce7b90e..0000000 --- a/test/cases/submitSponsorTimes.js +++ /dev/null @@ -1,47 +0,0 @@ -var assert = require('assert'); -var request = require('request'); - -var utils = require('../utils.js'); - -describe('postVideoSponsorTime', () => { - it('Should be able to create a time', (done) => { - request.get(utils.getbaseURL() - + "/api/postVideoSponsorTimes?videoID=fWvKvOViM3g&startTime=1&endTime=10&userID=test", null, - (err, res, body) => { - if (err) done("Couldn't call endpoint"); - else if (res.statusCode === 200) done(); - else done("non 200 status code: " + res.statusCode + " ("+body+")"); - }); - }); - - it('Should return 400 for missing params', (done) => { - request.get(utils.getbaseURL() - + "/api/postVideoSponsorTimes?startTime=1&endTime=10&userID=test", null, - (err, res, body) => { - if (err) done("Couldn't call endpoint"); - else if (res.statusCode === 400) done(); // pass - else done("non 400 status code: " + res.statusCode + " ("+body+")"); - }); - }); - - - it('Should be rejected if over 80% of the video', (done) => { - request.get(utils.getbaseURL() - + "/api/postVideoSponsorTimes?videoID=qqwerty&startTime=1&endTime=1000000&userID=test", 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) => { - request.get(utils.getbaseURL() - + "/api/postVideoSponsorTimes?videoID=knownWrongID&startTime=1&endTime=1000000&userID=test", 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+")"); - }); - }); -}); \ No newline at end of file diff --git a/test/databases/_private.db.sql b/test/databases/_private.db.sql index 317603a..b93aaee 100644 --- a/test/databases/_private.db.sql +++ b/test/databases/_private.db.sql @@ -1,8 +1,4 @@ BEGIN TRANSACTION; -DROP TABLE IF EXISTS "shadowBannedUsers"; -DROP TABLE IF EXISTS "votes"; -DROP TABLE IF EXISTS "sponsorTimes"; - CREATE TABLE IF NOT EXISTS "shadowBannedUsers" ( "userID" TEXT NOT NULL ); diff --git a/test/databases/_sponsorTimes.db.sql b/test/databases/_sponsorTimes.db.sql index bbf501e..dfca8bf 100644 --- a/test/databases/_sponsorTimes.db.sql +++ b/test/databases/_sponsorTimes.db.sql @@ -1,8 +1,4 @@ BEGIN TRANSACTION; -DROP TABLE IF EXISTS "vipUsers"; -DROP TABLE IF EXISTS "sponsorTimes"; -DROP TABLE IF EXISTS "userNames"; - CREATE TABLE IF NOT EXISTS "vipUsers" ( "userID" TEXT NOT NULL ); @@ -15,6 +11,7 @@ CREATE TABLE IF NOT EXISTS "sponsorTimes" ( "userID" TEXT NOT NULL, "timeSubmitted" INTEGER NOT NULL, "views" INTEGER NOT NULL, + "category" TEXT NOT NULL, "shadowHidden" INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS "userNames" ( diff --git a/test/mocks.js b/test/mocks.js index df19c8a..a124646 100644 --- a/test/mocks.js +++ b/test/mocks.js @@ -4,12 +4,10 @@ var app = express(); var config = require('../src/config.js'); app.post('/ReportChannelWebhook', (req, res) => { - console.log("report mock hit"); res.status(200); }); app.post('/FirstTimeSubmissionsWebhook', (req, res) => { - console.log("first time submisson mock hit"); res.status(200); }); diff --git a/test/youtubeMock.js b/test/youtubeMock.js index 56300fd..0a9998d 100644 --- a/test/youtubeMock.js +++ b/test/youtubeMock.js @@ -26,6 +26,14 @@ const YouTubeAPI = { { contentDetails: { duration: "PT1H23M30S" + }, + snippet: { + title: "Example Title", + thumbnails: { + maxres: { + url: "https://sponsor.ajay.app/LogoSponsorBlockSimple256px.png" + } + } } } ]