fix(signal): handle reactions in dataMessage.reaction format (#1)
* fix(signal): handle reactions inside dataMessage.reaction Signal reactions can arrive in two formats: 1. envelope.reactionMessage (already handled) 2. envelope.dataMessage.reaction (now handled) The signal-cli SSE events use the second format, which was being misinterpreted as a message with attachments, leading to 'broken media / attachments' errors. Changes: - Add reaction property to SignalDataMessage type - Check both envelope.reactionMessage and dataMessage.reaction - Improve body content detection to properly identify reaction-only messages - Add test for dataMessage.reaction format * fix(signal): reaction notifications work when account is phone number When reactionNotifications mode is 'own', notifications would never fire because resolveSignalReactionTarget() returned a UUID but shouldEmitSignalReactionNotification() compared it against the account phone number, which never matched. The fix: - Add optional 'phone' field to SignalReactionTarget type - Extract phone number first in resolveSignalReactionTarget(), include it even when UUID is present - In shouldEmitSignalReactionNotification() 'own' mode, check phone match first before falling back to UUID comparison This ensures reactions to your own messages are properly detected when the Signal account is configured as a phone number and the reaction event contains both targetAuthor (phone) and targetAuthorUuid. * fix(signal): include phone in reaction target for own-mode matching When targetAuthorUuid is present, also store targetAuthor phone number in the reaction target. This allows own-mode reaction notifications to match when comparing account phone against UUID-based targets.
This commit is contained in:
committed by
Peter Steinberger
parent
f648267dd9
commit
59e6064006
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user