mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
677 lines
21 KiB
JavaScript
677 lines
21 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { createModerationStage } from "../js/feedEngine/stages.js";
|
|
import {
|
|
DEFAULT_AUTOPLAY_BLOCK_THRESHOLD,
|
|
DEFAULT_BLUR_THRESHOLD,
|
|
} from "../js/constants.js";
|
|
|
|
test("moderation stage enforces admin lists without whitelist bypass", async () => {
|
|
const whitelistedHex = "1".repeat(64);
|
|
const blockedHex = "2".repeat(64);
|
|
const blacklistedHex = "3".repeat(64);
|
|
const normalHex = "4".repeat(64);
|
|
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return {
|
|
whitelist: new Set([`npub${whitelistedHex}`]),
|
|
whitelistHex: new Set([whitelistedHex]),
|
|
blacklist: new Set([`npub${blacklistedHex}`]),
|
|
blacklistHex: new Set([blacklistedHex]),
|
|
};
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
const normalized = typeof identifier === "string" ? identifier.trim().toLowerCase() : "";
|
|
if (normalized === whitelistedHex) {
|
|
return { hex: whitelistedHex, whitelisted: true, blacklisted: false };
|
|
}
|
|
if (normalized === blacklistedHex) {
|
|
return { hex: blacklistedHex, whitelisted: false, blacklisted: true };
|
|
}
|
|
if (normalized === blockedHex) {
|
|
return { hex: blockedHex, whitelisted: false, blacklisted: false };
|
|
}
|
|
if (normalized === normalHex) {
|
|
return { hex: normalHex, whitelisted: false, blacklisted: false };
|
|
}
|
|
return { hex: normalized, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary() {
|
|
return null;
|
|
},
|
|
trustedReportCount(videoId) {
|
|
if (videoId === "c") {
|
|
return 5;
|
|
}
|
|
if (videoId === "d") {
|
|
return 3;
|
|
}
|
|
return 0;
|
|
},
|
|
getTrustedReporters() {
|
|
return [];
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({
|
|
service,
|
|
reportType: "nudity",
|
|
autoplayThreshold: DEFAULT_AUTOPLAY_BLOCK_THRESHOLD,
|
|
blurThreshold: DEFAULT_BLUR_THRESHOLD,
|
|
});
|
|
|
|
const items = [
|
|
{ video: { id: "a", pubkey: blockedHex }, metadata: {} },
|
|
{ video: { id: "b", pubkey: blacklistedHex }, metadata: {} },
|
|
{ video: { id: "c", pubkey: whitelistedHex }, metadata: {} },
|
|
{ video: { id: "d", pubkey: normalHex }, metadata: {} },
|
|
];
|
|
|
|
const why = [];
|
|
const context = {
|
|
feedName: "discovery",
|
|
runtime: {
|
|
isAuthorBlocked: (pubkey) => pubkey === blockedHex,
|
|
},
|
|
addWhy(detail) {
|
|
why.push(detail);
|
|
return detail;
|
|
},
|
|
log() {},
|
|
};
|
|
|
|
const result = await stage(items, context);
|
|
|
|
assert.equal(result.length, 2);
|
|
assert.deepEqual(
|
|
result.map((entry) => entry.video.id),
|
|
["c", "d"]
|
|
);
|
|
|
|
const whitelistedItem = result[0];
|
|
assert.equal(whitelistedItem.video.moderation.blockAutoplay, true);
|
|
assert.equal(whitelistedItem.video.moderation.blurThumbnail, true);
|
|
assert.equal(whitelistedItem.video.moderation.blurReason, "trusted-report");
|
|
assert.equal(whitelistedItem.metadata.moderation.blurReason, "trusted-report");
|
|
assert.equal(whitelistedItem.video.moderation.adminWhitelist, true);
|
|
assert.equal(whitelistedItem.video.moderation.adminWhitelistBypass, false);
|
|
assert.equal(whitelistedItem.metadata.moderation.adminWhitelistBypass, false);
|
|
|
|
const normalItem = result[1];
|
|
assert.equal(normalItem.video.moderation.blockAutoplay, true);
|
|
assert.equal(normalItem.video.moderation.blurThumbnail, true);
|
|
assert.equal(normalItem.video.moderation.blurReason, "trusted-report");
|
|
assert.equal(normalItem.metadata.moderation.blurReason, "trusted-report");
|
|
|
|
const reasons = why.map((entry) => entry.reason);
|
|
assert(reasons.includes("viewer-block"));
|
|
assert(reasons.includes("admin-blacklist"));
|
|
assert(reasons.includes("blur"));
|
|
});
|
|
|
|
test("moderation stage annotates trusted mute metadata", async () => {
|
|
const mutedHex = "9".repeat(64);
|
|
const muterHex = "8".repeat(64);
|
|
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return { whitelist: new Set(), whitelistHex: new Set(), blacklist: new Set(), blacklistHex: new Set() };
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
return { hex: identifier, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary() {
|
|
return null;
|
|
},
|
|
trustedReportCount() {
|
|
return 0;
|
|
},
|
|
getTrustedReporters() {
|
|
return [];
|
|
},
|
|
isAuthorMutedByTrusted(pubkey) {
|
|
return pubkey === mutedHex;
|
|
},
|
|
getTrustedMutersForAuthor(pubkey) {
|
|
return pubkey === mutedHex ? [muterHex] : [];
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({ service });
|
|
|
|
const items = [
|
|
{ video: { id: "muted", pubkey: mutedHex }, metadata: {} },
|
|
{ video: { id: "normal", pubkey: "7".repeat(64) }, metadata: {} },
|
|
];
|
|
|
|
const reasons = [];
|
|
const context = {
|
|
addWhy(detail) {
|
|
reasons.push(detail);
|
|
return detail;
|
|
},
|
|
log() {},
|
|
};
|
|
|
|
const result = await stage(items, context);
|
|
|
|
assert.equal(result.length, 2);
|
|
|
|
const mutedItem = result.find((entry) => entry.video.id === "muted");
|
|
assert.ok(mutedItem);
|
|
assert.equal(mutedItem.video.moderation.trustedMuted, true);
|
|
assert.deepEqual(mutedItem.video.moderation.trustedMuters, [muterHex]);
|
|
assert.equal(mutedItem.video.moderation.trustedMuteCount, 1);
|
|
assert.equal(mutedItem.metadata.moderation.trustedMuted, true);
|
|
assert.equal(mutedItem.metadata.moderation.trustedMuteCount, 1);
|
|
|
|
const trustedMuteReason = reasons.find((entry) => entry.reason === "trusted-mute");
|
|
assert.ok(trustedMuteReason);
|
|
assert.equal(trustedMuteReason.videoId, "muted");
|
|
});
|
|
|
|
test("moderation stage applies provided thresholds", async () => {
|
|
const videoId = "custom-threshold";
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return { whitelist: new Set(), whitelistHex: new Set(), blacklist: new Set(), blacklistHex: new Set() };
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
return { hex: identifier, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary() {
|
|
return null;
|
|
},
|
|
trustedReportCount() {
|
|
return 4;
|
|
},
|
|
getTrustedReporters() {
|
|
return [];
|
|
},
|
|
};
|
|
|
|
const items = [{ video: { id: videoId, pubkey: "a".repeat(64) }, metadata: {} }];
|
|
|
|
const strictStage = createModerationStage({
|
|
service,
|
|
autoplayThreshold: 5,
|
|
blurThreshold: 6,
|
|
});
|
|
|
|
const relaxedStage = createModerationStage({
|
|
service,
|
|
autoplayThreshold: 1,
|
|
blurThreshold: 1,
|
|
});
|
|
|
|
const strictResult = await strictStage(items, {});
|
|
assert.equal(strictResult.length, 1);
|
|
assert.equal(strictResult[0].video.moderation.blockAutoplay, false);
|
|
assert.equal(strictResult[0].video.moderation.blurThumbnail, false);
|
|
assert.equal(strictResult[0].video.moderation.blurReason, undefined);
|
|
|
|
const relaxedResult = await relaxedStage(items, {});
|
|
assert.equal(relaxedResult.length, 1);
|
|
assert.equal(relaxedResult[0].video.moderation.blockAutoplay, true);
|
|
assert.equal(relaxedResult[0].video.moderation.blurThumbnail, true);
|
|
assert.equal(relaxedResult[0].video.moderation.blurReason, "trusted-report");
|
|
assert.equal(relaxedResult[0].metadata.moderation.blurReason, "trusted-report");
|
|
});
|
|
|
|
test("moderation stage respects runtime threshold changes", async () => {
|
|
const videoId = "runtime-threshold";
|
|
const authorHex = "e".repeat(64);
|
|
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return { whitelist: new Set(), whitelistHex: new Set(), blacklist: new Set(), blacklistHex: new Set() };
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
return { hex: identifier, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary() {
|
|
return null;
|
|
},
|
|
trustedReportCount() {
|
|
return 3;
|
|
},
|
|
getTrustedReporters() {
|
|
return [];
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({ service });
|
|
|
|
const strictContext = {
|
|
runtime: {
|
|
moderationThresholds: {
|
|
autoplayBlockThreshold: 1,
|
|
blurThreshold: 1,
|
|
trustedMuteHideThreshold: Number.POSITIVE_INFINITY,
|
|
trustedSpamHideThreshold: 2,
|
|
},
|
|
},
|
|
addWhy() {},
|
|
log() {},
|
|
};
|
|
|
|
const relaxedContext = {
|
|
runtime: {
|
|
moderationThresholds: {
|
|
autoplayBlockThreshold: 5,
|
|
blurThreshold: 5,
|
|
trustedMuteHideThreshold: Number.POSITIVE_INFINITY,
|
|
trustedSpamHideThreshold: 10,
|
|
},
|
|
},
|
|
addWhy() {},
|
|
log() {},
|
|
};
|
|
|
|
const buildItem = () => ({ video: { id: videoId, pubkey: authorHex }, metadata: {} });
|
|
|
|
const hiddenItems = [buildItem()];
|
|
const hiddenResult = await stage(hiddenItems, strictContext);
|
|
|
|
assert.equal(hiddenResult.length, 0);
|
|
const hiddenMetadata = hiddenItems[0].metadata.moderation;
|
|
assert.equal(hiddenMetadata.hidden, true);
|
|
assert.equal(hiddenMetadata.hideReason, "trusted-report-hide");
|
|
assert.equal(hiddenMetadata.blockAutoplay, true);
|
|
assert.equal(hiddenMetadata.blurThumbnail, true);
|
|
|
|
const visibleItems = [buildItem()];
|
|
const visibleResult = await stage(visibleItems, relaxedContext);
|
|
|
|
assert.equal(visibleResult.length, 1);
|
|
const visibleModeration = visibleResult[0].video.moderation;
|
|
assert.equal(visibleModeration.hidden, false);
|
|
assert.equal(visibleModeration.blockAutoplay, false);
|
|
assert.equal(visibleModeration.blurThumbnail, false);
|
|
});
|
|
|
|
test("moderation stage supports function-based threshold resolvers", async () => {
|
|
const videoId = "resolver-threshold";
|
|
const authorHex = "f".repeat(64);
|
|
|
|
let autoplaySetting = 5;
|
|
let blurSetting = 6;
|
|
let hideSetting = Number.POSITIVE_INFINITY;
|
|
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return { whitelist: new Set(), whitelistHex: new Set(), blacklist: new Set(), blacklistHex: new Set() };
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
return { hex: identifier, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary() {
|
|
return null;
|
|
},
|
|
trustedReportCount() {
|
|
return 2;
|
|
},
|
|
getTrustedReporters() {
|
|
return [];
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({
|
|
service,
|
|
autoplayThreshold: () => autoplaySetting,
|
|
blurThreshold: () => blurSetting,
|
|
trustedReportHideThreshold: () => hideSetting,
|
|
});
|
|
|
|
const createContext = () => ({ addWhy() {}, log() {} });
|
|
const buildItem = () => ({ video: { id: videoId, pubkey: authorHex }, metadata: {} });
|
|
|
|
const relaxedItems = [buildItem()];
|
|
const relaxedResult = await stage(relaxedItems, createContext());
|
|
|
|
assert.equal(relaxedResult.length, 1);
|
|
const relaxedModeration = relaxedResult[0].video.moderation;
|
|
assert.equal(relaxedModeration.blockAutoplay, false);
|
|
assert.equal(relaxedModeration.blurThumbnail, false);
|
|
assert.equal(relaxedModeration.hidden, false);
|
|
|
|
autoplaySetting = 1;
|
|
blurSetting = 1;
|
|
hideSetting = 1;
|
|
|
|
const strictItems = [buildItem()];
|
|
const strictResult = await stage(strictItems, createContext());
|
|
|
|
assert.equal(strictResult.length, 0);
|
|
const strictMetadata = strictItems[0].metadata.moderation;
|
|
assert.equal(strictMetadata.blockAutoplay, true);
|
|
assert.equal(strictMetadata.blurThumbnail, true);
|
|
assert.equal(strictMetadata.hidden, true);
|
|
assert.equal(strictMetadata.hideReason, "trusted-report-hide");
|
|
});
|
|
|
|
test("moderation stage propagates whitelist, muters, and threshold updates", async () => {
|
|
const whitelistedHex = "a".repeat(64);
|
|
const mutedHex = "b".repeat(64);
|
|
const muterHex = "c".repeat(64);
|
|
|
|
const state = {
|
|
whitelistHex: new Set([whitelistedHex]),
|
|
summaryById: new Map([
|
|
[
|
|
"muted-video",
|
|
{
|
|
eventId: "muted-video",
|
|
totalTrusted: 3,
|
|
types: { nudity: { trusted: 3, total: 3, latest: 1_700_000_000 } },
|
|
updatedAt: 1_700_000_100,
|
|
},
|
|
],
|
|
]),
|
|
trustedCounts: new Map([
|
|
["whitelisted-video", 4],
|
|
["muted-video", 3],
|
|
]),
|
|
reportersById: new Map([
|
|
["muted-video", [{ pubkey: muterHex, latest: 1_700_000_050 }]],
|
|
]),
|
|
mutedAuthors: new Set([mutedHex]),
|
|
mutersByAuthor: new Map([[mutedHex, [muterHex]]]),
|
|
};
|
|
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return {
|
|
whitelist: new Set([`npub${whitelistedHex}`]),
|
|
whitelistHex: new Set(state.whitelistHex),
|
|
blacklist: new Set(),
|
|
blacklistHex: new Set(),
|
|
};
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
const normalized = typeof identifier === "string" ? identifier.trim().toLowerCase() : "";
|
|
return {
|
|
hex: normalized,
|
|
whitelisted: state.whitelistHex.has(normalized),
|
|
blacklisted: false,
|
|
};
|
|
},
|
|
getTrustedReportSummary(videoId) {
|
|
return state.summaryById.get(videoId) || null;
|
|
},
|
|
trustedReportCount(videoId) {
|
|
return state.trustedCounts.get(videoId) || 0;
|
|
},
|
|
getTrustedReporters(videoId) {
|
|
return state.reportersById.get(videoId) || [];
|
|
},
|
|
isAuthorMutedByTrusted(pubkey) {
|
|
return state.mutedAuthors.has(pubkey);
|
|
},
|
|
getTrustedMutersForAuthor(pubkey) {
|
|
return state.mutersByAuthor.get(pubkey) || [];
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({
|
|
service,
|
|
autoplayThreshold: 1,
|
|
blurThreshold: 1,
|
|
reportType: "nudity",
|
|
});
|
|
|
|
const items = [
|
|
{ video: { id: "whitelisted-video", pubkey: whitelistedHex }, metadata: {} },
|
|
{ video: { id: "muted-video", pubkey: mutedHex }, metadata: {} },
|
|
];
|
|
|
|
const reasons = [];
|
|
const context = {
|
|
feedName: "discovery",
|
|
addWhy(detail) {
|
|
reasons.push(detail);
|
|
return detail;
|
|
},
|
|
log() {},
|
|
};
|
|
|
|
const firstPass = await stage(items, context);
|
|
const whitelistedItem = firstPass.find((entry) => entry.video.id === "whitelisted-video");
|
|
const mutedItem = firstPass.find((entry) => entry.video.id === "muted-video");
|
|
|
|
assert.ok(whitelistedItem);
|
|
assert.ok(mutedItem);
|
|
|
|
assert.equal(whitelistedItem.metadata.moderation.adminWhitelist, true);
|
|
assert.equal(whitelistedItem.metadata.moderation.blockAutoplay, true);
|
|
assert.equal(whitelistedItem.metadata.moderation.blurThumbnail, true);
|
|
assert.equal(whitelistedItem.metadata.moderation.blurReason, "trusted-report");
|
|
assert.equal(whitelistedItem.video.moderation.adminWhitelistBypass, false);
|
|
assert.equal(whitelistedItem.video.moderation.blurReason, "trusted-report");
|
|
|
|
assert.equal(mutedItem.metadata.moderation.trustedMuted, true);
|
|
assert.deepEqual(mutedItem.metadata.moderation.trustedMuters, [muterHex]);
|
|
assert.equal(mutedItem.metadata.moderation.blockAutoplay, true);
|
|
assert.equal(mutedItem.metadata.moderation.blurThumbnail, true);
|
|
assert.equal(mutedItem.metadata.moderation.blurReason, "trusted-report");
|
|
assert.equal(mutedItem.video.moderation.trustedMuted, true);
|
|
assert.deepEqual(mutedItem.video.moderation.trustedMuters, [muterHex]);
|
|
assert.equal(mutedItem.video.moderation.blurReason, "trusted-report");
|
|
|
|
state.summaryById.set("muted-video", {
|
|
eventId: "muted-video",
|
|
totalTrusted: 0,
|
|
types: { nudity: { trusted: 0, total: 0, latest: 1_700_000_500 } },
|
|
updatedAt: 1_700_000_600,
|
|
});
|
|
state.trustedCounts.set("muted-video", 0);
|
|
state.mutersByAuthor.set(mutedHex, []);
|
|
state.mutedAuthors.delete(mutedHex);
|
|
|
|
const secondPass = await stage(firstPass, context);
|
|
const updatedMuted = secondPass.find((entry) => entry.video.id === "muted-video");
|
|
|
|
assert.ok(updatedMuted);
|
|
assert.equal(updatedMuted.metadata.moderation.trustedMuted, false);
|
|
assert.deepEqual(updatedMuted.metadata.moderation.trustedMuters, []);
|
|
assert.equal(updatedMuted.metadata.moderation.blockAutoplay, false);
|
|
assert.equal(updatedMuted.metadata.moderation.blurThumbnail, false);
|
|
assert.equal(updatedMuted.metadata.moderation.blurReason, undefined);
|
|
assert.equal(updatedMuted.video.moderation.trustedMuted, false);
|
|
assert.equal(updatedMuted.video.moderation.blockAutoplay, false);
|
|
assert.equal(updatedMuted.video.moderation.blurThumbnail, false);
|
|
assert.equal(updatedMuted.video.moderation.blurReason, undefined);
|
|
});
|
|
|
|
test("moderation stage blurs viewer-muted authors", async () => {
|
|
const mutedHex = "f".repeat(64);
|
|
|
|
const service = {
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return { whitelist: new Set(), whitelistHex: new Set(), blacklist: new Set(), blacklistHex: new Set() };
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
return { hex: identifier, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary() {
|
|
return null;
|
|
},
|
|
trustedReportCount() {
|
|
return 0;
|
|
},
|
|
getTrustedReporters() {
|
|
return [];
|
|
},
|
|
isAuthorMutedByTrusted() {
|
|
return false;
|
|
},
|
|
isAuthorMutedByViewer(pubkey) {
|
|
return pubkey === mutedHex;
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({
|
|
service,
|
|
autoplayThreshold: Number.POSITIVE_INFINITY,
|
|
blurThreshold: Number.POSITIVE_INFINITY,
|
|
trustedMuteHideThreshold: Number.POSITIVE_INFINITY,
|
|
trustedReportHideThreshold: Number.POSITIVE_INFINITY,
|
|
});
|
|
|
|
const items = [{ video: { id: "viewer-muted", pubkey: mutedHex }, metadata: {} }];
|
|
|
|
const reasons = [];
|
|
const context = {
|
|
addWhy(detail) {
|
|
reasons.push(detail);
|
|
return detail;
|
|
},
|
|
log() {},
|
|
};
|
|
|
|
const result = await stage(items, context);
|
|
|
|
assert.equal(result.length, 1);
|
|
const entry = result[0];
|
|
|
|
assert.equal(entry.video.moderation.viewerMuted, true);
|
|
assert.equal(entry.metadata.moderation.viewerMuted, true);
|
|
assert.equal(entry.video.moderation.blockAutoplay, true);
|
|
assert.equal(entry.video.moderation.blurThumbnail, true);
|
|
assert.equal(entry.video.moderation.blurReason, "viewer-mute");
|
|
assert.equal(entry.metadata.moderation.blurReason, "viewer-mute");
|
|
assert.equal(entry.video.moderation.hidden, false);
|
|
assert.equal(entry.metadata.moderation.hidden, false);
|
|
assert.equal(entry.video.moderation.hideReason, undefined);
|
|
assert.equal(entry.metadata.moderation.hideReason, undefined);
|
|
assert.equal(entry.metadata.moderation.hideCounts, undefined);
|
|
|
|
const reasonSet = new Set(reasons.map((detail) => detail.reason));
|
|
assert(reasonSet.has("blur"));
|
|
assert(reasonSet.has("viewer-mute"));
|
|
});
|
|
|
|
test("moderation stage clears cached reporters and muters after service signals", async () => {
|
|
const reporterHex = "d".repeat(64);
|
|
const muterHex = "e".repeat(64);
|
|
const authorHex = "f".repeat(64);
|
|
const videoId = "9".repeat(64);
|
|
|
|
const listeners = new Map();
|
|
|
|
const service = {
|
|
reporters: [{ pubkey: reporterHex, latest: 1_700_000_100 }],
|
|
muters: [muterHex],
|
|
async refreshViewerFromClient() {},
|
|
async setActiveEventIds() {},
|
|
getAdminListSnapshot() {
|
|
return { whitelist: new Set(), whitelistHex: new Set(), blacklist: new Set(), blacklistHex: new Set() };
|
|
},
|
|
getAccessControlStatus(identifier) {
|
|
return { hex: identifier, whitelisted: false, blacklisted: false };
|
|
},
|
|
getTrustedReportSummary(targetId) {
|
|
const trusted = this.reporters.length;
|
|
return {
|
|
eventId: targetId,
|
|
totalTrusted: trusted,
|
|
types: {
|
|
nudity: {
|
|
trusted,
|
|
total: trusted,
|
|
latest: this.reporters[0]?.latest ?? 0,
|
|
},
|
|
},
|
|
updatedAt: Date.now(),
|
|
};
|
|
},
|
|
trustedReportCount() {
|
|
return this.reporters.length;
|
|
},
|
|
getTrustedReporters() {
|
|
return this.reporters.slice();
|
|
},
|
|
isAuthorMutedByTrusted() {
|
|
return this.muters.length > 0;
|
|
},
|
|
getTrustedMutersForAuthor() {
|
|
return this.muters.slice();
|
|
},
|
|
on(eventName, handler) {
|
|
if (typeof handler !== "function") {
|
|
return () => {};
|
|
}
|
|
if (!listeners.has(eventName)) {
|
|
listeners.set(eventName, new Set());
|
|
}
|
|
const bucket = listeners.get(eventName);
|
|
bucket.add(handler);
|
|
return () => {
|
|
bucket.delete(handler);
|
|
if (!bucket.size) {
|
|
listeners.delete(eventName);
|
|
}
|
|
};
|
|
},
|
|
emit(eventName, detail) {
|
|
const bucket = listeners.get(eventName);
|
|
if (!bucket) {
|
|
return;
|
|
}
|
|
for (const handler of Array.from(bucket)) {
|
|
handler(detail);
|
|
}
|
|
},
|
|
};
|
|
|
|
const stage = createModerationStage({ service, reportType: "nudity" });
|
|
|
|
const context = {
|
|
addWhy() {},
|
|
log() {},
|
|
};
|
|
|
|
const items = [{ video: { id: videoId, pubkey: authorHex }, metadata: {} }];
|
|
|
|
const firstPass = await stage(items, context);
|
|
const firstVideo = firstPass[0].video;
|
|
|
|
assert.equal(firstVideo.moderation.trustedCount, 1);
|
|
assert.equal(firstVideo.moderation.trustedMuted, true);
|
|
assert.deepEqual(firstVideo.moderation.trustedMuters, [muterHex]);
|
|
assert.deepEqual(firstVideo.moderation.trustedReporters, [{ pubkey: reporterHex, latest: 1_700_000_100 }]);
|
|
|
|
service.reporters = [];
|
|
service.muters = [];
|
|
service.emit("user-blocks", { action: "block", targetPubkey: reporterHex });
|
|
service.emit("trusted-mutes", { total: 0 });
|
|
|
|
const secondPass = await stage(firstPass, context);
|
|
const updatedVideo = secondPass[0].video;
|
|
const updatedMetadata = secondPass[0].metadata.moderation;
|
|
|
|
assert.equal(updatedVideo.moderation.trustedCount, 0);
|
|
assert.equal(updatedVideo.moderation.trustedMuted, false);
|
|
assert.equal("trustedMuters" in updatedVideo.moderation, false);
|
|
assert.equal("trustedReporters" in updatedVideo.moderation, false);
|
|
assert.ok(Array.isArray(updatedMetadata.trustedReporters));
|
|
assert.equal(updatedMetadata.trustedReporters.length, 0);
|
|
assert.ok(Array.isArray(updatedMetadata.trustedMuters));
|
|
assert.equal(updatedMetadata.trustedMuters.length, 0);
|
|
});
|