ReviewOS

also looking at this

stacks/postline

Bluesky publishing v2: rich-text facets + image upload

#9
Merged glennmichael123 wants to merge feat/bluesky-publishing-v2 into main
8 files +347 -12
app/Services/Social/BlueskyService.tsmodified+24-0
Changes to app/Services/Social/BlueskyService.ts
@@ -187,9 +187,33 @@ export class BlueskyService {
187187 const publishInput = { text: post.body } as Parameters<BlueskyDriver['publish']>[1] & {
188188 external?: { uri: string, title: string, description?: string }
189189 reply?: { root: { uri: string, cid: string }, parent: { uri: string, cid: string } }
190 media?: Array<{ bytes?: Uint8Array, mimeType?: string, altText?: string }>
190191 }
191192 if (content?.external) publishInput.external = content.external
192193 if (content?.reply) publishInput.reply = content.reply
194 if (content?.media?.length) {
195 // The driver embeds raw bytes; fetch URL-only media server-side.
196 const media: Array<{ bytes: Uint8Array, mimeType?: string, altText?: string }> = []
197 for (const item of content.media.slice(0, 4)) {
198 if (item.bytes?.length) {
199 media.push({ bytes: item.bytes, mimeType: item.mimeType, altText: item.altText })
200 }
201 else if (item.url) {
202 try {
203 const response = await fetch(item.url)
204 if (!response.ok) continue
205 const bytes = new Uint8Array(await response.arrayBuffer())
206 media.push({
207 bytes,
208 mimeType: item.mimeType || response.headers.get('content-type') || 'image/jpeg',
209 altText: item.altText,
210 })
211 }
212 catch {}
213 }
214 }
215 if (media.length) publishInput.media = media
216 }
193217
194218 await database.insertInto('post_targets').values({
195219 uuid: targetUuid,