import heading from "./pds-heading.txt" with { type: "text" }; import { didDocSchema, getRecordSchema, listRecordsSchema, listReposSchema, profileSelfSchema, statusphereSchema, tealFmStatusSchema, } from "../schemas.ts"; import { stagger } from "../utils.ts"; const PDS = Deno.env.get("PDS") ?? "http://pi:8000"; const PLC_DIRECTORY = Deno.env.get("PLC_DIRECTORY") ?? "https://plc.directory"; async function renderUser(did: string): Promise { const handle = fetch( did.startsWith("did:plc") ? `${PLC_DIRECTORY}/${did}` : `https://${did.replace("did:web:", "")}/.well-known/did.json`, ) .then((res) => res.json()) .then((res) => didDocSchema.safeParse(res).data) .then((doc) => doc ? (doc.alsoKnownAs .filter((x: string) => x.startsWith("at://"))[0] ?.replace("at://", "") ?? "handle.invalid") : "handle.invalid", ) .catch(() => "handle.invalid"); const displayName: Promise = fetch( `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=app.bsky.actor.profile&rkey=self`, ) .then((res) => res.json()) .then((data) => getRecordSchema(profileSelfSchema).safeParse(data).data) .then((data) => (data ? data.value.displayName : undefined)) .catch(() => undefined); const statusphere: Promise = fetch( `${PDS}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=xyz.statusphere.status`, ) .then((res) => res.json()) .then((data) => listRecordsSchema(statusphereSchema).safeParse(data).data) .then((data) => data && data.records.length > 0 ? data.records[0].value.status : undefined, ) .catch(() => undefined); const nowPlaying = fetch( `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=fm.teal.alpha.actor.status&rkey=self`, ) .then((res) => res.json()) .then((data) => getRecordSchema(tealFmStatusSchema).safeParse(data).data) .then((data) => data ? data.value.item.trackName + ((data.value.item.artists.length > 0 ? ` - ${data.value.item.artists[0].artistName}` : "") + (data.value.item.artists.length > 1 ? `${data.value.item.artists.length - 1} more artist${data.value.item.artists.length > 1 ? "s" : ""}` : "")) : undefined, ) .catch(() => undefined); return ( "- " + [ (await statusphere) ? `${await displayName} (${await statusphere})` : await displayName, `@${await handle}`, `at://${did}`, await nowPlaying, ] .filter((x) => x) .join("\n ") ); } export default function (): Response { // get upstream pds status const status = fetch(`${PDS}/xrpc/_health`) .then(async (res) => `${res.status} ${res.statusText} ${await res.text()}`) .catch((e) => e instanceof Error ? `ERR: ${e.name}\n\n${e.message}\n${e.cause}\n${e.stack}` : `Err\n\n${e}`, ); const users = fetch(`${PDS}/xrpc/com.atproto.sync.listRepos`) .then((res) => res.json()) .catch(() => undefined) .then((data) => listReposSchema.safeParse(data).data) .then((data) => data ? data.repos.filter((x) => x.active).map((x) => x.did) : undefined, ) .then(async (users) => users ? await Promise.all(users.map((did) => renderUser(did))).then((x) => x.join("\n\n"), ) : undefined, ); const body = new ReadableStream({ async start(controller: ReadableStreamDefaultController) { controller.enqueue(heading); controller.enqueue(await status); controller.enqueue("\n\n"); controller.enqueue(await users); controller.close(); }, }); return new Response( body.pipeThrough(stagger()).pipeThrough(new TextEncoderStream()), { headers: { "Content-Type": "text/text; charset=utf-8", "Access-Control-Allow-Origin": "*", Link: "; rel=stylesheet", }, }, ); }