diff --git a/src/signal/monitor.tool-result.test.ts b/src/signal/monitor.tool-result.test.ts index b9a6a24bf..07a3fa262 100644 --- a/src/signal/monitor.tool-result.test.ts +++ b/src/signal/monitor.tool-result.test.ts @@ -197,6 +197,45 @@ describe("monitorSignalProvider tool results", () => { expect(updateLastRouteMock).not.toHaveBeenCalled(); }); + it("ignores reaction-only messages when reactions live in dataMessage", async () => { + const abortController = new AbortController(); + + streamMock.mockImplementation(async ({ onEvent }) => { + const payload = { + envelope: { + sourceNumber: "+15550001111", + sourceName: "Ada", + timestamp: 1, + dataMessage: { + reaction: { + emoji: "👍", + targetAuthor: "+15550002222", + targetSentTimestamp: 2, + }, + attachments: [{}], + }, + }, + }; + await onEvent({ + event: "receive", + data: JSON.stringify(payload), + }); + abortController.abort(); + }); + + await monitorSignalProvider({ + autoStart: false, + baseUrl: "http://127.0.0.1:8080", + abortSignal: abortController.signal, + }); + + await flush(); + + expect(replyMock).not.toHaveBeenCalled(); + expect(sendMock).not.toHaveBeenCalled(); + expect(updateLastRouteMock).not.toHaveBeenCalled(); + }); + it("enqueues system events for reaction notifications", async () => { config = { ...config, diff --git a/src/signal/monitor.ts b/src/signal/monitor.ts index 022219ed9..fe27016cb 100644 --- a/src/signal/monitor.ts +++ b/src/signal/monitor.ts @@ -70,6 +70,7 @@ type SignalDataMessage = { groupName?: string | null; } | null; quote?: { text?: string | null } | null; + reaction?: SignalReactionMessage | null; }; type SignalAttachment = { @@ -403,8 +404,14 @@ export async function monitorSignalProvider( } const dataMessage = envelope.dataMessage ?? envelope.editMessage?.dataMessage; - if (envelope.reactionMessage && !dataMessage) { - const reaction = envelope.reactionMessage; + const reaction = + envelope.reactionMessage ?? dataMessage?.reaction ?? null; + const messageText = (dataMessage?.message ?? "").trim(); + const quoteText = dataMessage?.quote?.text?.trim() ?? ""; + const hasBodyContent = + Boolean(messageText || quoteText) || + Boolean(!reaction && dataMessage?.attachments?.length); + if (reaction && !hasBodyContent) { if (reaction.isRemove) return; // Ignore reaction removals const emojiLabel = reaction.emoji?.trim() || "emoji"; const senderDisplay = formatSignalSenderDisplay(sender); @@ -550,7 +557,6 @@ export async function monitorSignalProvider( ? isSignalSenderAllowed(sender, effectiveGroupAllow) : true : dmAllowed; - const messageText = (dataMessage.message ?? "").trim(); let mediaPath: string | undefined; let mediaType: string | undefined;