mirror of
https://github.com/ajayyy/SponsorBlockServer.git
synced 2025-12-13 06:57:05 +03:00
Require permission for filler submissions
This commit is contained in:
@@ -44,6 +44,7 @@ addDefaults(config, {
|
|||||||
discordReportChannelWebhookURL: null,
|
discordReportChannelWebhookURL: null,
|
||||||
discordMaliciousReportWebhookURL: null,
|
discordMaliciousReportWebhookURL: null,
|
||||||
minReputationToSubmitChapter: 0,
|
minReputationToSubmitChapter: 0,
|
||||||
|
minReputationToSubmitFiller: 0,
|
||||||
getTopUsersCacheTimeMinutes: 240,
|
getTopUsersCacheTimeMinutes: 240,
|
||||||
globalSalt: null,
|
globalSalt: null,
|
||||||
mode: "",
|
mode: "",
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ interface AddFeatureRequest extends Request {
|
|||||||
|
|
||||||
const allowedFeatures = {
|
const allowedFeatures = {
|
||||||
vip: [
|
vip: [
|
||||||
Feature.ChapterSubmitter
|
Feature.ChapterSubmitter,
|
||||||
|
Feature.FillerSubmitter
|
||||||
],
|
],
|
||||||
admin: [
|
admin: [
|
||||||
Feature.ChapterSubmitter
|
Feature.ChapterSubmitter,
|
||||||
|
Feature.FillerSubmitter
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { Request, Response } from "express";
|
|||||||
import { Logger } from "../utils/logger";
|
import { Logger } from "../utils/logger";
|
||||||
import { HashedUserID, UserID } from "../types/user.model";
|
import { HashedUserID, UserID } from "../types/user.model";
|
||||||
import { getReputation } from "../utils/reputation";
|
import { getReputation } from "../utils/reputation";
|
||||||
import { SegmentUUID } from "../types/segments.model";
|
import { Category, SegmentUUID } from "../types/segments.model";
|
||||||
import { config } from "../config";
|
import { config } from "../config";
|
||||||
import { canSubmitChapter } from "../utils/permissions";
|
import { canSubmit } from "../utils/permissions";
|
||||||
const maxRewardTime = config.maxRewardTimePerSegmentInSeconds;
|
const maxRewardTime = config.maxRewardTimePerSegmentInSeconds;
|
||||||
|
|
||||||
async function dbGetSubmittedSegmentSummary(userID: HashedUserID): Promise<{ minutesSaved: number, segmentCount: number }> {
|
async function dbGetSubmittedSegmentSummary(userID: HashedUserID): Promise<{ minutesSaved: number, segmentCount: number }> {
|
||||||
@@ -106,6 +106,15 @@ async function dbGetBanned(userID: HashedUserID): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getPermissions(userID: HashedUserID): Promise<Record<string, boolean>> {
|
||||||
|
const result: Record<string, boolean> = {};
|
||||||
|
for (const category of config.categoryList) {
|
||||||
|
result[category] = (await canSubmit(userID, category as Category)).canSubmit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
type cases = Record<string, any>
|
type cases = Record<string, any>
|
||||||
|
|
||||||
const executeIfFunction = (f: any) =>
|
const executeIfFunction = (f: any) =>
|
||||||
@@ -130,7 +139,7 @@ const dbGetValue = (userID: HashedUserID, property: string): Promise<string|Segm
|
|||||||
reputation: () => getReputation(userID),
|
reputation: () => getReputation(userID),
|
||||||
vip: () => isUserVIP(userID),
|
vip: () => isUserVIP(userID),
|
||||||
lastSegmentID: () => dbGetLastSegmentForUser(userID),
|
lastSegmentID: () => dbGetLastSegmentForUser(userID),
|
||||||
canSubmitChapter: () => canSubmitChapter(userID)
|
permissions: () => getPermissions(userID)
|
||||||
})("")(property);
|
})("")(property);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -140,7 +149,7 @@ async function getUserInfo(req: Request, res: Response): Promise<Response> {
|
|||||||
const defaultProperties: string[] = ["userID", "userName", "minutesSaved", "segmentCount", "ignoredSegmentCount",
|
const defaultProperties: string[] = ["userID", "userName", "minutesSaved", "segmentCount", "ignoredSegmentCount",
|
||||||
"viewCount", "ignoredViewCount", "warnings", "warningReason", "reputation",
|
"viewCount", "ignoredViewCount", "warnings", "warningReason", "reputation",
|
||||||
"vip", "lastSegmentID"];
|
"vip", "lastSegmentID"];
|
||||||
const allProperties: string[] = [...defaultProperties, "banned", "canSubmitChapter"];
|
const allProperties: string[] = [...defaultProperties, "banned", "permissions"];
|
||||||
let paramValues: string[] = req.query.values
|
let paramValues: string[] = req.query.values
|
||||||
? JSON.parse(req.query.values as string)
|
? JSON.parse(req.query.values as string)
|
||||||
: req.query.value
|
: req.query.value
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { parseUserAgent } from "../utils/userAgent";
|
|||||||
import { getService } from "../utils/getService";
|
import { getService } from "../utils/getService";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { vote } from "./voteOnSponsorTime";
|
import { vote } from "./voteOnSponsorTime";
|
||||||
import { canSubmitChapter } from "../utils/permissions";
|
import { canSubmit } from "../utils/permissions";
|
||||||
|
|
||||||
type CheckResult = {
|
type CheckResult = {
|
||||||
pass: boolean,
|
pass: boolean,
|
||||||
@@ -230,8 +230,10 @@ async function checkInvalidFields(videoID: VideoID, userID: UserID, hashedUserID
|
|||||||
invalidFields.push("segment description");
|
invalidFields.push("segment description");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segmentPair.actionType === ActionType.Chapter && !(await canSubmitChapter(hashedUserID))) {
|
const permission = await canSubmit(hashedUserID, segmentPair.category);
|
||||||
invalidFields.push("permission to submit chapters");
|
if (!permission.canSubmit) {
|
||||||
|
invalidFields.push(`permission to submit ${segmentPair.category}`);
|
||||||
|
errors.push(permission.reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +243,7 @@ async function checkInvalidFields(videoID: VideoID, userID: UserID, hashedUserID
|
|||||||
const formattedErrors = errors.reduce((p, c, i) => p + (i !== 0 ? ". " : " ") + c, "");
|
const formattedErrors = errors.reduce((p, c, i) => p + (i !== 0 ? ". " : " ") + c, "");
|
||||||
return {
|
return {
|
||||||
pass: false,
|
pass: false,
|
||||||
errorMessage: `No valid ${formattedFields} field(s) provided.${formattedErrors}`,
|
errorMessage: `No valid ${formattedFields}.${formattedErrors}`,
|
||||||
errorCode: 400
|
errorCode: 400
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export interface SBSConfig {
|
|||||||
neuralBlockURL?: string;
|
neuralBlockURL?: string;
|
||||||
discordNeuralBlockRejectWebhookURL?: string;
|
discordNeuralBlockRejectWebhookURL?: string;
|
||||||
minReputationToSubmitChapter: number;
|
minReputationToSubmitChapter: number;
|
||||||
|
minReputationToSubmitFiller: number;
|
||||||
userCounterURL?: string;
|
userCounterURL?: string;
|
||||||
proxySubmission?: string;
|
proxySubmission?: string;
|
||||||
behindProxy: string | boolean;
|
behindProxy: string | boolean;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { HashedUserID, UserID } from "./user.model";
|
|||||||
export type SegmentUUID = string & { __segmentUUIDBrand: unknown };
|
export type SegmentUUID = string & { __segmentUUIDBrand: unknown };
|
||||||
export type VideoID = string & { __videoIDBrand: unknown };
|
export type VideoID = string & { __videoIDBrand: unknown };
|
||||||
export type VideoDuration = number & { __videoDurationBrand: unknown };
|
export type VideoDuration = number & { __videoDurationBrand: unknown };
|
||||||
export type Category = ("sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "poi_highlight" | "chapter") & { __categoryBrand: unknown };
|
export type Category = ("sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "filler" | "poi_highlight" | "chapter") & { __categoryBrand: unknown };
|
||||||
export type VideoIDHash = VideoID & HashedValue;
|
export type VideoIDHash = VideoID & HashedValue;
|
||||||
export type IPAddress = string & { __ipAddressBrand: unknown };
|
export type IPAddress = string & { __ipAddressBrand: unknown };
|
||||||
export type HashedIP = IPAddress & HashedValue;
|
export type HashedIP = IPAddress & HashedValue;
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ export type UserID = string & { __userIDBrand: unknown };
|
|||||||
export type HashedUserID = UserID & HashedValue;
|
export type HashedUserID = UserID & HashedValue;
|
||||||
|
|
||||||
export enum Feature {
|
export enum Feature {
|
||||||
ChapterSubmitter = 0
|
ChapterSubmitter = 0,
|
||||||
|
FillerSubmitter = 1
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,33 @@
|
|||||||
import { config } from "../config";
|
import { config } from "../config";
|
||||||
|
import { Category } from "../types/segments.model";
|
||||||
import { Feature, HashedUserID } from "../types/user.model";
|
import { Feature, HashedUserID } from "../types/user.model";
|
||||||
import { hasFeature } from "./features";
|
import { hasFeature } from "./features";
|
||||||
import { isUserVIP } from "./isUserVIP";
|
import { isUserVIP } from "./isUserVIP";
|
||||||
import { getReputation } from "./reputation";
|
import { getReputation } from "./reputation";
|
||||||
|
|
||||||
export async function canSubmitChapter(userID: HashedUserID): Promise<boolean> {
|
interface CanSubmitResult {
|
||||||
return (await isUserVIP(userID))
|
canSubmit: boolean;
|
||||||
|| (await getReputation(userID)) > config.minReputationToSubmitChapter
|
reason?: string;
|
||||||
|| (await hasFeature(userID, Feature.ChapterSubmitter));
|
}
|
||||||
|
|
||||||
|
export async function canSubmit(userID: HashedUserID, category: Category): Promise<CanSubmitResult> {
|
||||||
|
switch (category) {
|
||||||
|
case "chapter":
|
||||||
|
return {
|
||||||
|
canSubmit: (await isUserVIP(userID))
|
||||||
|
|| (await getReputation(userID)) > config.minReputationToSubmitChapter
|
||||||
|
|| (await hasFeature(userID, Feature.ChapterSubmitter))
|
||||||
|
};
|
||||||
|
case "filler":
|
||||||
|
return {
|
||||||
|
canSubmit: (await isUserVIP(userID))
|
||||||
|
|| (await getReputation(userID)) > config.minReputationToSubmitFiller
|
||||||
|
|| (await hasFeature(userID, Feature.FillerSubmitter)),
|
||||||
|
reason: "Someone is submitting over 180,000 spam filler submissions and refuses to stop even after talking with them, so we have to restrict it for now. You can request submission access on chat.sponsor.ajay.app, discord.gg/SponsorBlock or matrix.to/#/#sponsor:ajay.app"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
canSubmit: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user