mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 00:12:25 +00:00
- Updated `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD` in `config/instance-config.js` from 1 to 20. - Updated `tests/moderation/trusted-mute-lists.test.mjs` to explicitly override the threshold to 1 for tests verifying the hide mechanism. - Updated documentation (`docs/moderation/*.md`, `docs/instance-config.md`, `content/community-guidelines.md`) to reflect the new default and upstream examples. - Verified that existing blurring logic in `js/feedEngine/stages.js` and `js/ui/components/VideoCard.js` handles the intermediate state (1-19 mutes) correctly.
181 lines
6.1 KiB
JavaScript
181 lines
6.1 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
import {
|
|
DEFAULT_AUTOPLAY_BLOCK_THRESHOLD,
|
|
DEFAULT_BLUR_THRESHOLD,
|
|
DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD,
|
|
} from "../../js/constants.js";
|
|
import { createModerationStage } from "../../js/feedEngine/stages.js";
|
|
import { withMockedNostrTools, createModerationServiceHarness } from "../helpers/moderation-test-helpers.mjs";
|
|
|
|
const { userBlocks } = await import("../../js/userBlocks.js");
|
|
const { nostrClient } = await import("../../js/nostrClientFacade.js");
|
|
const { setActiveSigner, clearActiveSigner } = await import(
|
|
"../../js/nostr/client.js"
|
|
);
|
|
|
|
test("trusted mute lists from seeds hide authors for anonymous viewers", async (t) => {
|
|
withMockedNostrTools(t);
|
|
|
|
const seedHex = "1".repeat(64);
|
|
const mutedHex = "2".repeat(64);
|
|
|
|
const originalBlocked = new Set(userBlocks.blockedPubkeys);
|
|
const originalBlockEventId = userBlocks.blockEventId;
|
|
const originalBlockEventCreatedAt = userBlocks.blockEventCreatedAt;
|
|
const originalLastPublishedCreatedAt = userBlocks.lastPublishedCreatedAt;
|
|
const originalMuteEventId = userBlocks.muteEventId;
|
|
const originalMuteEventCreatedAt = userBlocks.muteEventCreatedAt;
|
|
const originalLoaded = userBlocks.loaded;
|
|
const originalLoadBlocks = userBlocks.loadBlocks;
|
|
const originalRelays = Array.isArray(nostrClient.relays)
|
|
? [...nostrClient.relays]
|
|
: nostrClient.relays;
|
|
const originalWriteRelays = Array.isArray(nostrClient.writeRelays)
|
|
? [...nostrClient.writeRelays]
|
|
: nostrClient.writeRelays;
|
|
const originalPool = nostrClient.pool;
|
|
|
|
t.after(() => {
|
|
userBlocks.blockedPubkeys = originalBlocked;
|
|
userBlocks.blockEventId = originalBlockEventId;
|
|
userBlocks.blockEventCreatedAt = originalBlockEventCreatedAt;
|
|
userBlocks.lastPublishedCreatedAt = originalLastPublishedCreatedAt;
|
|
userBlocks.muteEventId = originalMuteEventId;
|
|
userBlocks.muteEventCreatedAt = originalMuteEventCreatedAt;
|
|
userBlocks.loaded = originalLoaded;
|
|
userBlocks.loadBlocks = originalLoadBlocks;
|
|
nostrClient.relays = originalRelays;
|
|
nostrClient.writeRelays = originalWriteRelays;
|
|
nostrClient.pool = originalPool;
|
|
clearActiveSigner();
|
|
});
|
|
|
|
const publishedEvents = [];
|
|
nostrClient.relays = ["wss://seed-relay.example"];
|
|
nostrClient.writeRelays = nostrClient.relays;
|
|
nostrClient.pool = {
|
|
publish: (_targets, event) => {
|
|
publishedEvents.push(event);
|
|
return {
|
|
on(eventName, handler) {
|
|
if (eventName === "ok") {
|
|
setImmediate(handler);
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
};
|
|
},
|
|
list: async () => [],
|
|
};
|
|
|
|
userBlocks.loadBlocks = async () => {};
|
|
userBlocks.blockedPubkeys = new Set();
|
|
userBlocks.blockEventId = null;
|
|
userBlocks.blockEventCreatedAt = null;
|
|
userBlocks.lastPublishedCreatedAt = null;
|
|
userBlocks.muteEventId = null;
|
|
userBlocks.muteEventCreatedAt = null;
|
|
userBlocks.loaded = true;
|
|
|
|
const signedEvents = [];
|
|
setActiveSigner({
|
|
type: "private-key",
|
|
pubkey: seedHex,
|
|
nip04Encrypt: async (pubkey, plaintext) => {
|
|
assert.equal(pubkey, seedHex, "nip04 encrypt should target the seed pubkey");
|
|
return `nip04:${plaintext}`;
|
|
},
|
|
nip44Encrypt: async (pubkey, plaintext) => {
|
|
assert.equal(pubkey, seedHex, "nip44 encrypt should target the seed pubkey");
|
|
return `nip44:${plaintext}`;
|
|
},
|
|
signEvent: async (event) => {
|
|
const signed = {
|
|
...event,
|
|
id: `event-${signedEvents.length + 1}`,
|
|
created_at: event.created_at ?? Math.floor(Date.now() / 1000),
|
|
};
|
|
signedEvents.push(signed);
|
|
return signed;
|
|
},
|
|
});
|
|
|
|
await userBlocks.addBlock(mutedHex, seedHex);
|
|
|
|
const muteEvent = signedEvents.find(
|
|
(event) =>
|
|
event.kind === 10000 &&
|
|
Array.isArray(event.tags) &&
|
|
event.tags.some((tag) => Array.isArray(tag) && tag[0] === "p"),
|
|
);
|
|
assert(muteEvent, "block list updates should publish a kind 10000 mute list event");
|
|
assert(
|
|
muteEvent.tags.some((tag) => Array.isArray(tag) && tag[0] === "p" && tag[1] === mutedHex),
|
|
"mute list event should include p-tags for blocked pubkeys",
|
|
);
|
|
const publishedMuteEvents = publishedEvents.filter(
|
|
(event) =>
|
|
event?.kind === 10000 &&
|
|
Array.isArray(event.tags) &&
|
|
event.tags.some((tag) => Array.isArray(tag) && tag[0] === "p"),
|
|
);
|
|
assert(publishedMuteEvents.length >= 1, "mute list event should be published to relays");
|
|
|
|
const { service } = createModerationServiceHarness(t, { userBlocks });
|
|
service.refreshViewerFromClient = async () => {};
|
|
service.setActiveEventIds = async () => {};
|
|
service.subscribeToReports = async () => {};
|
|
service.teardownReportSubscription = () => {};
|
|
service.getAdminListSnapshot = () => ({
|
|
whitelist: new Set(),
|
|
whitelistHex: new Set(),
|
|
blacklist: new Set(),
|
|
blacklistHex: new Set(),
|
|
});
|
|
service.getAccessControlStatus = () => ({ whitelisted: false, blacklisted: false, hex: "" });
|
|
|
|
service.setTrustedSeeds([seedHex]);
|
|
service.ingestTrustedMuteEvent(muteEvent);
|
|
|
|
assert.equal(
|
|
service.isAuthorMutedByTrusted(mutedHex),
|
|
true,
|
|
"trusted mute ingestion should flag the author as muted",
|
|
);
|
|
|
|
const stage = createModerationStage({
|
|
service,
|
|
autoplayThreshold: DEFAULT_AUTOPLAY_BLOCK_THRESHOLD,
|
|
blurThreshold: DEFAULT_BLUR_THRESHOLD,
|
|
trustedMuteHideThreshold: 1, // Override to ensure hiding triggers with a single mute
|
|
reportType: "nudity",
|
|
});
|
|
|
|
const items = [{ video: { id: "video-muted", pubkey: mutedHex }, metadata: {} }];
|
|
const why = [];
|
|
const context = {
|
|
runtime: { isAuthorBlocked: () => false },
|
|
addWhy(detail) {
|
|
why.push(detail);
|
|
return detail;
|
|
},
|
|
log() {},
|
|
};
|
|
|
|
const result = await stage(items, context);
|
|
|
|
assert.equal(result.length, 0, "trusted mute threshold should hide the muted author");
|
|
const reasons = why.map((entry) => entry.reason);
|
|
assert(
|
|
reasons.includes("trusted-mute"),
|
|
"moderation stage should record trusted mute metadata",
|
|
);
|
|
assert(
|
|
reasons.includes("trusted-mute-hide"),
|
|
"moderation stage should record the trusted mute hide reason",
|
|
);
|
|
});
|