Retro Bulletin Board Systems on atproto. Web app and TUI. lazy mirror of alyraffauf/atbbs atbbs.xyz
forums python tui atproto bbs
3
fork

Configure Feed

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

move from ufos -> lightrail for bbs discovery

+50 -33
+1 -1
README.md
··· 89 89 90 90 - [Slingshot](https://slingshot.microcosm.blue/) — cached record and identity fetching 91 91 - [Constellation](https://constellation.microcosm.blue/) — backlink index for discovering threads and replies 92 - - [UFOs](https://ufos.microcosm.blue/) — BBS discovery feed 92 + - [Lightrail](https://lightrail.microcosm.blue/) — BBS discovery via collection listing 93 93 94 94 ## Configuration 95 95
+18 -13
tui/screens/home.py
··· 11 11 from core import lexicon 12 12 from core.models import BBSNotFoundError, NetworkError, NoBBSError 13 13 from core.resolver import resolve_bbs 14 - from core.slingshot import resolve_identities_batch 14 + from core.slingshot import get_record, resolve_identities_batch 15 15 from tui.screens.site import SiteScreen 16 16 17 17 ··· 103 103 client = self.app.http_client 104 104 try: 105 105 resp = await client.get( 106 - "https://ufos-api.microcosm.blue/records", 106 + "https://lightrail.microcosm.blue/xrpc/com.atproto.sync.listReposByCollection", 107 107 params={"collection": lexicon.SITE, "limit": 50}, 108 108 ) 109 109 if resp.status_code != 200: 110 110 return 111 - raw = resp.json() 112 - if len(raw) > 5: 113 - raw = random.sample(raw, 5) 111 + repos = resp.json().get("repos", []) 112 + if len(repos) > 5: 113 + repos = random.sample(repos, 5) 114 114 115 - dids = [record["did"] for record in raw] 115 + dids = [repo["did"] for repo in repos] 116 116 authors = await resolve_identities_batch(client, dids) 117 117 118 118 items = [] 119 - for record in raw: 120 - did = record["did"] 121 - if did in authors: 122 - name = record["record"].get("name", "") 123 - desc = record["record"].get("description", "") 124 - handle = authors[did].handle 125 - items.append((handle, name, desc)) 119 + for repo in repos: 120 + did = repo["did"] 121 + if did not in authors: 122 + continue 123 + try: 124 + site_record = await get_record(client, did, lexicon.SITE, "self") 125 + name = site_record.value.get("name", "") 126 + desc = site_record.value.get("description", "") 127 + except Exception: 128 + continue 129 + handle = authors[did].handle 130 + items.append((handle, name, desc)) 126 131 127 132 if not items: 128 133 return
+31 -19
web/src/hooks/useDiscovery.ts
··· 1 - /** Fetch discovered BBSes from the UFOs API, cached in memory. */ 1 + /** Fetch discovered BBSes from the Lightrail API, cached in memory. */ 2 2 3 3 import { useEffect, useState } from "react"; 4 4 import { TTLCache } from "../lib/cache"; 5 - import { resolveIdentitiesBatch } from "../lib/atproto"; 5 + import { getRecord, resolveIdentitiesBatch } from "../lib/atproto"; 6 6 import { SITE } from "../lib/lexicon"; 7 + import { is } from "@atcute/lexicons/validations"; 8 + import { mainSchema as siteSchema } from "../lexicons/types/xyz/atbbs/site"; 9 + import type { XyzAtbbsSite } from "../lexicons"; 7 10 8 - interface UFORecord { 11 + interface LightrailRepo { 9 12 did: string; 10 - record: { name?: string; description?: string }; 11 13 } 12 14 13 15 export interface DiscoveredBBS { ··· 30 32 } 31 33 (async () => { 32 34 try { 33 - const resp = await fetch( 34 - `https://ufos-api.microcosm.blue/records?collection=${SITE}&limit=50`, 35 + const response = await fetch( 36 + `https://lightrail.microcosm.blue/xrpc/com.atproto.sync.listReposByCollection?collection=${SITE}&limit=50`, 35 37 ); 36 - let records = (await resp.json()) as UFORecord[]; 37 - if (!records.length) return; 38 - records = records.sort(() => Math.random() - 0.5); 39 - const authors = await resolveIdentitiesBatch( 40 - records.map((record) => record.did), 38 + const data = (await response.json()) as { repos: LightrailRepo[] }; 39 + if (!data.repos.length) return; 40 + 41 + const shuffled = data.repos.sort(() => Math.random() - 0.5); 42 + const identities = await resolveIdentitiesBatch( 43 + shuffled.map((repo) => repo.did), 41 44 ); 45 + 42 46 const items: DiscoveredBBS[] = []; 43 - for (const record of records) { 44 - if (!(record.did in authors)) continue; 45 - items.push({ 46 - did: record.did, 47 - handle: authors[record.did].handle, 48 - name: record.record.name || authors[record.did].handle, 49 - description: record.record.description || "", 50 - }); 47 + for (const repo of shuffled) { 48 + if (!(repo.did in identities)) continue; 49 + try { 50 + const siteRecord = await getRecord(repo.did, SITE, "self"); 51 + if (!is(siteSchema, siteRecord.value)) continue; 52 + const siteValue = siteRecord.value as unknown as XyzAtbbsSite.Main; 53 + items.push({ 54 + did: repo.did, 55 + handle: identities[repo.did].handle, 56 + name: siteValue.name || identities[repo.did].handle, 57 + description: siteValue.description || "", 58 + }); 59 + } catch { 60 + continue; 61 + } 51 62 } 63 + 52 64 discoveryCache.set("all", items); 53 65 setDiscovered(items); 54 66 } catch {}