diff --git a/lib/buildMessageContent.ts b/lib/buildMessageContent.ts index 135f68f..d6e83f8 100644 --- a/lib/buildMessageContent.ts +++ b/lib/buildMessageContent.ts @@ -1,4 +1,10 @@ -import type { AppBskyFeedPost } from "@atcute/bluesky" +import { + AppBskyEmbedRecord, + AppBskyEmbedRecordWithMedia, + type AppBskyFeedPost, +} from "@atcute/bluesky" +import { is, parseResourceUri } from "@atcute/lexicons" +import { getTraqMessageIdByAtProtoUri } from "../repository/post.ts" import { config } from "./config.ts" interface BuildMessageParams { @@ -9,7 +15,9 @@ interface BuildMessageParams { const encoder = new TextEncoder() const decoder = new TextDecoder() -export const buildMessageContent = ({ post, imageIds }: BuildMessageParams) => { +export const buildMessageContent = async ( + { post, imageIds }: BuildMessageParams, +) => { let text = post.text if (post.facets?.length) { @@ -54,5 +62,42 @@ export const buildMessageContent = ({ post, imageIds }: BuildMessageParams) => { text = text ? `${text}\n${imageLinks}` : imageLinks } + if ( + is(AppBskyEmbedRecord.mainSchema, post.embed) || + is(AppBskyEmbedRecordWithMedia.mainSchema, post.embed) + ) { + let embeddedRecordUriStr: string + + if (is(AppBskyEmbedRecord.mainSchema, post.embed.record)) { + embeddedRecordUriStr = post.embed.record.record.uri + } else { + embeddedRecordUriStr = post.embed.record.uri + } + + let urlToAppend: string + const traqMessageId = await getTraqMessageIdByAtProtoUri( + embeddedRecordUriStr, + ) + + if (traqMessageId) { + // This message is already posted to traQ, so we can append its URL to the text + urlToAppend = `${config.traqBaseUrl}/messages/${traqMessageId}` + } else { + // This message is not posted to traQ, so we should append the URL to the original post + const embeddedRecordUri = parseResourceUri(embeddedRecordUriStr) + + if (embeddedRecordUri.ok) { + urlToAppend = + `https://bsky.app/profile/${embeddedRecordUri.value.repo}/post/${embeddedRecordUri.value.rkey}` + } else { + throw new Error("Invalid embedded record URI", { + cause: embeddedRecordUri.error, + }) + } + } + + text = text ? `${text}\n${urlToAppend}` : urlToAppend + } + return text } diff --git a/service/jestream.ts b/service/jestream.ts index 77a9c29..a9ceb16 100644 --- a/service/jestream.ts +++ b/service/jestream.ts @@ -1,4 +1,9 @@ -import { AppBskyFeedPost } from "@atcute/bluesky" +import { + AppBskyEmbedImages, + AppBskyEmbedRecordWithMedia, + AppBskyEmbedVideo, + AppBskyFeedPost, +} from "@atcute/bluesky" import { JetstreamSubscription } from "@atcute/jetstream" import { type Did, is } from "@atcute/lexicons" import { buildAtProtoUri } from "../lib/atProto.ts" @@ -14,13 +19,6 @@ import { getUserAccessToken, getUserSettingByDid } from "../repository/user.ts" import { client } from "../traq/client.gen.ts" import { postMessage } from "../traq/index.ts" -type EmbedType = NonNullable["$type"] - -const SUPPORTED_EMBED_TYPES: readonly EmbedType[] = [ - "app.bsky.embed.external", - "app.bsky.embed.images", -] as const - client.setConfig({ baseUrl: `${config.traqBaseUrl}/api/v3`, }) @@ -92,13 +90,14 @@ export class JetstreamService { userDid: event.did, recordKey: event.commit.rkey, }) - const hasNonSupportedEmbed = event.commit.record.embed && - !SUPPORTED_EMBED_TYPES.includes(event.commit.record.embed.$type) const isReply = !!event.commit.record.reply?.parent - if (hasNonSupportedEmbed || isReply) { + if ( + is(AppBskyEmbedVideo.mainSchema, event.commit.record.embed) || + isReply + ) { console.warn( - `Skipping post ${atProtoUri} because it has unsupported embed or is a reply.`, + `Skipping post ${atProtoUri} because it has video or is a reply.`, ) this.cursor = event.time_us @@ -118,13 +117,26 @@ export class JetstreamService { const userSetting = await getUserSettingByDid(event.did) const accessToken = await getUserAccessToken(userSetting.userId) + let images: AppBskyEmbedImages.Image[] | undefined let imageIds: string[] | undefined - if (event.commit.record.embed?.$type === "app.bsky.embed.images") { + if (is(AppBskyEmbedImages.mainSchema, event.commit.record.embed)) { + images = event.commit.record.embed.images + } else if ( + is( + AppBskyEmbedRecordWithMedia.mainSchema, + event.commit.record.embed, + ) && + is(AppBskyEmbedImages.mainSchema, event.commit.record.embed.media) + ) { + images = event.commit.record.embed.media.images + } + + if (images?.length) { imageIds = await uploadImages({ accessToken, did: event.did, - images: event.commit.record.embed.images, + images: images, targetChannelId: userSetting.targetChannelId, }) @@ -141,7 +153,7 @@ export class JetstreamService { channelId: userSetting.targetChannelId, }, body: { - content: buildMessageContent({ + content: await buildMessageContent({ imageIds, post: event.commit.record, }),