A TypeScript toolkit for consuming the Bluesky network in real-time.
at main 123 lines 3.4 kB view raw
1// Post Lifecycle Tracker 2// Streams post events from the Bluesky network, focusing on the interesting 3// ones: edits and deletes. Creates are counted silently and shown in a 4// periodic stats line. Use --all to print every create too. 5// Demonstrates: parsePost, parsePostUpdate, parsePostDelete, toBskyUrl. 6// 7// Usage: npx tsx examples/post-lifecycle.ts [options] 8// Options: 9// --all Print creates, updates, and deletes 10// --creates-only Print only creates 11// --updates-only Print only updates 12// --deletes-only Print only deletes 13 14import { 15 startStreamWithReconnect, 16 parsePost, 17 parsePostUpdate, 18 parsePostDelete, 19 toBskyUrl, 20 formatTruncated, 21} from "../lib/index.js"; 22import type { PostData, PostUpdateData, PostDeleteData } from "../lib/index.js"; 23 24// --- Config --- 25 26const args = new Set(process.argv.slice(2)); 27 28const printCreates = args.has("--all") || args.has("--creates-only"); 29const printUpdates = !args.has("--creates-only") && !args.has("--deletes-only"); 30const printDeletes = !args.has("--creates-only") && !args.has("--updates-only"); 31 32const STATS_INTERVAL_MS = 10_000; 33 34// --- Counters --- 35 36let creates = 0; 37let updates = 0; 38let deletes = 0; 39 40const stats = () => `[C:${creates} U:${updates} D:${deletes}]`; 41 42// --- Formatters --- 43 44const formatCreate = (post: PostData): string => { 45 const text = formatTruncated(post.text, 100); 46 const url = toBskyUrl(post.did, post.rkey); 47 return [ 48 `+ CREATE ${stats()}`, 49 ` ${text}`, 50 ` ${url}`, 51 ].join("\n"); 52}; 53 54const formatUpdate = (post: PostUpdateData): string => { 55 const text = formatTruncated(post.text, 100); 56 const url = toBskyUrl(post.did, post.rkey); 57 return [ 58 `~ UPDATE ${stats()}`, 59 ` ${text}`, 60 ` cid: ${post.cid}`, 61 ` ${url}`, 62 ].join("\n"); 63}; 64 65const formatDelete = (post: PostDeleteData): string => { 66 return [ 67 `- DELETE ${stats()}`, 68 ` ${post.uri}`, 69 ` did: ${post.did}`, 70 ].join("\n"); 71}; 72 73// --- Stats timer --- 74 75let lastStats = { creates: 0, updates: 0, deletes: 0 }; 76 77const printStats = () => { 78 const dc = creates - lastStats.creates; 79 const du = updates - lastStats.updates; 80 const dd = deletes - lastStats.deletes; 81 const elapsed = STATS_INTERVAL_MS / 1000; 82 console.log( 83 ` --- ${stats()} | last ${elapsed}s: +${dc} creates, ~${du} updates, -${dd} deletes (${(dc / elapsed).toFixed(0)} posts/sec) ---\n` 84 ); 85 lastStats = { creates, updates, deletes }; 86}; 87 88// --- Stream --- 89 90startStreamWithReconnect({ 91 config: { wantedCollections: ["app.bsky.feed.post"] }, 92 onEvent: (event) => { 93 const created = parsePost(event); 94 if (created) { 95 creates++; 96 if (printCreates) console.log(formatCreate(created) + "\n"); 97 return; 98 } 99 100 const updated = parsePostUpdate(event); 101 if (updated) { 102 updates++; 103 if (printUpdates) console.log(formatUpdate(updated) + "\n"); 104 return; 105 } 106 107 const deleted = parsePostDelete(event); 108 if (deleted) { 109 deletes++; 110 if (printDeletes) console.log(formatDelete(deleted) + "\n"); 111 } 112 }, 113 onOpen: () => { 114 const showing = [ 115 printCreates && "creates", 116 printUpdates && "updates", 117 printDeletes && "deletes", 118 ].filter(Boolean).join(", "); 119 console.log(`Connected. Printing: ${showing}. Stats every ${STATS_INTERVAL_MS / 1000}s.\n`); 120 }, 121}); 122 123setInterval(printStats, STATS_INTERVAL_MS);