refactor: clean interactive message logs
refactor: clean interactive message logs
This commit is contained in:
+29
-48
@@ -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' })
|
||||
}
|
||||
@@ -1493,18 +1466,20 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
let tcTokenBuffer = existingTokenEntry?.token
|
||||
|
||||
// Treat expired tokens the same as missing — re-fetch from server
|
||||
if(tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
|
||||
if (tcTokenBuffer?.length && isTcTokenExpired(existingTokenEntry?.timestamp)) {
|
||||
logTcToken('expired', { jid: destinationJid, timestamp: existingTokenEntry?.timestamp })
|
||||
tcTokenBuffer = undefined
|
||||
// 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
|
||||
// retry path (error 463 → handleBadAck) can pick it up on resend
|
||||
if(!tcTokenBuffer?.length && is1on1Send && !tcTokenFetchingJids.has(tcTokenJid)) {
|
||||
if (!tcTokenBuffer?.length && is1on1Send && !tcTokenFetchingJids.has(tcTokenJid)) {
|
||||
tcTokenFetchingJids.add(tcTokenJid)
|
||||
logTcToken('fetch', { jid: destinationJid })
|
||||
getPrivacyTokens([destinationJid])
|
||||
@@ -1525,7 +1500,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
})
|
||||
}
|
||||
|
||||
if(tcTokenBuffer?.length) {
|
||||
if (tcTokenBuffer?.length) {
|
||||
;(stanza.content as BinaryNode[]).push({
|
||||
tag: 'tctoken',
|
||||
attrs: {},
|
||||
@@ -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 =======
|
||||
@@ -1615,7 +1596,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
|
||||
// Fire-and-forget: issue our token to the contact (like WA Web's sendTcToken)
|
||||
// Only for 1:1 sends where we already have a token, and when bucket boundary crossed
|
||||
if(is1on1Send && tcTokenBuffer?.length && shouldSendNewTcToken(existingTokenEntry?.senderTimestamp)) {
|
||||
if (is1on1Send && tcTokenBuffer?.length && shouldSendNewTcToken(existingTokenEntry?.senderTimestamp)) {
|
||||
const issueTimestamp = unixTimestampSeconds()
|
||||
logTcToken('reissue', { jid: destinationJid })
|
||||
// WA Web writes senderTimestamp only AFTER the IQ succeeds
|
||||
@@ -1635,7 +1616,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
// Re-read entry to avoid overwriting concurrent notification handler updates
|
||||
const currentData = await authState.keys.get('tctoken', [tcTokenJid])
|
||||
const currentEntry = currentData[tcTokenJid]
|
||||
if(currentEntry?.token?.length) {
|
||||
if (currentEntry?.token?.length) {
|
||||
await authState.keys.set({
|
||||
tctoken: {
|
||||
[tcTokenJid]: {
|
||||
|
||||
Reference in New Issue
Block a user