refactor: clean interactive message logs
refactor: clean interactive message logs
This commit is contained in:
@@ -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
@@ -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 =======
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user