diff --git a/README.md b/README.md index a55d937..d51df23 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ The web app and TUI query existing network infrastructure: - [Slingshot](https://slingshot.microcosm.blue/) — cached record and identity fetching - [Constellation](https://constellation.microcosm.blue/) — backlink index for discovering threads and replies -- [UFOs](https://ufos.microcosm.blue/) — BBS discovery feed +- [Lightrail](https://lightrail.microcosm.blue/) — BBS discovery via collection listing ## Configuration diff --git a/tui/screens/home.py b/tui/screens/home.py index 4dacd0c..f6c27eb 100644 --- a/tui/screens/home.py +++ b/tui/screens/home.py @@ -11,7 +11,7 @@ from tui.widgets.handle_input import HandleInput from core import lexicon from core.models import BBSNotFoundError, NetworkError, NoBBSError from core.resolver import resolve_bbs -from core.slingshot import resolve_identities_batch +from core.slingshot import get_record, resolve_identities_batch from tui.screens.site import SiteScreen @@ -103,26 +103,31 @@ class HomeScreen(Screen): client = self.app.http_client try: resp = await client.get( - "https://ufos-api.microcosm.blue/records", + "https://lightrail.microcosm.blue/xrpc/com.atproto.sync.listReposByCollection", params={"collection": lexicon.SITE, "limit": 50}, ) if resp.status_code != 200: return - raw = resp.json() - if len(raw) > 5: - raw = random.sample(raw, 5) + repos = resp.json().get("repos", []) + if len(repos) > 5: + repos = random.sample(repos, 5) - dids = [record["did"] for record in raw] + dids = [repo["did"] for repo in repos] authors = await resolve_identities_batch(client, dids) items = [] - for record in raw: - did = record["did"] - if did in authors: - name = record["record"].get("name", "") - desc = record["record"].get("description", "") - handle = authors[did].handle - items.append((handle, name, desc)) + for repo in repos: + did = repo["did"] + if did not in authors: + continue + try: + site_record = await get_record(client, did, lexicon.SITE, "self") + name = site_record.value.get("name", "") + desc = site_record.value.get("description", "") + except Exception: + continue + handle = authors[did].handle + items.append((handle, name, desc)) if not items: return diff --git a/web/src/hooks/useDiscovery.ts b/web/src/hooks/useDiscovery.ts index e2c11e8..f3c73b3 100644 --- a/web/src/hooks/useDiscovery.ts +++ b/web/src/hooks/useDiscovery.ts @@ -1,13 +1,15 @@ -/** Fetch discovered BBSes from the UFOs API, cached in memory. */ +/** Fetch discovered BBSes from the Lightrail API, cached in memory. */ import { useEffect, useState } from "react"; import { TTLCache } from "../lib/cache"; -import { resolveIdentitiesBatch } from "../lib/atproto"; +import { getRecord, resolveIdentitiesBatch } from "../lib/atproto"; import { SITE } from "../lib/lexicon"; +import { is } from "@atcute/lexicons/validations"; +import { mainSchema as siteSchema } from "../lexicons/types/xyz/atbbs/site"; +import type { XyzAtbbsSite } from "../lexicons"; -interface UFORecord { +interface LightrailRepo { did: string; - record: { name?: string; description?: string }; } export interface DiscoveredBBS { @@ -30,25 +32,35 @@ export function useDiscovery(): DiscoveredBBS[] { } (async () => { try { - const resp = await fetch( - `https://ufos-api.microcosm.blue/records?collection=${SITE}&limit=50`, + const response = await fetch( + `https://lightrail.microcosm.blue/xrpc/com.atproto.sync.listReposByCollection?collection=${SITE}&limit=50`, ); - let records = (await resp.json()) as UFORecord[]; - if (!records.length) return; - records = records.sort(() => Math.random() - 0.5); - const authors = await resolveIdentitiesBatch( - records.map((record) => record.did), + const data = (await response.json()) as { repos: LightrailRepo[] }; + if (!data.repos.length) return; + + const shuffled = data.repos.sort(() => Math.random() - 0.5); + const identities = await resolveIdentitiesBatch( + shuffled.map((repo) => repo.did), ); + const items: DiscoveredBBS[] = []; - for (const record of records) { - if (!(record.did in authors)) continue; - items.push({ - did: record.did, - handle: authors[record.did].handle, - name: record.record.name || authors[record.did].handle, - description: record.record.description || "", - }); + for (const repo of shuffled) { + if (!(repo.did in identities)) continue; + try { + const siteRecord = await getRecord(repo.did, SITE, "self"); + if (!is(siteSchema, siteRecord.value)) continue; + const siteValue = siteRecord.value as unknown as XyzAtbbsSite.Main; + items.push({ + did: repo.did, + handle: identities[repo.did].handle, + name: siteValue.name || identities[repo.did].handle, + description: siteValue.description || "", + }); + } catch { + continue; + } } + discoveryCache.set("all", items); setDiscovered(items); } catch {}