refactor: clean interactive message logs

refactor: clean interactive message logs
This commit is contained in:
Renato Alcara
2026-02-21 02:06:36 -03:00
committed by GitHub
parent 16a0d5b163
commit 89fd500f86
6 changed files with 41 additions and 81 deletions
+1 -4
View File
@@ -85,10 +85,7 @@ export const DEFAULT_CONNECTION_CONFIG: SocketConfig = {
// Enable automatic recovery of Click-to-WhatsApp ads messages
// These arrive as "placeholder messages" and need to be requested from the phone
enableCTWARecovery: true,
// ⚠️ EXPERIMENTAL: Enable interactive messages (buttons, lists, templates, carousel)
// WARNING: These features may not work and can cause account BANS
// Use ONLY for testing with DISPOSABLE accounts in DEV environment
// Default: true (for dev/testing) - set to false in production
// Enable interactive messages (buttons, lists, templates, carousel)
enableInteractiveMessages: true,
options: {},
appStateMacVerification: {
+24 -43
View File
@@ -622,9 +622,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
return { nodes, shouldIncludeDeviceIdentity }
}
// ⚠️ EXPERIMENTAL: Functions to detect and handle interactive messages
// These features may not work and can cause account bans
// Based on Itsukichan/Baileys and baileys_helpers implementation
// Interactive message detection and binary node injection
/**
* Detects the type of interactive message and returns the appropriate binary node tag
@@ -1247,8 +1245,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
content: binaryNodeContent
}
// ⚠️ EXPERIMENTAL: Inject 'biz' node for interactive messages
// This may not work and can cause account bans
// Inject 'biz' node for interactive messages
const buttonType = getButtonType(message)
const isCatalog = isCatalogMessage(message)
const isCarousel = isCarouselMessage(message)
@@ -1281,29 +1278,20 @@ export const makeMessagesSocket = (config: SocketConfig) => {
const isListDetected = isListNativeFlow(message)
// Log full button details including buttonParamsJson for debugging
const buttonDetails = nativeFlowButtons.map((b: any) => ({
name: b?.name,
paramsJson: b?.buttonParamsJson ? JSON.parse(b.buttonParamsJson) : null
}))
logger.info(
{
msgId,
buttonType,
to: destinationJid,
hasListMessage: !!listMsg,
hasInteractiveMessage: !!interactiveMsg,
hasNativeFlow: !!interactiveMsg?.nativeFlowMessage,
nativeFlowButtonNames: nativeFlowButtons.map((b: any) => b?.name),
buttonDetails: JSON.stringify(buttonDetails, null, 2),
isListDetected
isListDetected,
isCatalog,
isCarousel
},
'[DEBUG] Interactive message structure'
)
logger.warn(
{ msgId, buttonType, to: destinationJid, enableInteractiveMessages, isCatalog },
'[EXPERIMENTAL] Injecting biz node for interactive message - may cause ban'
'[Interactive] Preparing biz node'
)
// Track that we're sending an interactive message
@@ -1333,10 +1321,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
}
]
})
logger.info(
{ msgId, to: destinationJid },
'[EXPERIMENTAL] Injected biz node for listMessage (legacy format)'
)
logger.info({ msgId, to: destinationJid }, '[BIZ NODE] Injected biz > list (product_list, v=2)')
} else {
const SPECIAL_FLOW_NAMES: Record<string, string> = {
review_and_pay: 'payment_info',
@@ -1349,7 +1334,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
logger.info(
{ msgId, buttonNames: allButtonNames, hasCTA, hasQuickReply, isCTAOnly, nativeFlowName },
'[EXPERIMENTAL] Determined native_flow name based on button types'
'[BIZ NODE] Injected biz > interactive(native_flow, v=1) > native_flow(v=9, name=' + nativeFlowName + ')'
)
const interactiveType = 'native_flow'
@@ -1389,38 +1374,26 @@ export const makeMessagesSocket = (config: SocketConfig) => {
tag: 'bot',
attrs: { biz_bot: '1' }
})
logger.debug(
{ msgId, to: destinationJid },
'[EXPERIMENTAL] Added bot node for private chat interactive message'
)
logger.info({ msgId, to: destinationJid }, '[BOT NODE] Added bot node (biz_bot=1)')
} else if (isNativeFlowButtons) {
logger.debug(
{ msgId, to: destinationJid, buttonNames: allButtonNames },
'[EXPERIMENTAL] Skipping bot node for native_flow buttons (Web/Desktop compatibility)'
)
logger.debug({ msgId, to: destinationJid }, '[BOT NODE] Skipped — native_flow (Web compatibility)')
} else if (isCarousel) {
logger.debug(
{ msgId, to: destinationJid, buttonType },
'[EXPERIMENTAL] Skipping bot node for carousel message'
)
logger.debug({ msgId, to: destinationJid }, '[BOT NODE] Skipped — carousel message')
} else if (isCatalog) {
logger.debug(
{ msgId, to: destinationJid, buttonType },
'[EXPERIMENTAL] Skipping bot node for catalog message (with biz node)'
)
logger.debug({ msgId, to: destinationJid }, '[BOT NODE] Skipped — catalog message')
}
// Track success and latency after message is sent
metrics.interactiveMessagesSuccess.inc({ type: buttonType })
metrics.interactiveMessagesLatency.observe({ type: buttonType }, Date.now() - startTime)
} catch (error) {
logger.error({ error, msgId, buttonType }, '[EXPERIMENTAL] Failed to inject biz node for interactive message')
logger.error({ error, msgId, buttonType }, '[BIZ NODE] Failed to inject biz node')
metrics.interactiveMessagesFailures.inc({ type: buttonType, reason: 'injection_failed' })
}
} else if (buttonType && !enableInteractiveMessages) {
logger.warn(
{ msgId, buttonType },
'[EXPERIMENTAL] Interactive message detected but feature disabled (enableInteractiveMessages=false)'
'[Interactive] Message detected but feature disabled (enableInteractiveMessages=false)'
)
metrics.interactiveMessagesFailures.inc({ type: buttonType, reason: 'feature_disabled' })
}
@@ -1499,7 +1472,9 @@ export const makeMessagesSocket = (config: SocketConfig) => {
// Opportunistic cleanup: remove expired token from store
try {
await authState.keys.set({ tctoken: { [tcTokenJid]: null } })
} catch { /* ignore cleanup errors */ }
} catch {
/* ignore cleanup errors */
}
}
// If tctoken is missing for a 1:1 send, fire-and-forget fetch so the
@@ -1544,6 +1519,12 @@ export const makeMessagesSocket = (config: SocketConfig) => {
;(stanza.content as BinaryNode[]).push(...deferredNodes)
}
// Log stanza structure for interactive messages
if (buttonType || isCarousel) {
const contentTags = Array.isArray(stanza.content) ? stanza.content.map((n: BinaryNode) => n.tag) : []
logger.info({ msgId, to: destinationJid, contentTags }, '[STANZA] Content tags: ' + JSON.stringify(contentTags))
}
logger.debug({ msgId }, `sending message to ${participants.length} devices`)
// ======= PROTOBUF ROUNDTRIP TEST: Verify encoding preserves carousel =======
+1 -3
View File
@@ -281,9 +281,7 @@ export type WASendableProduct = Omit<proto.Message.ProductMessage.IProductSnapsh
productImage: WAMediaUpload
}
// ⚠️ EXPERIMENTAL: Interactive message types
// These features may not work and can cause account bans
// Use only for testing with disposable accounts
// Interactive message types
export type ButtonInfo = {
buttonId: string
+3 -18
View File
@@ -135,24 +135,9 @@ export type SocketConfig = {
enableCTWARecovery: boolean
/**
* ⚠️ EXPERIMENTAL: Enable interactive messages (buttons, lists, templates, carousel).
*
* **WARNING**: These features MAY NOT WORK and can cause ACCOUNT BANS.
*
* WhatsApp actively blocks non-business accounts from sending interactive messages.
* Using this feature can result in:
* - Messages not being delivered
* - Temporary account restrictions
* - Permanent account bans
*
* Use ONLY for:
* - Testing in DEV environment
* - DISPOSABLE test accounts
* - Experimental research
*
* @default true (for dev/testing)
* @see https://github.com/WhiskeySockets/Baileys/issues/56
* @see https://github.com/WhiskeySockets/Baileys/issues/25
* Enable interactive messages (buttons, lists, templates, carousel).
* When true, injects biz/bot binary nodes required by WhatsApp.
* @default true
*/
enableInteractiveMessages: boolean
+6 -7
View File
@@ -1287,8 +1287,7 @@ export const generateWAMessageContent = async (
m.viewOnceMessage = generated.viewOnceMessage
options.logger?.info('Sending productCarouselMessage with viewOnceMessage wrapper')
}
// ⚠️ EXPERIMENTAL: Check for interactive messages FIRST (buttons, lists, templates)
// These use the older API which may not work reliably
// Interactive messages: buttons, lists, templates
else if (hasNonNullishProperty(message, 'text') && hasNonNullishProperty(message, 'buttons')) {
// Process buttons for text messages
const buttonsMessage: proto.Message.IButtonsMessage = {
@@ -1304,7 +1303,7 @@ export const generateWAMessageContent = async (
}))
m.buttonsMessage = buttonsMessage
options.logger?.warn('[EXPERIMENTAL] Sending buttonsMessage - this may not work and can cause bans')
options.logger?.info('[Interactive] Sending buttonsMessage (legacy format)')
} else if (hasNonNullishProperty(message, 'text') && hasNonNullishProperty(message, 'templateButtons')) {
// Process templateButtons
const hydratedButtons = ((message as any).templateButtons as any[]).map((btn: any) => {
@@ -1328,7 +1327,7 @@ export const generateWAMessageContent = async (
}
m.templateMessage = templateMessage
options.logger?.warn('[EXPERIMENTAL] Sending templateMessage - this may not work and can cause bans')
options.logger?.info('[Interactive] Sending templateMessage')
} else if (hasNonNullishProperty(message, 'sections')) {
// Process list messages - validate limits
validateListSections((message as any).sections, (message as any).buttonText)
@@ -1350,7 +1349,7 @@ export const generateWAMessageContent = async (
}))
m.listMessage = listMessage
options.logger?.warn('[EXPERIMENTAL] Sending listMessage - this may not work and can cause bans')
options.logger?.info({ sections: listMessage.sections?.length || 0 }, '[Interactive] Sending listMessage (sections: ' + (listMessage.sections?.length || 0) + ')')
} else if (hasNonNullishProperty(message, 'carousel')) {
// Process carousel/interactive messages with viewOnceMessage wrapper
const carousel = (message as any).carousel
@@ -1530,7 +1529,7 @@ export const generateWAMessageContent = async (
break
}
}
// ⚠️ EXPERIMENTAL: Process buttons with media (image/video)
// Process buttons with media (image/video)
else if (
(hasNonNullishProperty(message, 'image') || hasNonNullishProperty(message, 'video')) &&
hasNonNullishProperty(message, 'buttons')
@@ -1583,7 +1582,7 @@ export const generateWAMessageContent = async (
}))
m.buttonsMessage = buttonsMessage
options.logger?.warn('[EXPERIMENTAL] Sending buttonsMessage with media - this may not work and can cause bans')
options.logger?.info('[Interactive] Sending buttonsMessage with media')
} else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options)
m.ptvMessage = videoMessage
+1 -1
View File
@@ -1444,7 +1444,7 @@ export const metrics = {
new Counter('ctwa_recovery_failures_total', 'Total CTWA recovery failures', ['reason'])
),
// ========== Interactive Messages Metrics (EXPERIMENTAL) ==========
// ========== Interactive Messages Metrics ==========
interactiveMessagesSent: baileysMetrics.register(
new Counter(
'interactive_messages_sent_total',