feat: improve BlueBubbles message processing by adding reply context formatting and enhancing message ID extraction from responses

This commit is contained in:
Tyler Yust
2026-01-20 01:34:51 -08:00
committed by Peter Steinberger
parent e5514d4854
commit b0b42b4e14
5 changed files with 66 additions and 9 deletions

View File

@@ -1078,6 +1078,8 @@ describe("BlueBubbles webhook monitor", () => {
expect(callArgs.ctx.ReplyToId).toBe("msg-0");
expect(callArgs.ctx.ReplyToBody).toBe("original message");
expect(callArgs.ctx.ReplyToSender).toBe("+15550000000");
expect(callArgs.ctx.Body).toContain("[Replying to +15550000000 id:msg-0]");
expect(callArgs.ctx.Body).toContain("original message");
});
});

View File

@@ -216,6 +216,21 @@ function buildMessagePlaceholder(message: NormalizedWebhookMessage): string {
return "";
}
function formatReplyContext(message: {
replyToId?: string;
replyToBody?: string;
replyToSender?: string;
}): string | null {
if (!message.replyToId && !message.replyToBody && !message.replyToSender) return null;
const sender = message.replyToSender?.trim() || "unknown sender";
const idPart = message.replyToId ? ` id:${message.replyToId}` : "";
const body = message.replyToBody?.trim();
if (!body) {
return `[Replying to ${sender}${idPart}]\n[/Replying]`;
}
return `[Replying to ${sender}${idPart}]\n${body}\n[/Replying]`;
}
function readNumberLike(record: Record<string, unknown> | null, key: string): number | undefined {
if (!record) return undefined;
const value = record[key];
@@ -1178,6 +1193,8 @@ async function processMessage(
}
}
const rawBody = text.trim() || placeholder;
const replyContext = formatReplyContext(message);
const baseBody = replyContext ? `${rawBody}\n\n${replyContext}` : rawBody;
const fromLabel = isGroup
? `group:${peerId}`
: message.senderName || `user:${message.senderId}`;
@@ -1202,7 +1219,7 @@ async function processMessage(
timestamp: message.timestamp,
previousTimestamp,
envelope: envelopeOptions,
body: rawBody,
body: baseBody,
});
let chatGuidForActions = chatGuid;
if (!chatGuidForActions && baseUrl && password) {

View File

@@ -589,6 +589,38 @@ describe("send", () => {
expect(result.messageId).toBe("numeric-id-456");
});
it("extracts messageGuid from response payload", async () => {
mockFetch
.mockResolvedValueOnce({
ok: true,
json: () =>
Promise.resolve({
data: [
{
guid: "iMessage;-;+15551234567",
participants: [{ address: "+15551234567" }],
},
],
}),
})
.mockResolvedValueOnce({
ok: true,
text: () =>
Promise.resolve(
JSON.stringify({
data: { messageGuid: "msg-guid-789" },
}),
),
});
const result = await sendMessageBlueBubbles("+15551234567", "Hello", {
serverUrl: "http://localhost:1234",
password: "test",
});
expect(result.messageId).toBe("msg-guid-789");
});
it("resolves credentials from config", async () => {
mockFetch
.mockResolvedValueOnce({

View File

@@ -86,12 +86,18 @@ function resolveSendTarget(raw: string): BlueBubblesSendTarget {
function extractMessageId(payload: unknown): string {
if (!payload || typeof payload !== "object") return "unknown";
const record = payload as Record<string, unknown>;
const data = record.data && typeof record.data === "object" ? (record.data as Record<string, unknown>) : null;
const data =
record.data && typeof record.data === "object" ? (record.data as Record<string, unknown>) : 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,
];