import { Client, simpleFetchHandler } from '@atcute/client'; export const pub = new Client({ handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }) }); const followsCache = new Map(); export async function getAllFollows(actor) { if (followsCache.has(actor)) return followsCache.get(actor); const results = []; let cursor; do { const { data } = await pub.get('app.bsky.graph.getFollows', { params: { actor, limit: 100, ...(cursor ? { cursor } : {}) }, }); results.push(...data.follows); cursor = data.cursor; } while (cursor); followsCache.set(actor, results); return results; } export async function enrichWithFollowerCounts(users) { const { data } = await pub.get('app.bsky.actor.getProfiles', { params: { actors: users.map(u => u.did) }, }); if (!data.profiles) return; const byDid = new Map(data.profiles.map(p => [p.did, p])); users.forEach(u => { const p = byDid.get(u.did); if (p) u.followersCount = p.followersCount ?? 0; }); } export async function getSuggestedFollows(actor) { const { data } = await pub.get('app.bsky.graph.getSuggestedFollowsByActor', { params: { actor }, }); return { suggestions: data?.suggestions ?? [], isFallback: !!data?.isFallback }; } export async function getProfile(actor) { const { data } = await pub.get('app.bsky.actor.getProfile', { params: { actor } }); return data; } export async function enrichWithTopPosts(users) { await Promise.all(users.map(async u => { try { const { data } = await pub.get('app.bsky.feed.getAuthorFeed', { params: { actor: u.did, limit: 20, filter: 'posts_no_replies' }, }); const posts = (data?.feed || []) .map(item => item.post) .filter(p => p && !p.record?.reply); u.lastPostAt = posts[0]?.indexedAt ?? posts[0]?.record?.createdAt ?? null; posts.sort((a, b) => (b.likeCount || 0) - (a.likeCount || 0)); u.topPosts = posts.slice(0, 2); } catch { u.topPosts = []; u.lastPostAt = null; } })); } export async function followUser(rpc, myDid, subjectDid) { const res = await rpc.post('com.atproto.repo.createRecord', { input: { repo: myDid, collection: 'app.bsky.graph.follow', record: { $type: 'app.bsky.graph.follow', subject: subjectDid, createdAt: new Date().toISOString() }, }, }); if (!res.ok) throw new Error(`Follow failed: ${res.data?.message || res.data?.error}`); return res; } export async function unfollowUser(rpc, myDid, rkey) { const res = await rpc.post('com.atproto.repo.deleteRecord', { input: { repo: myDid, collection: 'app.bsky.graph.follow', rkey: rkey } }); if (!res.ok) throw new Error(`Undo failed: ${res.data?.message || res.data?.error}`); return res; }