feat: add automatic CTWA (Click-to-WhatsApp) ads message recovery
Messages from Facebook/Instagram ads (Click-to-WhatsApp) don't arrive on linked devices because Meta's ads endpoint doesn't encrypt for multi-device. They arrive as "Message absent from node" placeholders. This change automatically requests the message from the primary phone via PDO (Peer Data Operation) when a CTWA placeholder is detected. Changes: - Add enableCTWARecovery config option (default: true) - Trigger requestPlaceholderResend() for "Message absent from node" errors - Add Prometheus metrics for CTWA recovery tracking - Add comprehensive unit tests for CTWA recovery functionality Resolves: https://github.com/WhiskeySockets/Baileys/issues/1723 Resolves: https://github.com/WhiskeySockets/Baileys/issues/1034
This commit is contained in:
@@ -16,6 +16,7 @@ import type {
|
||||
WAMessage,
|
||||
WAMessageKey
|
||||
} from '../Types'
|
||||
import { metrics } from './prometheus-metrics.js'
|
||||
import { WAMessageStubType } from '../Types'
|
||||
import { getContentType, normalizeMessageContent } from '../Utils/messages'
|
||||
import {
|
||||
@@ -402,11 +403,24 @@ const processMessage = async (
|
||||
await placeholderResendCache?.del(response.stanzaId!)
|
||||
// TODO: IMPLEMENT HISTORY SYNC ETC (sticker uploads etc.).
|
||||
const { peerDataOperationResult } = response
|
||||
let recoveredCount = 0
|
||||
for (const result of peerDataOperationResult!) {
|
||||
const { placeholderMessageResendResponse: retryResponse } = result
|
||||
//eslint-disable-next-line max-depth
|
||||
if (retryResponse) {
|
||||
const webMessageInfo = proto.WebMessageInfo.decode(retryResponse.webMessageInfoBytes!)
|
||||
|
||||
// Track CTWA message recovery success
|
||||
recoveredCount++
|
||||
logger?.info(
|
||||
{
|
||||
msgId: webMessageInfo.key?.id,
|
||||
remoteJid: webMessageInfo.key?.remoteJid,
|
||||
requestId: response.stanzaId
|
||||
},
|
||||
'CTWA: Successfully recovered message via placeholder resend'
|
||||
)
|
||||
|
||||
// wait till another upsert event is available, don't want it to be part of the PDO response message
|
||||
// TODO: parse through proper message handling utilities (to add relevant key fields)
|
||||
ev.emit('messages.upsert', {
|
||||
@@ -416,6 +430,16 @@ const processMessage = async (
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Update metrics for recovered messages
|
||||
if (recoveredCount > 0) {
|
||||
metrics.ctwaMessagesRecovered.inc(recoveredCount)
|
||||
metrics.ctwaRecoveryRequests.inc({ status: 'success' })
|
||||
logger?.debug(
|
||||
{ recoveredCount, requestId: response.stanzaId },
|
||||
'CTWA: Placeholder resend response processed'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
@@ -1385,6 +1385,24 @@ export const metrics = {
|
||||
new Gauge('messages_queued', 'Current number of messages in queue', ['priority'])
|
||||
),
|
||||
|
||||
// ========== CTWA (Click-to-WhatsApp Ads) Metrics ==========
|
||||
/**
|
||||
* Messages from Facebook/Instagram ads that arrive as placeholder messages
|
||||
* and are recovered via PDO (Peer Data Operation) request
|
||||
*/
|
||||
ctwaRecoveryRequests: baileysMetrics.register(
|
||||
new Counter('ctwa_recovery_requests_total', 'Total CTWA placeholder resend requests', ['status'])
|
||||
),
|
||||
ctwaMessagesRecovered: baileysMetrics.register(
|
||||
new Counter('ctwa_messages_recovered_total', 'Total CTWA messages successfully recovered')
|
||||
),
|
||||
ctwaRecoveryLatency: baileysMetrics.register(
|
||||
new Histogram('ctwa_recovery_latency_ms', 'CTWA message recovery latency in ms', [], [500, 1000, 2000, 3000, 5000, 8000, 10000])
|
||||
),
|
||||
ctwaRecoveryFailures: baileysMetrics.register(
|
||||
new Counter('ctwa_recovery_failures_total', 'Total CTWA recovery failures', ['reason'])
|
||||
),
|
||||
|
||||
// ========== Media Metrics ==========
|
||||
mediaUploads: baileysMetrics.register(
|
||||
new Counter('media_uploads_total', 'Total media uploads', ['type', 'status'])
|
||||
|
||||
Reference in New Issue
Block a user