From 515b72153053840c14e798d3281e4074e09182b9 Mon Sep 17 00:00:00 2001 From: Maykon Jhonathan Date: Tue, 25 Aug 2026 15:03:53 -0300 Subject: [PATCH 1/2] fix: preserve reply metadata when flattening extendedTextMessage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A text reply carries its quote in extendedTextMessage.contextInfo (stanzaId, participant, quotedMessage). prepareMessage() copied only .text into conversation and deleted the wrapper, dropping the reply metadata before it reached webhooks, the database and integrations. The data-level contextInfo comes from messageContextInfo, which carries threadId/messageSecret/limitSharingV2 and never stanzaId, so nothing downstream could recover it. Media replies were unaffected because they are never flattened. The quote is now merged into contextInfo before the wrapper is dropped — which is where the quotedMessage normalization right below already expects it. --- .../channel/whatsapp/whatsapp.baileys.service.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 22839fd451..39231e7003 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -5184,6 +5184,21 @@ export class BaileysStartupService extends ChannelStartupService { if (messageRaw.message.extendedTextMessage) { messageRaw.messageType = 'conversation'; messageRaw.message.conversation = messageRaw.message.extendedTextMessage.text; + + /** + * Keep the reply metadata before dropping the wrapper. + * + * `stanzaId`, `participant` and `quotedMessage` live inside the + * extendedTextMessage's own contextInfo. Deleting the wrapper dropped + * them, so every text reply reached webhooks and the database unquoted, + * while media replies (never flattened) kept theirs. + */ + const quotedContext = messageRaw.message.extendedTextMessage.contextInfo; + + if (quotedContext) { + messageRaw.contextInfo = { ...(messageRaw.contextInfo ?? {}), ...quotedContext }; + } + delete messageRaw.message.extendedTextMessage; } From 1685687cc7825e30a541388bc551685a5edec326 Mon Sep 17 00:00:00 2001 From: Maykon Jhonathan Date: Tue, 25 Aug 2026 15:16:04 -0300 Subject: [PATCH 2/2] fix: prefer the message key over the quoted author for pushName `fetchMessages()` fell back to `contextInfo.participant` before `messageKey.participant` when a message had no pushName. That branch was unreachable while contextInfo never carried a participant; with the quote preserved, it would label a text reply with the author of the message it quotes. The key is the only field that identifies who sent THIS message, so it now comes first, and the quoted participant stays as a last resort. --- .../channel/whatsapp/whatsapp.baileys.service.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 39231e7003..d37e41d37e 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -5700,12 +5700,22 @@ export class BaileysStartupService extends ChannelStartupService { if (!message.pushName) { if (messageKey.fromMe) { message.pushName = 'Você'; + } else if (messageKey.participant) { + /** + * The sender comes from the message key. + * + * `contextInfo.participant` is the author of the QUOTED message, not + * of this one, so it may only stand in when the key carries nothing. + * It used to be checked first, which was harmless while contextInfo + * never carried a participant — and would mislabel the sender of a + * text reply now that the quote is preserved. + */ + message.pushName = messageKey.participant.split('@')[0]; } else if (message.contextInfo) { const contextInfo = message.contextInfo as { participant?: string }; + if (contextInfo.participant) { message.pushName = contextInfo.participant.split('@')[0]; - } else if (messageKey.participant) { - message.pushName = messageKey.participant.split('@')[0]; } } }