#!/usr/bin/env -S deno --allow-net --allow-env import * as path from "jsr:@std/path"; import { simpleFetchHandler, XRPC } from "npm:@atcute/client"; import { JetstreamSubscription } from "npm:@atcute/jetstream"; import * as TID from "npm:@atcute/tid"; function usage() { const scriptName = path.basename(Deno.mainModule); console.log( `Usage: ${scriptName} at://handle-or-did/collection/rkey ${scriptName} https://bsky.app/profile/handle-or-did/post/rkey ${scriptName} handle-or-did rkey`, ); Deno.exit(1); } if (Deno.args.length < 1 || Deno.args.length > 2) { usage(); } const atRegex = /at:\/\/([^/]+)\/([^/]+)\/([^/"]+)/; let didOrHandle; let did; let collection; let rkey; if (Deno.args.length == 1) { if (Deno.args[0].startsWith("at://")) { const match = Deno.args[0].match(atRegex); if (match) { didOrHandle = match[1]; collection = match[2]; rkey = match[3]; } else usage(); } else { const match = Deno.args[0].match(/\/profile\/([^/]+)\/post\/([2-7a-z]+)/); if (match) { didOrHandle = match[1]; collection = "app.bsky.feed.post"; rkey = match[2]; } else usage(); } } else { didOrHandle = Deno.args[0]; rkey = Deno.args[1]; collection = "app.bsky.feed.post"; } if (didOrHandle.startsWith("did:")) { did = didOrHandle; } else { const handler = simpleFetchHandler({ service: "https://public.api.bsky.app", }); const rpc = new XRPC({ handler }); try { const { data } = await rpc.get("com.atproto.identity.resolveHandle", { params: { handle: didOrHandle }, }); did = data.did; } catch (e) { console.error(e); Deno.exit(1); } } console.log(did, rkey); function dateFromTID(tid: String): Date { return new Date(TID.parse(tid).timestamp / 1000.0); } function dateFromCursor(cursor: Number): Date { return new Date(cursor / 1000.0); } function formatDate(date: Date): string { return date.toISOString().replace("T", " ").replace("Z", ""); } function atURILink(did: string, collection: string, rkey: string): string { const atURI = `at://${did}/${collection}/${rkey}`; return `\x1b]8;;https://pdsls.dev/${atURI}\x1b\\${atURI}\x1b]8;;\x1b\\`; } function linkify(text: string): string { return text.replace( /at:\/\/([^/]+)\/([^/]+)\/([^/"]+)/g, (match, did, collection, rkey) => { return atURILink(did, collection, rkey); }, ); } async function getSingleEvent() { const subscription = new JetstreamSubscription({ url: "wss://jetstream1.us-east.bsky.network", }); for await (const event of subscription) { if (event.kind === "commit") return event; } throw new Error("No commit events received"); } // const event = await getSingleEvent(); // console.log( // `Latest: // Cursor: ${formatDate(dateFromCursor(event.time_us))} (${event.time_us}) // Rev: ${formatDate(dateFromTID(event.commit.rev))} (${event.commit.rev})`, // ); const postTime = dateFromTID(rkey); const offset = 5 * 60 * 1000; const startCursor = (postTime.getTime() - offset) * 1000; console.log( `Target: Rkey time: ${formatDate(postTime)} (${rkey}) Start cursor: ${formatDate(dateFromCursor(startCursor))} (${startCursor}) `, ); const subscription = new JetstreamSubscription({ url: "wss://jetstream2.us-east.bsky.network", wantedCollections: [collection], wantedDids: [did], cursor: startCursor, }); for await (const event of subscription) { if (event.kind === "commit") { const atURI = atURILink( event.did, event.commit.collection, event.commit.rkey, ); console.log( `Stream event ${atURI} Rev time: ${formatDate(dateFromTID(event.commit.rev))} (${event.commit.rev}) Cursor: ${formatDate(dateFromCursor(event.time_us))} (${event.time_us}) `, ); if (event.commit.rkey === rkey) { console.log(`Found post ${atURI}`); console.log(linkify(JSON.stringify(event, null, 2))); break; } else if (event.commit.rkey > rkey) { console.log("Saw event with rkey after expected rkey"); break; } } }