fix(messages): reorganize interactive message processing to execute correctly

Critical bug fix: Interactive message blocks were never being executed because
they were placed AFTER the normal text message processing block. Messages with
text+buttons were processed as plain text, ignoring the interactive features.

Changes:
- Move all interactive message processing (buttons, lists, templates, carousel)
  to BEFORE the normal text processing block
- Remove duplicate interactive message blocks that were unreachable
- Ensure correct execution order: check for interactive features first, then
  fallback to normal text processing

This fixes the runtime error where interactive message functions were not
being called properly when sending messages with buttons/lists.

Structure now:
1. Check text + buttons → buttonsMessage
2. Check text + templateButtons → templateMessage
3. Check sections → listMessage
4. Check carousel → interactiveMessage
5. Check text (normal) → extendedTextMessage
6. Other message types...

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Renato Alcara
2026-01-23 01:23:24 -03:00
parent b48dbd7038
commit 87ed383ba4
+84 -103
View File
@@ -396,7 +396,89 @@ export const generateWAMessageContent = async (
options: MessageContentGenerationOptions
) => {
let m: WAMessageContent = {}
if (hasNonNullishProperty(message, 'text')) {
// ⚠️ EXPERIMENTAL: Check for interactive messages FIRST (buttons, lists, templates)
if (hasNonNullishProperty(message, 'text') && hasNonNullishProperty(message, 'buttons')) {
// Process buttons for text messages
const buttonsMessage: proto.Message.IButtonsMessage = {
contentText: (message as any).text,
footerText: (message as any).footerText,
headerType: (message as any).headerType || proto.Message.ButtonsMessage.HeaderType.EMPTY
}
buttonsMessage.buttons = ((message as any).buttons as any[]).map((btn: any, idx: number) => ({
buttonId: btn.buttonId || `btn_${idx}`,
buttonText: { displayText: btn.buttonText?.displayText || btn.displayText || btn.text },
type: btn.type || proto.Message.ButtonsMessage.Button.Type.RESPONSE
}))
m.buttonsMessage = buttonsMessage
options.logger?.warn('[EXPERIMENTAL] Sending buttonsMessage - this may not work and can cause bans')
} else if (hasNonNullishProperty(message, 'text') && hasNonNullishProperty(message, 'templateButtons')) {
// Process templateButtons
const templateMessage: proto.Message.ITemplateMessage = {
hydratedTemplate: {
hydratedContentText: (message as any).text,
hydratedFooterText: (message as any).footer
}
}
templateMessage.hydratedTemplate!.hydratedButtons = ((message as any).templateButtons as any[]).map((btn: any) => {
if (btn.quickReplyButton) {
return { index: btn.index, quickReplyButton: btn.quickReplyButton }
} else if (btn.urlButton) {
return { index: btn.index, urlButton: btn.urlButton }
} else if (btn.callButton) {
return { index: btn.index, callButton: btn.callButton }
}
return btn
})
m.templateMessage = templateMessage
options.logger?.warn('[EXPERIMENTAL] Sending templateMessage - this may not work and can cause bans')
} else if (hasNonNullishProperty(message, 'sections')) {
// Process list messages
const listMessage: proto.Message.IListMessage = {
title: (message as any).title,
description: (message as any).text,
buttonText: (message as any).buttonText || 'View options',
footerText: (message as any).footerText,
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT
}
listMessage.sections = ((message as any).sections as any[]).map((section: any) => ({
title: section.title,
rows: section.rows.map((row: any) => ({
rowId: row.rowId || row.id,
title: row.title,
description: row.description
}))
}))
m.listMessage = listMessage
options.logger?.warn('[EXPERIMENTAL] Sending listMessage - this may not work and can cause bans')
} else if (hasNonNullishProperty(message, 'carousel')) {
// Process carousel/interactive messages
const carousel = (message as any).carousel
const interactiveMessage: proto.Message.IInteractiveMessage = {
header: carousel.header || { title: carousel.title || 'Carousel', hasMediaAttachment: false },
body: { text: (message as any).text || carousel.description || '' },
footer: carousel.footer ? { text: carousel.footer } : undefined,
carouselMessage: {
cards: carousel.cards.map((card: any) => ({
header: card.header,
body: card.body,
footer: card.footer,
nativeFlowMessage: card.nativeFlowMessage
})),
messageVersion: carousel.messageVersion || 1
}
}
m.interactiveMessage = interactiveMessage
options.logger?.warn('[EXPERIMENTAL] Sending carouselMessage - this may not work and can cause bans')
} else if (hasNonNullishProperty(message, 'text')) {
// Normal text message processing
const extContent = { text: message.text } as WATextMessage
let urlInfo = message.linkPreview
@@ -513,108 +595,7 @@ export const generateWAMessageContent = async (
break
}
}
// ⚠️ EXPERIMENTAL: Interactive messages - may not work and can cause account bans
// Process buttons for text messages
else if (hasNonNullishProperty(message, 'text') && hasNonNullishProperty(message, 'buttons')) {
const buttonsMessage: proto.Message.IButtonsMessage = {
contentText: (message as any).text,
footerText: (message as any).footerText,
headerType: (message as any).headerType || proto.Message.ButtonsMessage.HeaderType.EMPTY
}
// Add buttons
buttonsMessage.buttons = ((message as any).buttons as any[]).map((btn: any, idx: number) => ({
buttonId: btn.buttonId || `btn_${idx}`,
buttonText: { displayText: btn.buttonText?.displayText || btn.displayText || btn.text },
type: btn.type || proto.Message.ButtonsMessage.Button.Type.RESPONSE
}))
m.buttonsMessage = buttonsMessage
options.logger?.warn('[EXPERIMENTAL] Sending buttonsMessage - this may not work and can cause bans')
}
// Process templateButtons
else if (hasNonNullishProperty(message, 'text') && hasNonNullishProperty(message, 'templateButtons')) {
const templateMessage: proto.Message.ITemplateMessage = {
hydratedTemplate: {
hydratedContentText: (message as any).text,
hydratedFooterText: (message as any).footer
}
}
// Add template buttons
templateMessage.hydratedTemplate!.hydratedButtons = ((message as any).templateButtons as any[]).map((btn: any) => {
if (btn.quickReplyButton) {
return {
index: btn.index,
quickReplyButton: btn.quickReplyButton
}
} else if (btn.urlButton) {
return {
index: btn.index,
urlButton: btn.urlButton
}
} else if (btn.callButton) {
return {
index: btn.index,
callButton: btn.callButton
}
}
return btn
})
m.templateMessage = templateMessage
options.logger?.warn('[EXPERIMENTAL] Sending templateMessage - this may not work and can cause bans')
}
// Process list messages
else if (hasNonNullishProperty(message, 'sections')) {
const listMessage: proto.Message.IListMessage = {
title: (message as any).title,
description: (message as any).text,
buttonText: (message as any).buttonText || 'View options',
footerText: (message as any).footerText,
listType: proto.Message.ListMessage.ListType.SINGLE_SELECT
}
// Add sections
listMessage.sections = ((message as any).sections as any[]).map((section: any) => ({
title: section.title,
rows: section.rows.map((row: any) => ({
rowId: row.rowId || row.id,
title: row.title,
description: row.description
}))
}))
m.listMessage = listMessage
options.logger?.warn('[EXPERIMENTAL] Sending listMessage - this may not work and can cause bans')
}
// Process carousel/interactive messages
else if (hasNonNullishProperty(message, 'carousel')) {
const carousel = (message as any).carousel
const interactiveMessage: proto.Message.IInteractiveMessage = {
header: carousel.header || {
title: carousel.title || 'Carousel',
hasMediaAttachment: false
},
body: {
text: (message as any).text || carousel.description || ''
},
footer: carousel.footer ? { text: carousel.footer } : undefined,
carouselMessage: {
cards: carousel.cards.map((card: any) => ({
header: card.header,
body: card.body,
footer: card.footer,
nativeFlowMessage: card.nativeFlowMessage
})),
messageVersion: carousel.messageVersion || 1
}
}
m.interactiveMessage = interactiveMessage
options.logger?.warn('[EXPERIMENTAL] Sending carouselMessage - this may not work and can cause bans')
}
// Process buttons with media (image/video)
// ⚠️ EXPERIMENTAL: Process buttons with media (image/video)
else if (
(hasNonNullishProperty(message, 'image') || hasNonNullishProperty(message, 'video')) &&
hasNonNullishProperty(message, 'buttons')