import { EmbedImage, Post, PostEmbed, RecordEmbed, RecordWithMediaEmbed, } from "@skyware/bot"; import * as c from "../core"; import * as yaml from "js-yaml"; import type { ParsedPost } from "../types"; import { postCache } from "../utils/cache"; export async function parsePost( post: Post, includeThread: boolean, seenUris: Set = new Set(), ): Promise { if (seenUris.has(post.uri)) { return undefined; } seenUris.add(post.uri); const [images, quotePost, ancestorPosts] = await Promise.all([ parsePostImages(post), parseQuote(post, seenUris), includeThread ? traverseThread(post) : Promise.resolve(null), ]); return { author: post.author.displayName ? `${post.author.displayName} (${post.author.handle})` : `Handle: ${post.author.handle}`, text: post.text, ...(images && { images }), ...(quotePost && { quotePost }), ...(ancestorPosts && { thread: { ancestors: (await Promise.all( ancestorPosts.map((ancestor) => parsePost(ancestor, false, seenUris)), )).filter((post): post is ParsedPost => post !== undefined), }, }), }; } async function parseQuote(post: Post, seenUris: Set) { if ( !post.embed || (!post.embed.isRecord() && !post.embed.isRecordWithMedia()) ) return undefined; const record = (post.embed as RecordEmbed || RecordWithMediaEmbed).record; if (seenUris.has(record.uri)) { return undefined; } let embedPost = postCache.get(record.uri); if (!embedPost) { embedPost = await c.bot.getPost(record.uri); postCache.set(record.uri, embedPost); } return await parsePost(embedPost, false, seenUris); } export function parsePostImages(post: Post) { if (!post.embed) return []; let images: EmbedImage[] = []; if (post.embed.isImages()) { images = post.embed.images; } else if (post.embed.isRecordWithMedia()) { const media = post.embed.media; if (media && media.isImages()) { images = media.images; } } return images.map((image, idx) => parseImage(image, idx + 1)).filter((img) => img.alt.length > 0 ); } function parseImage(image: EmbedImage, index: number) { return { index: index, alt: image.alt, }; } /* Traversal */ export async function traverseThread(post: Post): Promise { const thread: Post[] = [ post, ]; let currentPost: Post | undefined = post; let parentCount = 0; while ( currentPost && parentCount < c.MAX_THREAD_DEPTH ) { const parentPost = await currentPost.fetchParent(); if (parentPost) { thread.push(parentPost); currentPost = parentPost; } else { break; } parentCount++; } return thread.reverse(); } export function parseThread(thread: Post[]) { return yaml.dump({ uri: thread[0]!.uri, posts: thread.map((post) => ({ author: `${post.author.displayName} (${post.author.handle})`, text: post.text, })), }); }