Sifa professional network API (Fastify, AT Protocol, Jetstream)
sifa.id/
1import { isValidHandle } from '@atproto/syntax';
2import { Agent } from '@atproto/api';
3import { logger } from '../logger.js';
4
5export interface ResolvedProfile {
6 did: string;
7 handle: string;
8 displayName: string | undefined;
9 avatar: string | undefined;
10 about: string | undefined;
11}
12
13const SIMPLE_HANDLE_RE = /^[a-zA-Z0-9-]+$/;
14const publicAgent = new Agent('https://public.api.bsky.app');
15
16export async function resolveHandleFromNetwork(query: string): Promise<ResolvedProfile | null> {
17 const candidates: string[] = [];
18
19 if (isValidHandle(query)) {
20 candidates.push(query);
21 }
22
23 // If no dots and looks like a simple username, also try .bsky.social
24 if (SIMPLE_HANDLE_RE.test(query) && !query.includes('.')) {
25 const bskySocial = `${query}.bsky.social`;
26 if (isValidHandle(bskySocial) && !candidates.includes(bskySocial)) {
27 candidates.push(bskySocial);
28 }
29 }
30
31 for (const handle of candidates) {
32 try {
33 const response = await publicAgent.getProfile(
34 { actor: handle },
35 { signal: AbortSignal.timeout(3000) },
36 );
37 return {
38 did: response.data.did,
39 handle: response.data.handle,
40 displayName: response.data.displayName || undefined,
41 avatar: response.data.avatar || undefined,
42 about: response.data.description || undefined,
43 };
44 } catch (err) {
45 logger.debug({ handle, err }, 'Handle resolution failed');
46 }
47 }
48
49 return null;
50}