Retro Bulletin Board Systems on atproto. Web app and TUI.
lazy mirror of alyraffauf/atbbs
atbbs.xyz
forums
python
tui
atproto
bbs
1/** Delete a user's entire BBS: boards, news posts, bans, hides, then the site record. */
2
3import type { Client } from "@atcute/client";
4import { getRecord, getBacklinks, listRecords } from "./atproto";
5import { BAN, BOARD, HIDE, POST, SITE } from "./lexicon";
6import { makeAtUri, parseAtUri } from "./util";
7import { deleteRecord } from "./writes";
8
9export async function deleteBBS(agent: Client, did: string, pdsUrl: string) {
10 const failed: string[] = [];
11
12 const existing = await getRecord(did, SITE, "self");
13 const siteValue = existing.value as Record<string, unknown>;
14 const boardUris: string[] = (
15 Array.isArray(siteValue.boards) ? siteValue.boards : []
16 ) as string[];
17
18 // Delete boards
19 for (const uri of boardUris) {
20 try {
21 const { rkey } = parseAtUri(uri);
22 await deleteRecord(agent, BOARD, rkey);
23 } catch {
24 failed.push(`board/${uri}`);
25 }
26 }
27
28 // Delete sysop's news posts (posts scoped to the site)
29 const siteUri = makeAtUri(did, SITE, "self");
30 try {
31 const backlinks = await getBacklinks(siteUri, `${POST}:scope`, 100);
32 for (const ref of backlinks.records) {
33 if (ref.did === did) {
34 try {
35 await deleteRecord(agent, POST, ref.rkey);
36 } catch {
37 failed.push(`post/${ref.rkey}`);
38 }
39 }
40 }
41 } catch {
42 failed.push("news lookup");
43 }
44
45 for (const collection of [BAN, HIDE]) {
46 const records = await listRecords(pdsUrl, did, collection);
47 for (const record of records) {
48 try {
49 await deleteRecord(agent, collection, parseAtUri(record.uri).rkey);
50 } catch {
51 failed.push(`${collection}/${parseAtUri(record.uri).rkey}`);
52 }
53 }
54 }
55
56 if (failed.length) {
57 throw new Error(
58 `Could not delete: ${failed.join(", ")}. Site record was not deleted.`,
59 );
60 }
61
62 await deleteRecord(agent, SITE, "self");
63}