this repo has no description
1import heading from "./pds-heading.txt" with { type: "text" };
2import {
3 didDocSchema,
4 getRecordSchema,
5 listRecordsSchema,
6 listReposSchema,
7 profileSelfSchema,
8 statusphereSchema,
9 tealFmStatusSchema,
10} from "../schemas.ts";
11import { stagger } from "../utils.ts";
12
13const PDS = Deno.env.get("PDS") ?? "http://pi:8000";
14const PLC_DIRECTORY = Deno.env.get("PLC_DIRECTORY") ?? "https://plc.directory";
15
16async function renderUser(did: string): Promise<string> {
17 const handle = fetch(
18 did.startsWith("did:plc")
19 ? `${PLC_DIRECTORY}/${did}`
20 : `https://${did.replace("did:web:", "")}/.well-known/did.json`,
21 )
22 .then((res) => res.json())
23 .then((res) => didDocSchema.safeParse(res).data)
24 .then((doc) =>
25 doc
26 ? (doc.alsoKnownAs
27 .filter((x: string) => x.startsWith("at://"))[0]
28 ?.replace("at://", "") ?? "handle.invalid")
29 : "handle.invalid",
30 )
31 .catch(() => "handle.invalid");
32
33 const displayName: Promise<string | undefined> = fetch(
34 `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=app.bsky.actor.profile&rkey=self`,
35 )
36 .then((res) => res.json())
37 .then((data) => getRecordSchema(profileSelfSchema).safeParse(data).data)
38 .then((data) => (data ? data.value.displayName : undefined))
39 .catch(() => undefined);
40
41 const statusphere: Promise<string | undefined> = fetch(
42 `${PDS}/xrpc/com.atproto.repo.listRecords?repo=${did}&collection=xyz.statusphere.status`,
43 )
44 .then((res) => res.json())
45 .then((data) => listRecordsSchema(statusphereSchema).safeParse(data).data)
46 .then((data) =>
47 data && data.records.length > 0
48 ? data.records[0].value.status
49 : undefined,
50 )
51 .catch(() => undefined);
52
53 const nowPlaying = fetch(
54 `${PDS}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=fm.teal.alpha.actor.status&rkey=self`,
55 )
56 .then((res) => res.json())
57 .then((data) => getRecordSchema(tealFmStatusSchema).safeParse(data).data)
58 .then((data) =>
59 data
60 ? data.value.item.trackName +
61 ((data.value.item.artists.length > 0
62 ? ` - ${data.value.item.artists[0].artistName}`
63 : "") +
64 (data.value.item.artists.length > 1
65 ? `${data.value.item.artists.length - 1} more artist${data.value.item.artists.length > 1 ? "s" : ""}`
66 : ""))
67 : undefined,
68 )
69 .catch(() => undefined);
70
71 return (
72 "- " +
73 [
74 (await statusphere)
75 ? `${await displayName} (${await statusphere})`
76 : await displayName,
77
78 `@${await handle}`,
79 `at://${did}`,
80 await nowPlaying,
81 ]
82 .filter((x) => x)
83 .join("\n ")
84 );
85}
86
87export default function (): Response {
88 // get upstream pds status
89 const status = fetch(`${PDS}/xrpc/_health`)
90 .then(async (res) => `${res.status} ${res.statusText} ${await res.text()}`)
91 .catch((e) =>
92 e instanceof Error
93 ? `ERR: ${e.name}\n\n${e.message}\n${e.cause}\n${e.stack}`
94 : `Err\n\n${e}`,
95 );
96
97 const users = fetch(`${PDS}/xrpc/com.atproto.sync.listRepos`)
98 .then((res) => res.json())
99 .catch(() => undefined)
100 .then((data) => listReposSchema.safeParse(data).data)
101 .then((data) =>
102 data ? data.repos.filter((x) => x.active).map((x) => x.did) : undefined,
103 )
104 .then(async (users) =>
105 users
106 ? await Promise.all(users.map((did) => renderUser(did))).then((x) =>
107 x.join("\n\n"),
108 )
109 : undefined,
110 );
111
112 const body = new ReadableStream({
113 async start(controller: ReadableStreamDefaultController<string>) {
114 controller.enqueue(heading);
115 controller.enqueue(await status);
116 controller.enqueue("\n\n");
117 controller.enqueue(await users);
118 controller.close();
119 },
120 });
121
122 return new Response(
123 body.pipeThrough(stagger()).pipeThrough(new TextEncoderStream()),
124 {
125 headers: {
126 "Content-Type": "text/text; charset=utf-8",
127 "Access-Control-Allow-Origin": "*",
128 Link: "</styles.css>; rel=stylesheet",
129 },
130 },
131 );
132}