diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index 2484cbdf..66775fd6 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -628,8 +628,12 @@ export const makeMessagesSocket = (config: SocketConfig) => { } else if (message.templateMessage) { return 'template' } else if (message.listMessage) { + (SINGLE_SELECT and PRODUCT_LIST) need biz > list node + return 'list' + // listMessage works natively without biz node return undefined + } else if (message.buttonsResponseMessage) { return 'buttons_response' } else if (message.listResponseMessage) { @@ -671,8 +675,13 @@ export const makeMessagesSocket = (config: SocketConfig) => { } else if (innerMessage.templateMessage) { return 'template' } else if (innerMessage.listMessage) { + + // All listMessages (SINGLE_SELECT and PRODUCT_LIST) need biz > list node + return 'list' + // listMessage works natively without biz node return undefined + } else if (innerMessage.buttonsResponseMessage) { return 'buttons_response' } else if (innerMessage.listResponseMessage) { @@ -1199,6 +1208,13 @@ export const makeMessagesSocket = (config: SocketConfig) => { '[DEBUG] Interactive message structure' ) + // Log message check for biz node (similar to Pastorini) + const messageKeys = Object.keys(message).filter(k => (message as any)[k] != null) + logger.info( + { hasInteractive: !!interactiveMsg, messageKeys }, + '[BIZ NODE] Checking message for biz node...' + ) + logger.warn( { msgId, buttonType, to: destinationJid, enableInteractiveMessages, isCatalog }, '[EXPERIMENTAL] Injecting biz node for interactive message - may cause ban' @@ -1208,9 +1224,20 @@ export const makeMessagesSocket = (config: SocketConfig) => { metrics.interactiveMessagesSent.inc({ type: buttonType }) try { + + // Differentiate biz node structure based on message type + // isListMessage: true for direct listMessage OR nativeFlowMessage with single_select + const isListMessage = buttonType === 'list' || isListNativeFlow(message) + + if (isListMessage) { + // For listMessage: use biz > list (type=product_list, v=2) + // This is the format that works on both phone and web WhatsApp + // Reference: Pastorini implementation +======= // For listMessage (legacy format), use direct tag // This matches pastorini's working implementation if (buttonType === 'list') { + ;(stanza.content as BinaryNode[]).push({ tag: 'biz', attrs: {}, @@ -1226,6 +1253,13 @@ export const makeMessagesSocket = (config: SocketConfig) => { }) logger.info( { msgId, to: destinationJid }, + + '[BIZ NODE] Added list biz node (type=product_list, v=2) for listMessage' + ) + } else { + // For other interactive messages (native_flow, buttons, templates, carousels): + // use biz > interactive > native_flow +======= '[EXPERIMENTAL] Injected biz node for listMessage (legacy format)' ) } else { @@ -1258,8 +1292,8 @@ export const makeMessagesSocket = (config: SocketConfig) => { // 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 - // IMPORTANT: Carousels and catalog messages should NOT have bot node - // as they are regular interactive messages, not bot messages + // IMPORTANT: Carousels, catalog messages, and list messages should NOT have bot node + // List messages use biz > list structure and don't need bot node // NOTE: Only for regular JIDs, NOT hosted JIDs (to avoid interference with interactive messages) const isPrivateUserChat = ( isPnUser(destinationJid) || @@ -1267,7 +1301,7 @@ export const makeMessagesSocket = (config: SocketConfig) => { destinationJid?.endsWith('@c.us') ) && !isJidBot(destinationJid) - if (isPrivateUserChat && !isCarousel && !isCatalog) { + if (isPrivateUserChat && !isCarousel && !isCatalog && !isListMessage) { ;(stanza.content as BinaryNode[]).push({ tag: 'bot', attrs: { biz_bot: '1' } @@ -1286,6 +1320,11 @@ export const makeMessagesSocket = (config: SocketConfig) => { { msgId, to: destinationJid, buttonType }, '[EXPERIMENTAL] Skipping bot node for catalog message (with biz node)' ) + } else if (isListMessage) { + logger.debug( + { msgId, to: destinationJid, buttonType }, + '[BIZ NODE] Skipping bot node for list message (uses list biz node)' + ) } // Track success and latency after message is sent @@ -1374,6 +1413,10 @@ export const makeMessagesSocket = (config: SocketConfig) => { ;(stanza.content as BinaryNode[]).push(...additionalNodes) } + // Log stanza content tags for debugging (similar to Pastorini format) + const contentTags = (stanza.content as BinaryNode[]).map((n: BinaryNode) => n.tag) + logger.info({ msgId, contentTags }, `[STANZA] Content tags: ${JSON.stringify(contentTags)}`) + logger.debug({ msgId }, `sending message to ${participants.length} devices`) await sendNode(stanza)