From 452df5d40970559d725eb665f533b028391b6a00 Mon Sep 17 00:00:00 2001 From: Renato Alcara Date: Mon, 23 Mar 2026 00:38:05 -0300 Subject: [PATCH] Feat/view once receive (#316) * fix: detect view-once media (image/video/audio) on stanza 1 for linked devices --- src/Socket/messages-send.ts | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index 445036bb..3cff8d7e 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -1277,21 +1277,48 @@ export const makeMessagesSocket = (config: SocketConfig) => { : otherRecipients const effectiveAllRecipients = [...effectiveMeRecipients, ...effectiveOtherRecipients] - await assertSessions(effectiveAllRecipients) + // P2: detect actual view-once media by checking inner message's viewOnce flag. + // viewOnceMessage wrapper is also used for interactive messages (buttons, lists, etc) + // which do NOT carry viewOnce=true on the media — those must NOT be filtered. + const viewOnceInner = + message.viewOnceMessageV2?.message || + message.viewOnceMessage?.message || + message.viewOnceMessageV2Extension?.message + const isViewOnceMsg = !!( + viewOnceInner?.imageMessage?.viewOnce || + viewOnceInner?.videoMessage?.viewOnce || + viewOnceInner?.audioMessage?.viewOnce + ) + + // For view-once: only send DSM to primary phone (device=0). + // Companion devices (device>0) are omitted — WA server generates + // for them automatically. + // Sending explicit from a companion is rejected by the server. + const viewOnceMeRecipients = isViewOnceMsg + ? effectiveMeRecipients.filter(jid => !jidDecode(jid)?.device) + : effectiveMeRecipients + + // P3: assert sessions only for recipients we actually encrypt for. + // For view-once, companions are omitted — asserting their sessions is wasteful + // and could block the send if a companion session is corrupted. + await assertSessions([...viewOnceMeRecipients, ...effectiveOtherRecipients]) const [ { nodes: meNodes, shouldIncludeDeviceIdentity: s1 }, { nodes: otherNodes, shouldIncludeDeviceIdentity: s2 } ] = await Promise.all([ // For own devices: use DSM (deviceSentMessage) wrapper - createParticipantNodes(effectiveMeRecipients, meMsg || message, extraAttrs), + createParticipantNodes(viewOnceMeRecipients, meMsg || message, extraAttrs), createParticipantNodes(effectiveOtherRecipients, message, extraAttrs) ]) participants.push(...meNodes) participants.push(...otherNodes) - if (effectiveMeRecipients.length > 0 || effectiveOtherRecipients.length > 0) { - extraAttrs['phash'] = generateParticipantHashV2(effectiveAllRecipients) + const phashRecipients = isViewOnceMsg + ? [...viewOnceMeRecipients, ...effectiveOtherRecipients] + : effectiveAllRecipients + if (phashRecipients.length > 0) { + extraAttrs['phash'] = generateParticipantHashV2(phashRecipients) } shouldIncludeDeviceIdentity = shouldIncludeDeviceIdentity || s1 || s2