Mass Block [bsky] Reposts [and more]
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 98 lines 3.8 kB view raw
1import { test } from "node:test"; 2import assert from "node:assert/strict"; 3import { readFile, unlink } from "node:fs/promises"; 4import { tmpdir } from "node:os"; 5import { join } from "node:path"; 6import { isProfileUrl, resolveMiniDoc, filterCandidates, writeOutput } from "./index.js"; 7 8test("isProfileUrl: profile URLs", () => { 9 assert.equal(isProfileUrl("https://bsky.app/profile/alice.bsky.social"), true); 10 assert.equal(isProfileUrl("https://witchsky.app/profile/jim.bsky.social"), true); 11 assert.equal(isProfileUrl("https://bsky.app/profile/did:plc:abc123"), true); 12}); 13 14test("isProfileUrl: post URLs are rejected", () => { 15 assert.equal(isProfileUrl("https://bsky.app/profile/alice.bsky.social/post/abc"), false); 16 assert.equal(isProfileUrl("at://did:plc:abc/app.bsky.feed.post/rkey"), false); 17}); 18 19test("isProfileUrl: unrelated URLs are rejected", () => { 20 assert.equal(isProfileUrl("https://bsky.app/"), false); 21 assert.equal(isProfileUrl("https://bsky.app/search"), false); 22}); 23 24test("resolveMiniDoc returns did, handle, pds for a known handle", async () => { 25 // bad-example.com is slingshot's own example account -- stable 26 const result = await resolveMiniDoc("bad-example.com"); 27 assert.equal(typeof result.did, "string"); 28 assert.ok(result.did.startsWith("did:")); 29 assert.equal(typeof result.handle, "string"); 30 assert.equal(typeof result.pds, "string"); 31 assert.ok(result.pds.startsWith("https://")); 32}); 33 34test("resolveMiniDoc accepts a DID as identifier", async () => { 35 // did:plc:hdhoaan3xa3jiuq4fg4mefid is bad-example.com per slingshot docs 36 const result = await resolveMiniDoc("did:plc:hdhoaan3xa3jiuq4fg4mefid"); 37 assert.equal(typeof result.did, "string"); 38 assert.equal(typeof result.pds, "string"); 39 assert.equal(typeof result.handle, "string"); 40}); 41 42const BASE_FILTER_ARGS = { 43 results: { reposts: [], likes: [], replies: [], followers: [], following: [], quotes: [], quotePosters: [] }, 44 did: "did:plc:me", 45 follows: new Set(), 46 followers: new Set(), 47 existingBlocks: new Set(), 48 blockFollowing: false, 49}; 50 51test("filterCandidates: blocks target by default", () => { 52 const result = filterCandidates({ 53 ...BASE_FILTER_ARGS, 54 results: { ...BASE_FILTER_ARGS.results, reposts: ["did:plc:reposter"] }, 55 targetDid: "did:plc:target", 56 blockTarget: true, 57 }); 58 assert.ok(result.toBlock.includes("did:plc:target")); 59}); 60 61test("filterCandidates: skips target when blockTarget is false", () => { 62 const result = filterCandidates({ 63 ...BASE_FILTER_ARGS, 64 results: { ...BASE_FILTER_ARGS.results, reposts: ["did:plc:reposter"] }, 65 targetDid: "did:plc:target", 66 blockTarget: false, 67 }); 68 assert.ok(!result.toBlock.includes("did:plc:target")); 69}); 70 71test("filterCandidates: excludes quote posters by default", () => { 72 const result = filterCandidates({ 73 ...BASE_FILTER_ARGS, 74 results: { ...BASE_FILTER_ARGS.results, quotePosters: ["did:plc:quoter"] }, 75 targetDid: null, 76 blockQuotes: false, 77 }); 78 assert.ok(!result.toBlock.includes("did:plc:quoter")); 79}); 80 81test("filterCandidates: includes quote posters when blockQuotes is true", () => { 82 const result = filterCandidates({ 83 ...BASE_FILTER_ARGS, 84 results: { ...BASE_FILTER_ARGS.results, quotes: ["did:plc:quoter"], quotePosters: ["did:plc:quoter"] }, 85 targetDid: null, 86 blockQuotes: true, 87 }); 88 assert.ok(result.toBlock.includes("did:plc:quoter")); 89}); 90 91test("writeOutput: writes one DID per line", async () => { 92 const path = join(tmpdir(), `blocker-test-${Date.now()}.txt`); 93 const dids = ["did:plc:aaa", "did:plc:bbb", "did:plc:ccc"]; 94 await writeOutput(path, dids); 95 const content = await readFile(path, "utf8"); 96 assert.equal(content, "did:plc:aaa\ndid:plc:bbb\ndid:plc:ccc\n"); 97 await unlink(path); 98});