fix: request view-once content via PDO from primary phone
When receiving <unavailable type='view_once'/>, attempt to request the actual media content from the primary phone via PDO (Peer Data Operation / PLACEHOLDER_MESSAGE_RESEND). If the phone responds, the full media (url, mediaKey, directPath) arrives via messages.upsert, allowing consumers to display the image. Falls back to placeholder if PDO fails or phone doesn't respond. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+44
-10
@@ -2241,25 +2241,34 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleMessage = async (node: BinaryNode) => {
|
const handleMessage = async (node: BinaryNode) => {
|
||||||
|
const _encNode = getBinaryNodeChild(node, 'enc')
|
||||||
|
const _unavNode = getBinaryNodeChild(node, 'unavailable')
|
||||||
|
// VO_RAW: log BEFORE any filtering — captures every stanza that enters handleMessage
|
||||||
|
console.log(`[VO_RAW] id=${node.attrs.id} from=${node.attrs.from} type=${node.attrs.type} enc=${_encNode ? _encNode.attrs?.type : 'NO'} unavailable=${_unavNode ? _unavNode.attrs?.type : 'NO'} children=${Array.isArray(node.content) ? (node.content as BinaryNode[]).map(c => c.tag).join(',') : 'none'}`)
|
||||||
|
|
||||||
if (shouldIgnoreJid(node.attrs.from!) && node.attrs.from !== S_WHATSAPP_NET) {
|
if (shouldIgnoreJid(node.attrs.from!) && node.attrs.from !== S_WHATSAPP_NET) {
|
||||||
|
console.log(`[VO_RAW] IGNORED by shouldIgnoreJid: ${node.attrs.from}`)
|
||||||
logger.trace({ from: node.attrs.from }, 'ignored message')
|
logger.trace({ from: node.attrs.from }, 'ignored message')
|
||||||
// Send a clean ACK (no error code) so the server considers the
|
|
||||||
// message delivered. Using error 500 (UnhandledError) previously
|
|
||||||
// caused the server to retry delivery, generating duplicate traffic.
|
|
||||||
await sendMessageAck(node)
|
await sendMessageAck(node)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const encNode = getBinaryNodeChild(node, 'enc')
|
const encNode = getBinaryNodeChild(node, 'enc')
|
||||||
// msmsg block removed — allow msmsg decryption (required for view-once on linked devices)
|
|
||||||
|
|
||||||
// Handle view-once unavailable sync from primary device.
|
|
||||||
// When the primary device sends a view-once, linked companions receive
|
|
||||||
// Handle view-once unavailable sync from primary device.
|
|
||||||
// Emit a viewOnceMessage placeholder so consumers can display it.
|
|
||||||
const unavailableNode = getBinaryNodeChild(node, 'unavailable')
|
const unavailableNode = getBinaryNodeChild(node, 'unavailable')
|
||||||
|
|
||||||
|
// DEBUG: log every incoming message stanza to trace view-once delivery
|
||||||
|
logger.info({
|
||||||
|
id: node.attrs.id,
|
||||||
|
from: node.attrs.from,
|
||||||
|
type: node.attrs.type,
|
||||||
|
hasEnc: !!encNode,
|
||||||
|
encType: encNode?.attrs?.type,
|
||||||
|
hasUnavailable: !!unavailableNode,
|
||||||
|
unavailableType: unavailableNode?.attrs?.type,
|
||||||
|
}, 'VO_TRACE: incoming message stanza')
|
||||||
|
|
||||||
|
// msmsg block removed — allow msmsg decryption (required for view-once on linked devices)
|
||||||
if (unavailableNode?.attrs?.type === 'view_once') {
|
if (unavailableNode?.attrs?.type === 'view_once') {
|
||||||
logger.debug({ id: node.attrs.id, from: node.attrs.from }, 'received view_once unavailable — emitting placeholder')
|
|
||||||
const { fullMessage: voMsg } = decryptMessageNode(
|
const { fullMessage: voMsg } = decryptMessageNode(
|
||||||
node,
|
node,
|
||||||
authState.creds.me!.id,
|
authState.creds.me!.id,
|
||||||
@@ -2268,6 +2277,31 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
|||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
voMsg.key.isViewOnce = true
|
voMsg.key.isViewOnce = true
|
||||||
|
|
||||||
|
// Try to request the actual view-once content from the primary phone via PDO
|
||||||
|
// The phone may resend the full media (url, mediaKey, directPath, etc.)
|
||||||
|
console.log(`[VO_PDO] Requesting view-once content via PDO for ${voMsg.key.id} from ${voMsg.key.remoteJid}`)
|
||||||
|
try {
|
||||||
|
const requestId = await requestPlaceholderResend(voMsg.key, {
|
||||||
|
key: { ...voMsg.key },
|
||||||
|
pushName: voMsg.pushName,
|
||||||
|
messageTimestamp: voMsg.messageTimestamp,
|
||||||
|
participant: voMsg.key.participant,
|
||||||
|
participantAlt: voMsg.key.participantAlt
|
||||||
|
})
|
||||||
|
if (requestId && requestId !== 'RESOLVED') {
|
||||||
|
console.log(`[VO_PDO] PDO request sent! requestId=${requestId} — waiting for phone to resend`)
|
||||||
|
} else if (requestId === 'RESOLVED') {
|
||||||
|
console.log(`[VO_PDO] Message arrived during PDO delay!`)
|
||||||
|
} else {
|
||||||
|
console.log(`[VO_PDO] Already requested or no response`)
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
console.log(`[VO_PDO] PDO request failed: ${err.message} — falling back to placeholder`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit placeholder immediately so consumers see the notification
|
||||||
|
// If PDO succeeds, the real message will arrive via messages.upsert later
|
||||||
voMsg.message = {
|
voMsg.message = {
|
||||||
viewOnceMessage: {
|
viewOnceMessage: {
|
||||||
message: {
|
message: {
|
||||||
|
|||||||
Reference in New Issue
Block a user