From 811e0c5797e89f2f2d3f96b6595c4a08503a8785 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 01:10:45 +0000 Subject: [PATCH] refactor(bluebubbles): share send helpers --- extensions/bluebubbles/src/attachments.ts | 52 +----------------- extensions/bluebubbles/src/send-helpers.ts | 53 ++++++++++++++++++ extensions/bluebubbles/src/send.ts | 64 ++-------------------- 3 files changed, 61 insertions(+), 108 deletions(-) create mode 100644 extensions/bluebubbles/src/send-helpers.ts diff --git a/extensions/bluebubbles/src/attachments.ts b/extensions/bluebubbles/src/attachments.ts index 24fe357b7c5..917079b3ae5 100644 --- a/extensions/bluebubbles/src/attachments.ts +++ b/extensions/bluebubbles/src/attachments.ts @@ -3,8 +3,8 @@ import crypto from "node:crypto"; import path from "node:path"; import { resolveBlueBubblesAccount } from "./accounts.js"; import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js"; +import { extractBlueBubblesMessageId, resolveBlueBubblesSendTarget } from "./send-helpers.js"; import { resolveChatGuidForTarget } from "./send.js"; -import { parseBlueBubblesTarget, normalizeBlueBubblesHandle } from "./targets.js"; import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl, @@ -102,52 +102,6 @@ export type SendBlueBubblesAttachmentResult = { messageId: string; }; -function resolveSendTarget(raw: string): BlueBubblesSendTarget { - const parsed = parseBlueBubblesTarget(raw); - if (parsed.kind === "handle") { - return { - kind: "handle", - address: normalizeBlueBubblesHandle(parsed.to), - service: parsed.service, - }; - } - if (parsed.kind === "chat_id") { - return { kind: "chat_id", chatId: parsed.chatId }; - } - if (parsed.kind === "chat_guid") { - return { kind: "chat_guid", chatGuid: parsed.chatGuid }; - } - return { kind: "chat_identifier", chatIdentifier: parsed.chatIdentifier }; -} - -function extractMessageId(payload: unknown): string { - if (!payload || typeof payload !== "object") { - return "unknown"; - } - const record = payload as Record; - const data = - record.data && typeof record.data === "object" - ? (record.data as Record) - : null; - const candidates = [ - record.messageId, - record.guid, - record.id, - data?.messageId, - data?.guid, - data?.id, - ]; - for (const candidate of candidates) { - if (typeof candidate === "string" && candidate.trim()) { - return candidate.trim(); - } - if (typeof candidate === "number" && Number.isFinite(candidate)) { - return String(candidate); - } - } - return "unknown"; -} - /** * Send an attachment via BlueBubbles API. * Supports sending media files (images, videos, audio, documents) to a chat. @@ -193,7 +147,7 @@ export async function sendBlueBubblesAttachment(params: { } } - const target = resolveSendTarget(to); + const target = resolveBlueBubblesSendTarget(to); const chatGuid = await resolveChatGuidForTarget({ baseUrl, password, @@ -299,7 +253,7 @@ export async function sendBlueBubblesAttachment(params: { } try { const parsed = JSON.parse(responseBody) as unknown; - return { messageId: extractMessageId(parsed) }; + return { messageId: extractBlueBubblesMessageId(parsed) }; } catch { return { messageId: "ok" }; } diff --git a/extensions/bluebubbles/src/send-helpers.ts b/extensions/bluebubbles/src/send-helpers.ts new file mode 100644 index 00000000000..7c3e4bdabf8 --- /dev/null +++ b/extensions/bluebubbles/src/send-helpers.ts @@ -0,0 +1,53 @@ +import type { BlueBubblesSendTarget } from "./types.js"; +import { normalizeBlueBubblesHandle, parseBlueBubblesTarget } from "./targets.js"; + +export function resolveBlueBubblesSendTarget(raw: string): BlueBubblesSendTarget { + const parsed = parseBlueBubblesTarget(raw); + if (parsed.kind === "handle") { + return { + kind: "handle", + address: normalizeBlueBubblesHandle(parsed.to), + service: parsed.service, + }; + } + if (parsed.kind === "chat_id") { + return { kind: "chat_id", chatId: parsed.chatId }; + } + if (parsed.kind === "chat_guid") { + return { kind: "chat_guid", chatGuid: parsed.chatGuid }; + } + return { kind: "chat_identifier", chatIdentifier: parsed.chatIdentifier }; +} + +export function extractBlueBubblesMessageId(payload: unknown): string { + if (!payload || typeof payload !== "object") { + return "unknown"; + } + const record = payload as Record; + const data = + record.data && typeof record.data === "object" + ? (record.data as Record) + : null; + const candidates = [ + record.messageId, + record.messageGuid, + record.message_guid, + record.guid, + record.id, + data?.messageId, + data?.messageGuid, + data?.message_guid, + data?.message_id, + data?.guid, + data?.id, + ]; + for (const candidate of candidates) { + if (typeof candidate === "string" && candidate.trim()) { + return candidate.trim(); + } + if (typeof candidate === "number" && Number.isFinite(candidate)) { + return String(candidate); + } + } + return "unknown"; +} diff --git a/extensions/bluebubbles/src/send.ts b/extensions/bluebubbles/src/send.ts index eaa85a67898..22e13bb3e31 100644 --- a/extensions/bluebubbles/src/send.ts +++ b/extensions/bluebubbles/src/send.ts @@ -3,11 +3,8 @@ import crypto from "node:crypto"; import { stripMarkdown } from "openclaw/plugin-sdk"; import { resolveBlueBubblesAccount } from "./accounts.js"; import { getCachedBlueBubblesPrivateApiStatus } from "./probe.js"; -import { - extractHandleFromChatGuid, - normalizeBlueBubblesHandle, - parseBlueBubblesTarget, -} from "./targets.js"; +import { extractBlueBubblesMessageId, resolveBlueBubblesSendTarget } from "./send-helpers.js"; +import { extractHandleFromChatGuid, normalizeBlueBubblesHandle } from "./targets.js"; import { blueBubblesFetchWithTimeout, buildBlueBubblesApiUrl, @@ -74,57 +71,6 @@ function resolveEffectId(raw?: string): string | undefined { return raw; } -function resolveSendTarget(raw: string): BlueBubblesSendTarget { - const parsed = parseBlueBubblesTarget(raw); - if (parsed.kind === "handle") { - return { - kind: "handle", - address: normalizeBlueBubblesHandle(parsed.to), - service: parsed.service, - }; - } - if (parsed.kind === "chat_id") { - return { kind: "chat_id", chatId: parsed.chatId }; - } - if (parsed.kind === "chat_guid") { - return { kind: "chat_guid", chatGuid: parsed.chatGuid }; - } - return { kind: "chat_identifier", chatIdentifier: parsed.chatIdentifier }; -} - -function extractMessageId(payload: unknown): string { - if (!payload || typeof payload !== "object") { - return "unknown"; - } - const record = payload as Record; - const data = - record.data && typeof record.data === "object" - ? (record.data as Record) - : null; - const candidates = [ - record.messageId, - record.messageGuid, - record.message_guid, - record.guid, - record.id, - data?.messageId, - data?.messageGuid, - data?.message_guid, - data?.message_id, - data?.guid, - data?.id, - ]; - for (const candidate of candidates) { - if (typeof candidate === "string" && candidate.trim()) { - return candidate.trim(); - } - if (typeof candidate === "number" && Number.isFinite(candidate)) { - return String(candidate); - } - } - return "unknown"; -} - type BlueBubblesChatRecord = Record; function extractChatGuid(chat: BlueBubblesChatRecord): string | null { @@ -365,7 +311,7 @@ async function createNewChatWithMessage(params: { } try { const parsed = JSON.parse(body) as unknown; - return { messageId: extractMessageId(parsed) }; + return { messageId: extractBlueBubblesMessageId(parsed) }; } catch { return { messageId: "ok" }; } @@ -400,7 +346,7 @@ export async function sendMessageBlueBubbles( } const privateApiStatus = getCachedBlueBubblesPrivateApiStatus(account.accountId); - const target = resolveSendTarget(to); + const target = resolveBlueBubblesSendTarget(to); const chatGuid = await resolveChatGuidForTarget({ baseUrl, password, @@ -477,7 +423,7 @@ export async function sendMessageBlueBubbles( } try { const parsed = JSON.parse(body) as unknown; - return { messageId: extractMessageId(parsed) }; + return { messageId: extractBlueBubblesMessageId(parsed) }; } catch { return { messageId: "ok" }; }