mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-31 11:56:27 +03:00
Merge pull request #284 from HaiDang666/283_show-lastest-warning
Show lastest warning when post segment
This commit is contained in:
@@ -1,15 +1,15 @@
|
|||||||
# SponsorTimesDB
|
# SponsorTimesDB
|
||||||
|
|
||||||
[vipUsers](###vipUsers)
|
[vipUsers](#vipUsers)
|
||||||
[sponsorTimes](###sponsorTimes)
|
[sponsorTimes](#sponsorTimes)
|
||||||
[userNames](###userNames)
|
[userNames](#userNames)
|
||||||
[userNameLogs](###userNameLogs)
|
[userNameLogs](#userNameLogs)
|
||||||
[categoryVotes](###categoryVotes)
|
[categoryVotes](#categoryVotes)
|
||||||
[lockCategories](###lockCategories)
|
[lockCategories](#lockCategories)
|
||||||
[warnings](###warnings)
|
[warnings](#warnings)
|
||||||
[shadowBannedUsers](###shadowBannedUsers)
|
[shadowBannedUsers](#shadowBannedUsers)
|
||||||
[unlistedVideos](###unlistedVideos)
|
[unlistedVideos](#unlistedVideos)
|
||||||
[config](###config)
|
[config](#config)
|
||||||
|
|
||||||
### vipUsers
|
### vipUsers
|
||||||
| Name | Type | |
|
| Name | Type | |
|
||||||
@@ -142,10 +142,10 @@
|
|||||||
|
|
||||||
# Private
|
# Private
|
||||||
|
|
||||||
[vote](###vote)
|
[vote](#vote)
|
||||||
[categoryVotes](###categoryVotes)
|
[categoryVotes](#categoryVotes)
|
||||||
[sponsorTimes](###sponsorTimes)
|
[sponsorTimes](#sponsorTimes)
|
||||||
[config](###config)
|
[config](#config)
|
||||||
|
|
||||||
### vote
|
### vote
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
"express-promise-router": "^4.1.0",
|
"express-promise-router": "^4.1.0",
|
||||||
"express-rate-limit": "^5.1.3",
|
"express-rate-limit": "^5.1.3",
|
||||||
"http": "0.0.0",
|
"http": "0.0.0",
|
||||||
"iso8601-duration": "^1.2.0",
|
|
||||||
"node-fetch": "^2.6.0",
|
"node-fetch": "^2.6.0",
|
||||||
"pg": "^8.5.1",
|
"pg": "^8.5.1",
|
||||||
"redis": "^3.1.1",
|
"redis": "^3.1.1",
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {db, privateDB} from '../databases/databases';
|
|||||||
import {getMaxResThumbnail, YouTubeAPI} from '../utils/youtubeApi';
|
import {getMaxResThumbnail, YouTubeAPI} from '../utils/youtubeApi';
|
||||||
import {getSubmissionUUID} from '../utils/getSubmissionUUID';
|
import {getSubmissionUUID} from '../utils/getSubmissionUUID';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
import isoDurations, { end } from 'iso8601-duration';
|
|
||||||
import {getHash} from '../utils/getHash';
|
import {getHash} from '../utils/getHash';
|
||||||
import {getIP} from '../utils/getIP';
|
import {getIP} from '../utils/getIP';
|
||||||
import {getFormattedTime} from '../utils/getFormattedTime';
|
import {getFormattedTime} from '../utils/getFormattedTime';
|
||||||
@@ -272,6 +271,34 @@ async function getYouTubeVideoInfo(videoID: VideoID, ignoreCache = false): Promi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkUserActiveWarning(userID: string): Promise<{ pass: boolean; errorMessage: string; }> {
|
||||||
|
const MILLISECONDS_IN_HOUR = 3600000;
|
||||||
|
const now = Date.now();
|
||||||
|
const warnings = await db.prepare('all',
|
||||||
|
`SELECT "reason"
|
||||||
|
FROM warnings
|
||||||
|
WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1
|
||||||
|
ORDER BY "issueTime" DESC
|
||||||
|
LIMIT ?`,
|
||||||
|
[
|
||||||
|
userID,
|
||||||
|
Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR)),
|
||||||
|
config.maxNumberOfActiveWarnings
|
||||||
|
],
|
||||||
|
) as {reason: string}[]
|
||||||
|
|
||||||
|
if (warnings?.length >= config.maxNumberOfActiveWarnings) {
|
||||||
|
const defaultMessage = 'Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?';
|
||||||
|
|
||||||
|
return {
|
||||||
|
pass: false,
|
||||||
|
errorMessage: warnings[0]?.reason?.length > 0 ? warnings[0].reason : defaultMessage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {pass: true, errorMessage: ''};
|
||||||
|
}
|
||||||
|
|
||||||
function proxySubmission(req: Request) {
|
function proxySubmission(req: Request) {
|
||||||
fetch(config.proxySubmission + '/api/skipSegments?userID=' + req.query.userID + '&videoID=' + req.query.videoID, {
|
fetch(config.proxySubmission + '/api/skipSegments?userID=' + req.query.userID + '&videoID=' + req.query.videoID, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -319,26 +346,18 @@ export async function postSkipSegments(req: Request, res: Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invalidFields.length !== 0) {
|
if (invalidFields.length !== 0) {
|
||||||
// invalid request
|
// invalid request
|
||||||
const fields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ', ' : '') + c, '');
|
const fields = invalidFields.reduce((p, c, i) => p + (i !== 0 ? ', ' : '') + c, '');
|
||||||
res.status(400).send(`No valid ${fields} field(s) provided`);
|
res.status(400).send(`No valid ${fields} field(s) provided`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//hash the userID
|
//hash the userID
|
||||||
userID = getHash(userID);
|
userID = getHash(userID);
|
||||||
|
|
||||||
//hash the ip 5000 times so no one can get it from the database
|
const warningResult: {pass: boolean, errorMessage: string} = await checkUserActiveWarning(userID);
|
||||||
const hashedIP = getHash(getIP(req) + config.globalSalt);
|
if (!warningResult.pass) {
|
||||||
|
return res.status(403).send(warningResult.errorMessage);
|
||||||
const MILLISECONDS_IN_HOUR = 3600000;
|
|
||||||
const now = Date.now();
|
|
||||||
const warningsCount = (await db.prepare('get', `SELECT count(*) as count FROM warnings WHERE "userID" = ? AND "issueTime" > ? AND enabled = 1`,
|
|
||||||
[userID, Math.floor(now - (config.hoursAfterWarningExpires * MILLISECONDS_IN_HOUR))],
|
|
||||||
)).count;
|
|
||||||
|
|
||||||
if (warningsCount >= config.maxNumberOfActiveWarnings) {
|
|
||||||
return res.status(403).send('Submission rejected due to a warning from a moderator. This means that we noticed you were making some common mistakes that are not malicious, and we just want to clarify the rules. Could you please send a message in Discord or Matrix so we can further help you?');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lockedCategoryList = (await db.prepare('all', 'SELECT category from "lockCategories" where "videoID" = ?', [videoID])).map((list: any) => {
|
let lockedCategoryList = (await db.prepare('all', 'SELECT category from "lockCategories" where "videoID" = ?', [videoID])).map((list: any) => {
|
||||||
@@ -350,9 +369,15 @@ export async function postSkipSegments(req: Request, res: Response) {
|
|||||||
|
|
||||||
const decreaseVotes = 0;
|
const decreaseVotes = 0;
|
||||||
|
|
||||||
const previousSubmissions = await db.prepare('all', `SELECT "videoDuration", "UUID" FROM "sponsorTimes" WHERE "videoID" = ? AND "service" = ? AND "hidden" = 0
|
const previousSubmissions = await db.prepare('all',
|
||||||
AND "shadowHidden" = 0 AND "votes" >= 0 AND "videoDuration" != 0`, [videoID, service]) as
|
`SELECT "videoDuration", "UUID"
|
||||||
{videoDuration: VideoDuration, UUID: SegmentUUID}[];
|
FROM "sponsorTimes"
|
||||||
|
WHERE "videoID" = ? AND "service" = ? AND
|
||||||
|
"hidden" = 0 AND "shadowHidden" = 0 AND
|
||||||
|
"votes" >= 0 AND "videoDuration" != 0`,
|
||||||
|
[videoID, service]
|
||||||
|
) as {videoDuration: VideoDuration, UUID: SegmentUUID}[];
|
||||||
|
|
||||||
// If the video's duration is changed, then the video should be unlocked and old submissions should be hidden
|
// If the video's duration is changed, then the video should be unlocked and old submissions should be hidden
|
||||||
const videoDurationChanged = (videoDuration: number) => videoDuration != 0 && previousSubmissions.length > 0 && !previousSubmissions.some((e) => Math.abs(videoDuration - e.videoDuration) < 2);
|
const videoDurationChanged = (videoDuration: number) => videoDuration != 0 && previousSubmissions.length > 0 && !previousSubmissions.some((e) => Math.abs(videoDuration - e.videoDuration) < 2);
|
||||||
|
|
||||||
@@ -452,6 +477,9 @@ export async function postSkipSegments(req: Request, res: Response) {
|
|||||||
const UUIDs = [];
|
const UUIDs = [];
|
||||||
const newSegments = [];
|
const newSegments = [];
|
||||||
|
|
||||||
|
//hash the ip 5000 times so no one can get it from the database
|
||||||
|
const hashedIP = getHash(getIP(req) + config.globalSalt);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//get current time
|
//get current time
|
||||||
const timeSubmitted = Date.now();
|
const timeSubmitted = Date.now();
|
||||||
|
|||||||
@@ -23,22 +23,31 @@ describe('postSkipSegments', () => {
|
|||||||
const warnUser01Hash = getHash("warn-user01");
|
const warnUser01Hash = getHash("warn-user01");
|
||||||
const warnUser02Hash = getHash("warn-user02");
|
const warnUser02Hash = getHash("warn-user02");
|
||||||
const warnUser03Hash = getHash("warn-user03");
|
const warnUser03Hash = getHash("warn-user03");
|
||||||
|
const warnUser04Hash = getHash("warn-user04");
|
||||||
|
const reason01 = 'Reason01';
|
||||||
|
const reason02 = '';
|
||||||
|
const reason03 = 'Reason03';
|
||||||
|
const reason04 = '';
|
||||||
const MILLISECONDS_IN_HOUR = 3600000;
|
const MILLISECONDS_IN_HOUR = 3600000;
|
||||||
const warningExpireTime = MILLISECONDS_IN_HOUR * config.hoursAfterWarningExpires;
|
const warningExpireTime = MILLISECONDS_IN_HOUR * config.hoursAfterWarningExpires;
|
||||||
|
|
||||||
const insertWarningQuery = 'INSERT INTO warnings ("userID", "issueTime", "issuerUserID", "enabled") VALUES(?, ?, ?, ?)';
|
const insertWarningQuery = 'INSERT INTO warnings ("userID", "issueTime", "issuerUserID", "enabled", "reason") VALUES(?, ?, ?, ?, ?)';
|
||||||
db.prepare("run", insertWarningQuery, [warnUser01Hash, now, warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser01Hash, now, warnVip01Hash, 1, reason01]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 1000), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 1000), warnVip01Hash, 1, reason01]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 2000), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 2000), warnVip01Hash, 1, reason01]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 3601000), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser01Hash, (now - 3601000), warnVip01Hash, 1, reason01]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1, reason02]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser02Hash, now, warnVip01Hash, 1, reason02]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 1000)), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 1000)), warnVip01Hash, 1, reason02]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 2000)), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser02Hash, (now - (warningExpireTime + 2000)), warnVip01Hash, 1, reason02]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser03Hash, now, warnVip01Hash, 0]);
|
db.prepare("run", insertWarningQuery, [warnUser03Hash, now, warnVip01Hash, 0, reason03]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 1000), warnVip01Hash, 0]);
|
db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 1000), warnVip01Hash, 0, reason03]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 2000), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 2000), warnVip01Hash, 1, reason03]);
|
||||||
db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 3601000), warnVip01Hash, 1]);
|
db.prepare("run", insertWarningQuery, [warnUser03Hash, (now - 3601000), warnVip01Hash, 1, reason03]);
|
||||||
|
db.prepare("run", insertWarningQuery, [warnUser04Hash, now, warnVip01Hash, 0, reason04]);
|
||||||
|
db.prepare("run", insertWarningQuery, [warnUser04Hash, (now - 1000), warnVip01Hash, 0, reason04]);
|
||||||
|
db.prepare("run", insertWarningQuery, [warnUser04Hash, (now - 2000), warnVip01Hash, 1, reason04]);
|
||||||
|
db.prepare("run", insertWarningQuery, [warnUser04Hash, (now - 3601000), warnVip01Hash, 1, reason04]);
|
||||||
|
|
||||||
const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)';
|
const insertVipUserQuery = 'INSERT INTO "vipUsers" ("userID") VALUES (?)';
|
||||||
db.prepare("run", insertVipUserQuery, [getHash("VIPUserSubmission")]);
|
db.prepare("run", insertVipUserQuery, [getHash("VIPUserSubmission")]);
|
||||||
@@ -601,7 +610,7 @@ describe('postSkipSegments', () => {
|
|||||||
.catch(err => done("Couldn't call endpoint"));
|
.catch(err => done("Couldn't call endpoint"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be rejected if user has to many active warnings', (done: Done) => {
|
it('Should be rejected with custom message if user has to many active warnings', (done: Done) => {
|
||||||
fetch(getbaseURL()
|
fetch(getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -619,7 +628,12 @@ describe('postSkipSegments', () => {
|
|||||||
})
|
})
|
||||||
.then(async res => {
|
.then(async res => {
|
||||||
if (res.status === 403) {
|
if (res.status === 403) {
|
||||||
done(); // success
|
const errorMessage = await res.text();
|
||||||
|
if (errorMessage === 'Reason01') {
|
||||||
|
done(); // success
|
||||||
|
} else {
|
||||||
|
done("Status code was 403 but message was: " + errorMessage);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
done("Status code was " + res.status);
|
done("Status code was " + res.status);
|
||||||
}
|
}
|
||||||
@@ -693,6 +707,37 @@ describe('postSkipSegments', () => {
|
|||||||
.catch(err => done(true));
|
.catch(err => done(true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should be rejected with default message if user has to many active warnings', (done: Done) => {
|
||||||
|
fetch(getbaseURL()
|
||||||
|
+ "/api/postVideoSponsorTimes", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
userID: "warn-user01",
|
||||||
|
videoID: "dQw4w9WgXcF",
|
||||||
|
segments: [{
|
||||||
|
segment: [0, 10],
|
||||||
|
category: "sponsor",
|
||||||
|
}],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then(async res => {
|
||||||
|
if (res.status === 403) {
|
||||||
|
const errorMessage = await res.text();
|
||||||
|
if (errorMessage !== '') {
|
||||||
|
done(); // success
|
||||||
|
} else {
|
||||||
|
done("Status code was 403 but message was: " + errorMessage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
done("Status code was " + res.status);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => done(err));
|
||||||
|
});
|
||||||
|
|
||||||
it('Should return 400 for missing params (JSON method) 1', (done: Done) => {
|
it('Should return 400 for missing params (JSON method) 1', (done: Done) => {
|
||||||
fetch(getbaseURL()
|
fetch(getbaseURL()
|
||||||
+ "/api/postVideoSponsorTimes", {
|
+ "/api/postVideoSponsorTimes", {
|
||||||
|
|||||||
Reference in New Issue
Block a user