Daily Bluesky bot for AT Mot. Invites players and congratulates yesterday's solvers.
1/**
2 * Identity resolution (handle/DID -> PDS) and per-PDS record reads.
3 * Mirrors the app's read path; failures resolve to null and drop the entry.
4 */
5import {
6 CompositeDidDocumentResolver,
7 LocalActorResolver,
8 PlcDidDocumentResolver,
9 WebDidDocumentResolver,
10 XrpcHandleResolver,
11 type ResolvedActor,
12} from '@atcute/identity-resolver';
13import { Client, simpleFetchHandler } from '@atcute/client';
14import { APPVIEW_URL } from './config.js';
15
16const actorResolver = new LocalActorResolver({
17 handleResolver: new XrpcHandleResolver({ serviceUrl: APPVIEW_URL }),
18 didDocumentResolver: new CompositeDidDocumentResolver({
19 methods: { plc: new PlcDidDocumentResolver(), web: new WebDidDocumentResolver() },
20 }),
21});
22
23/** Resolve a handle or DID to { did, handle, pds }. May reject. */
24export function resolveActor(identifier: string): Promise<ResolvedActor> {
25 return actorResolver.resolve(identifier as Parameters<typeof actorResolver.resolve>[0]);
26}
27
28/** Unauthenticated read client pointed at a specific PDS. */
29export function pdsClient(pds: string): Client {
30 return new Client({ handler: simpleFetchHandler({ service: pds }) });
31}
32
33/** Public read of a record by at:// URI; null on any failure. */
34export async function getRecordByUri<T = unknown>(uri: string): Promise<T | null> {
35 const m = /^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/.exec(uri);
36 if (!m) return null;
37 try {
38 const actor = await resolveActor(m[1]!);
39 const rpc = pdsClient(actor.pds);
40 const res = await rpc.get('com.atproto.repo.getRecord', {
41 params: {
42 repo: m[1]! as `did:${string}:${string}`,
43 collection: m[2]! as `${string}.${string}.${string}`,
44 rkey: m[3]!,
45 },
46 });
47 if (!res.ok) return null;
48 return res.data.value as unknown as T;
49 } catch {
50 return null;
51 }
52}