Retro Bulletin Board Systems on atproto. Web app and TUI.
lazy mirror of alyraffauf/atbbs
atbbs.xyz
forums
python
tui
atproto
bbs
1/** Fetch a random list of BBSes from the Lightrail API, with avatars. */
2
3import { getAvatars, getRecord, resolveIdentitiesBatch } from "./atproto";
4import { SITE } from "./lexicon";
5import { SERVICES } from "./shared";
6import { isSiteRecord } from "./recordGuards";
7
8export interface DiscoveredBBS {
9 did: string;
10 handle: string;
11 name: string;
12 description: string;
13 avatar?: string;
14}
15
16interface LightrailRepo {
17 did: string;
18}
19
20export async function fetchDiscovery(): Promise<DiscoveredBBS[]> {
21 let repos: LightrailRepo[] = [];
22 try {
23 const response = await fetch(
24 `${SERVICES.lightrail}/com.atproto.sync.listReposByCollection?collection=${SITE}&limit=50`,
25 );
26 const data = (await response.json()) as { repos: LightrailRepo[] };
27 repos = data.repos;
28 } catch {
29 return [];
30 }
31 if (!repos.length) return [];
32
33 const shuffled = repos.sort(() => Math.random() - 0.5);
34 const identities = await resolveIdentitiesBatch(
35 shuffled.map((repo) => repo.did),
36 );
37
38 const items: DiscoveredBBS[] = [];
39 for (const repo of shuffled) {
40 if (!(repo.did in identities)) continue;
41 try {
42 const siteRecord = await getRecord(repo.did, SITE, "self");
43 if (!isSiteRecord(siteRecord)) continue;
44 items.push({
45 did: repo.did,
46 handle: identities[repo.did].handle,
47 name: siteRecord.value.name || identities[repo.did].handle,
48 description: siteRecord.value.description || "",
49 });
50 } catch {
51 continue;
52 }
53 }
54
55 const avatars = await getAvatars(items.map((item) => item.did));
56 for (const item of items) {
57 item.avatar = avatars[item.did];
58 }
59
60 return items;
61}