A simple Bluesky bot to make sense of the noise, with responses powered by Gemini, similar to Grok.

feat: clean up post parsing & parse quote posts

Changed files
+85 -32
src
+1 -1
src/index.ts
··· 19 19 await bot.setChatPreference(IncomingChatPreference.All); 20 20 bot.on("message", messages.handler); 21 21 22 - logger.success("Registered events (reply, mention, quote)"); 22 + logger.success("Registered events (message)"); 23 23 } catch (e) { 24 24 logger.error("Failure to log-in: ", e); 25 25 process.exit(1);
+12
src/types.ts
··· 1 + export type ParsedPost = { 2 + thread?: { 3 + ancestors: ParsedPost[]; 4 + }; 5 + author: string; 6 + text: string; 7 + images?: { 8 + index: number; 9 + alt: string; 10 + }[]; 11 + quotePost?: ParsedPost; 12 + };
+24 -30
src/utils/conversation.ts
··· 9 9 import { and, eq } from "drizzle-orm"; 10 10 import { env } from "../env"; 11 11 import { bot, MAX_GRAPHEMES } from "../core"; 12 - import { parsePostImages, traverseThread } from "./post"; 12 + import { parsePost, parsePostImages, traverseThread } from "./post"; 13 13 14 14 /* 15 15 Utilities ··· 126 126 const post = await bot.getPost(row.postUri); 127 127 const convoMessages = await getRelevantMessages(row!); 128 128 129 - const thread = await traverseThread(post); 129 + let parseResult = null; 130 + try { 131 + parseResult = yaml.dump({ 132 + post: await parsePost(post, true), 133 + messages: convoMessages.map((message) => { 134 + const profile = resolveDid(convo, message.did); 130 135 131 - return yaml.dump({ 132 - post: { 133 - thread: { 134 - ancestors: thread.map((post) => ({ 135 - author: post.author.displayName 136 - ? `${post.author.displayName} (${post.author.handle})` 137 - : `Handle: ${post.author.handle}`, 138 - text: post.text, 139 - })), 140 - }, 141 - author: post.author.displayName 142 - ? `${post.author.displayName} (${post.author.handle})` 143 - : `Handle: ${post.author.handle}`, 144 - text: post.text, 145 - images: parsePostImages(post), 146 - likes: post.likeCount || 0, 147 - replies: post.replyCount || 0, 148 - }, 149 - messages: convoMessages.map((message) => { 150 - const profile = resolveDid(convo, message.did); 136 + return { 137 + user: profile.displayName 138 + ? `${profile.displayName} (${profile.handle})` 139 + : `Handle: ${profile.handle}`, 140 + text: message.text, 141 + }; 142 + }), 143 + }); 144 + } catch (e) { 145 + convo.sendMessage({ 146 + text: 147 + "Sorry, I ran into an issue analyzing that post. Please try again.", 148 + }); 149 + 150 + throw new Error("Failed to parse conversation"); 151 + } 151 152 152 - return { 153 - user: profile.displayName 154 - ? `${profile.displayName} (${profile.handle})` 155 - : `Handle: ${profile.handle}`, 156 - text: message.text, 157 - }; 158 - }), 159 - }); 153 + return parseResult; 160 154 } 161 155 162 156 /*
+48 -1
src/utils/post.ts
··· 1 - import { EmbedImage, Post } from "@skyware/bot"; 1 + import { 2 + EmbedImage, 3 + Post, 4 + PostEmbed, 5 + RecordEmbed, 6 + RecordWithMediaEmbed, 7 + } from "@skyware/bot"; 2 8 import * as c from "../core"; 3 9 import * as yaml from "js-yaml"; 10 + import type { ParsedPost } from "../types"; 11 + 12 + export async function parsePost( 13 + post: Post, 14 + includeThread: boolean, 15 + ): Promise<ParsedPost> { 16 + const [images, quotePost, ancestorPosts] = await Promise.all([ 17 + parsePostImages(post), 18 + parseQuote(post), 19 + includeThread ? traverseThread(post) : Promise.resolve(null), 20 + ]); 21 + 22 + return { 23 + author: post.author.displayName 24 + ? `${post.author.displayName} (${post.author.handle})` 25 + : `Handle: ${post.author.handle}`, 26 + text: post.text, 27 + ...(images && { images }), 28 + ...(quotePost && { quotePost }), 29 + ...(ancestorPosts && { 30 + thread: { 31 + ancestors: await Promise.all( 32 + ancestorPosts.map((ancestor) => parsePost(ancestor, false)), 33 + ), 34 + }, 35 + }), 36 + }; 37 + } 38 + 39 + async function parseQuote(post: Post) { 40 + if ( 41 + !post.embed || (!post.embed.isRecord() && !post.embed.isRecordWithMedia()) 42 + ) return undefined; 43 + 44 + const record = (post.embed as RecordEmbed || RecordWithMediaEmbed).record; 45 + console.log("embed: ", post.embed); 46 + console.log("record: ", record); 47 + const embedPost = await c.bot.getPost(record.uri); 48 + 49 + return await parsePost(embedPost, false); 50 + } 4 51 5 52 export function parsePostImages(post: Post) { 6 53 if (!post.embed) return [];