Merge pull request #1185 from PR0M3TH3AN/user-blocks-priority-1385686224757387826

Prioritize tagged user block lists over generic mute lists
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-11 16:09:26 -05:00
commit 2a9bf7f85b
2 files changed
+87 -3

No files matched your search

+28 -3
View File
@@ -386,6 +386,21 @@ function isUserBlockListEvent(event) {
return false;
}
function isTaggedBlockListEvent(event) {
if (!event || !Array.isArray(event.tags)) {
return false;
}
return event.tags.some(
(tag) =>
Array.isArray(tag) &&
tag.length >= 2 &&
typeof tag[0] === "string" &&
tag[0].trim().toLowerCase() === "d" &&
typeof tag[1] === "string" &&
tag[1].trim() === BLOCK_LIST_IDENTIFIER,
);
}
const BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
const BECH32_CHARSET_MAP = (() => {
const map = new Map();
@@ -1164,14 +1179,24 @@ class UserBlockListManager {
}
// Separate by kind to merge distinct lists (Block vs Mute)
const muteEvents = validEvents
.filter((e) => e.kind === 10000)
const muteEvents = validEvents.filter((e) => e.kind === 10000);
// Prioritize tagged mute lists (Bitvid) over plain ones (others)
const taggedMutes = muteEvents
.filter((e) => isTaggedBlockListEvent(e))
.sort((a, b) => (b?.created_at || 0) - (a?.created_at || 0));
const plainMutes = muteEvents
.filter((e) => !isTaggedBlockListEvent(e))
.sort((a, b) => (b?.created_at || 0) - (a?.created_at || 0));
const newestMute =
taggedMutes.length > 0 ? taggedMutes[0] : plainMutes[0] || null;
const blockEvents = validEvents
.filter((e) => e.kind === 30002)
.sort((a, b) => (b?.created_at || 0) - (a?.created_at || 0));
const newestMute = muteEvents[0] || null;
const newestBlock = blockEvents[0] || null;
const newestMuteTime = Number.isFinite(newestMute?.created_at)
+59
View File
@@ -1179,3 +1179,62 @@ await (async () => {
nostrClient.pool = originalPool;
}
})();
await (async () => {
// Test: Prioritize 'd=user-blocks' tagged lists over newer plain lists
const actor = "a".repeat(64);
const taggedBlocked = "b".repeat(64);
const plainBlocked = "c".repeat(64);
const originalRelays = nostrClient.relays;
const originalPool = nostrClient.pool;
const originalNostr = window.nostr;
nostrClient.relays = ["wss://repro.example"];
window.nostr = {
nip04: {
decrypt: async (_pubkey, ciphertext) => {
if (ciphertext === "encrypted-tagged") {
return JSON.stringify({ blockedPubkeys: [taggedBlocked] });
}
return "{}";
}
}
};
nostrClient.pool = {
list: async () => [
{
id: "event-tagged",
kind: 10000,
created_at: 1000,
pubkey: actor,
content: "encrypted-tagged",
tags: [["d", "user-blocks"]],
},
{
id: "event-plain",
kind: 10000,
created_at: 2000, // Newer!
pubkey: actor,
content: "",
tags: [["p", plainBlocked]],
}
]
};
userBlocks.reset();
try {
await userBlocks.loadBlocks(actor);
const blocked = userBlocks.getBlockedPubkeys();
assert.ok(blocked.includes(taggedBlocked), "Should include pubkey from tagged list");
assert.ok(!blocked.includes(plainBlocked), "Should NOT include pubkey from plain list (fallback ignored)");
} finally {
nostrClient.relays = originalRelays;
nostrClient.pool = originalPool;
window.nostr = originalNostr;
}
})();