your personal website on atproto - mirror blento.app
25
fork

Configure Feed

Select the types of activity you want to include in your feed.

at fix/layout-stuff 133 lines 3.9 kB view raw
1/** 2 * ATProto helper script for debugging. 3 * 4 * Usage: 5 * npx tsx scripts/atproto.ts listRecords <handle> <collection> 6 * npx tsx scripts/atproto.ts getRecord <handle> <collection> <rkey> 7 * 8 * Examples: 9 * npx tsx scripts/atproto.ts listRecords japan.selfhosted.social app.blento.card 10 * npx tsx scripts/atproto.ts getRecord japan.selfhosted.social app.blento.card self 11 */ 12 13async function resolveHandle(handle: string): Promise<string> { 14 // Try DNS-based resolution via DoH 15 const dnsRes = await fetch( 16 `https://mozilla.cloudflare-dns.com/dns-query?name=_atproto.${handle}&type=TXT`, 17 { headers: { Accept: 'application/dns-json' } } 18 ); 19 const dns = await dnsRes.json(); 20 for (const answer of dns.Answer ?? []) { 21 const match = answer.data?.replace(/"/g, '').match(/^did=(.+)$/); 22 if (match) return match[1]; 23 } 24 25 // Fallback: HTTP well-known 26 const httpRes = await fetch(`https://${handle}/.well-known/atproto-did`); 27 if (httpRes.ok) { 28 const did = (await httpRes.text()).trim(); 29 if (did.startsWith('did:')) return did; 30 } 31 32 throw new Error(`Could not resolve handle: ${handle}`); 33} 34 35async function resolvePDS(did: string): Promise<string> { 36 let docUrl: string; 37 if (did.startsWith('did:plc:')) { 38 docUrl = `https://plc.directory/${did}`; 39 } else if (did.startsWith('did:web:')) { 40 const host = did.replace('did:web:', ''); 41 docUrl = `https://${host}/.well-known/did.json`; 42 } else { 43 throw new Error(`Unsupported DID method: ${did}`); 44 } 45 46 const res = await fetch(docUrl); 47 if (!res.ok) throw new Error(`Failed to fetch DID document: ${res.status}`); 48 const doc = await res.json(); 49 50 for (const service of doc.service ?? []) { 51 if (service.id === '#atproto_pds') { 52 return service.serviceEndpoint; 53 } 54 } 55 throw new Error('No #atproto_pds service found in DID document'); 56} 57 58async function listRecords(handle: string, collection: string) { 59 const did = await resolveHandle(handle); 60 const pds = await resolvePDS(did); 61 console.error(`Resolved: ${handle}${did} @ ${pds}`); 62 63 const allRecords: any[] = []; 64 let cursor: string | undefined; 65 66 do { 67 const params = new URLSearchParams({ 68 repo: did, 69 collection, 70 limit: '100' 71 }); 72 if (cursor) params.set('cursor', cursor); 73 74 const res = await fetch(`${pds}/xrpc/com.atproto.repo.listRecords?${params}`); 75 if (!res.ok) { 76 const body = await res.text(); 77 throw new Error(`listRecords failed: ${res.status} ${body}`); 78 } 79 const data = await res.json(); 80 allRecords.push(...data.records); 81 cursor = data.cursor; 82 } while (cursor); 83 84 console.log(JSON.stringify(allRecords, null, 2)); 85} 86 87async function getRecord(handle: string, collection: string, rkey: string) { 88 const did = await resolveHandle(handle); 89 const pds = await resolvePDS(did); 90 console.error(`Resolved: ${handle}${did} @ ${pds}`); 91 92 const params = new URLSearchParams({ repo: did, collection, rkey }); 93 const res = await fetch(`${pds}/xrpc/com.atproto.repo.getRecord?${params}`); 94 if (!res.ok) { 95 const body = await res.text(); 96 throw new Error(`getRecord failed: ${res.status} ${body}`); 97 } 98 const data = await res.json(); 99 console.log(JSON.stringify(data, null, 2)); 100} 101 102// CLI 103const [, , command, ...args] = process.argv; 104 105switch (command) { 106 case 'listRecords': 107 if (args.length < 2) { 108 console.error('Usage: listRecords <handle> <collection>'); 109 process.exit(1); 110 } 111 listRecords(args[0], args[1]).catch((e) => { 112 console.error(e.message); 113 process.exit(1); 114 }); 115 break; 116 117 case 'getRecord': 118 if (args.length < 3) { 119 console.error('Usage: getRecord <handle> <collection> <rkey>'); 120 process.exit(1); 121 } 122 getRecord(args[0], args[1], args[2]).catch((e) => { 123 console.error(e.message); 124 process.exit(1); 125 }); 126 break; 127 128 default: 129 console.error('Commands: listRecords, getRecord'); 130 console.error(' listRecords <handle> <collection>'); 131 console.error(' getRecord <handle> <collection> <rkey>'); 132 process.exit(1); 133}