fix: Use legacy listMessage format with correct biz node to fix error…

fix: Use legacy listMessage format with correct biz node to fix error…
This commit is contained in:
Renato Alcara
2026-02-05 23:07:44 -03:00
committed by GitHub
2 changed files with 62 additions and 62 deletions
+50 -52
View File
@@ -628,11 +628,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
} else if (message.templateMessage) { } else if (message.templateMessage) {
return 'template' return 'template'
} else if (message.listMessage) { } else if (message.listMessage) {
// Check if it's a product list (uses PRODUCT_LIST type) // All listMessage types need biz node with type="product_list"
// Product lists from WhatsApp Business catalog don't need biz node injection // This includes SINGLE_SELECT and PRODUCT_LIST
if (message.listMessage.listType === proto.Message.ListMessage.ListType.PRODUCT_LIST) {
return undefined // No biz node needed for catalog product lists
}
return 'list' return 'list'
} else if (message.buttonsResponseMessage) { } else if (message.buttonsResponseMessage) {
return 'buttons_response' return 'buttons_response'
@@ -643,15 +640,6 @@ export const makeMessagesSocket = (config: SocketConfig) => {
} else if (message.interactiveMessage) { } else if (message.interactiveMessage) {
// Check if it has nativeFlowMessage (modern format) // Check if it has nativeFlowMessage (modern format)
if (message.interactiveMessage.nativeFlowMessage) { if (message.interactiveMessage.nativeFlowMessage) {
// Check if it's a list (single_select or multi_select)
// Lists don't need biz node injection, similar to product lists
const buttons = message.interactiveMessage.nativeFlowMessage.buttons || []
const isListButton = buttons.some(
(btn: any) => btn?.name === 'single_select' || btn?.name === 'multi_select'
)
if (isListButton) {
return undefined // No biz node for list messages
}
return 'native_flow' return 'native_flow'
} }
// Check if it's a carousel with nativeFlowMessage buttons in cards // Check if it's a carousel with nativeFlowMessage buttons in cards
@@ -684,10 +672,8 @@ export const makeMessagesSocket = (config: SocketConfig) => {
} else if (innerMessage.templateMessage) { } else if (innerMessage.templateMessage) {
return 'template' return 'template'
} else if (innerMessage.listMessage) { } else if (innerMessage.listMessage) {
// Product lists from WhatsApp Business catalog don't need biz node injection // All listMessage types need biz node with type="product_list"
if (innerMessage.listMessage.listType === proto.Message.ListMessage.ListType.PRODUCT_LIST) { // This includes SINGLE_SELECT and PRODUCT_LIST
return undefined // No biz node needed for catalog product lists
}
return 'list' return 'list'
} else if (innerMessage.buttonsResponseMessage) { } else if (innerMessage.buttonsResponseMessage) {
return 'buttons_response' return 'buttons_response'
@@ -698,15 +684,6 @@ export const makeMessagesSocket = (config: SocketConfig) => {
} else if (innerMessage.interactiveMessage) { } else if (innerMessage.interactiveMessage) {
// Check if it has nativeFlowMessage (modern format) // Check if it has nativeFlowMessage (modern format)
if (innerMessage.interactiveMessage.nativeFlowMessage) { if (innerMessage.interactiveMessage.nativeFlowMessage) {
// Check if it's a list (single_select or multi_select)
// Lists don't need biz node injection, similar to product lists
const buttons = innerMessage.interactiveMessage.nativeFlowMessage.buttons || []
const isListButton = buttons.some(
(btn: any) => btn?.name === 'single_select' || btn?.name === 'multi_select'
)
if (isListButton) {
return undefined // No biz node for list messages
}
return 'native_flow' return 'native_flow'
} }
// Check if it's a carousel with nativeFlowMessage buttons in cards // Check if it's a carousel with nativeFlowMessage buttons in cards
@@ -1233,32 +1210,53 @@ export const makeMessagesSocket = (config: SocketConfig) => {
metrics.interactiveMessagesSent.inc({ type: buttonType }) metrics.interactiveMessagesSent.inc({ type: buttonType })
try { try {
// Use nested structure: biz > interactive > native_flow // For listMessage (legacy format), use direct <list> tag
// For buttons, carousels, and other interactive messages // This matches pastorini's working implementation
// NOTE: Lists now skip biz node injection (handled in getButtonType) if (buttonType === 'list') {
const interactiveType = 'native_flow' ;(stanza.content as BinaryNode[]).push({
;(stanza.content as BinaryNode[]).push({ tag: 'biz',
tag: 'biz', attrs: {},
attrs: {}, content: [
content: [ {
{ tag: 'list',
tag: 'interactive', attrs: {
attrs: { type: 'product_list',
type: interactiveType, v: '2'
v: '1'
},
content: [
{
tag: interactiveType,
attrs: {
v: '9',
name: 'mixed'
}
} }
] }
} ]
] })
}) logger.info(
{ msgId, to: destinationJid },
'[EXPERIMENTAL] Injected biz node for listMessage (legacy format)'
)
} else {
// Use nested structure: biz > interactive > native_flow
// For buttons, carousels, and other interactive messages
const interactiveType = 'native_flow'
;(stanza.content as BinaryNode[]).push({
tag: 'biz',
attrs: {},
content: [
{
tag: 'interactive',
attrs: {
type: interactiveType,
v: '1'
},
content: [
{
tag: interactiveType,
attrs: {
v: '9',
name: 'mixed'
}
}
]
}
]
})
}
// For private 1:1 chats, add bot node (required for some interactive messages to render) // For private 1:1 chats, add bot node (required for some interactive messages to render)
// Only inject for actual user JIDs, not broadcasts, newsletters, or Meta AI bots // Only inject for actual user JIDs, not broadcasts, newsletters, or Meta AI bots
+12 -10
View File
@@ -1076,16 +1076,18 @@ export const generateWAMessageContent = async (
// Check for nativeList // Check for nativeList
else if (hasNonNullishProperty(message, 'nativeList')) { else if (hasNonNullishProperty(message, 'nativeList')) {
const listMsg = message as any const listMsg = message as any
const listOptions: ListMessageOptions = { // Use LEGACY format to avoid error 479
buttonText: listMsg.nativeList.buttonText, // Modern format (interactiveMessage) causes rejection
sections: listMsg.nativeList.sections, // Legacy format (listMessage) works on all platforms
text: listMsg.text || '', const generated = generateListMessageLegacy(
title: listMsg.title, { sections: listMsg.nativeList.sections },
footer: listMsg.footer listMsg.title || listMsg.text || '',
} listMsg.text || '',
const generated = generateListMessage(listOptions) listMsg.nativeList.buttonText,
m.viewOnceMessage = generated.viewOnceMessage listMsg.footer
options.logger?.info('Sending listMessage with viewOnceMessage wrapper') )
m.listMessage = generated.listMessage
options.logger?.info('Sending listMessage with LEGACY format (fixes error 479)')
} }
// Check for productList (multi-product message from catalog) // Check for productList (multi-product message from catalog)
else if (hasNonNullishProperty(message, 'productList')) { else if (hasNonNullishProperty(message, 'productList')) {