From 3f4e83a8afd8b39b9866f223187d8afc6d4107a6 Mon Sep 17 00:00:00 2001 From: Renato Alcara Date: Mon, 23 Mar 2026 00:35:10 -0300 Subject: [PATCH] fix: address codex/copilot review on view-once send/recv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P1 (messages-recv.ts): remove early return for . decryptMessageNode already handles this gracefully — decode-wa-message.ts sets fullMessage.key.isViewOnce=true and skips the CIPHERTEXT stub (line 464). The early return was preventing upsertMessage from emitting an event to consumers, making view-once sends from the primary phone invisible on linked devices. P2 (messages-send.ts): restrict isViewOnceMsg detection to actual view-once media. Previously checked only for the presence of viewOnceMessage* wrappers, which are also used by buttons/lists/carousels (non-view-once). Now unwraps the inner message and checks imageMessage.viewOnce / videoMessage.viewOnce / audioMessage.viewOnce, consistent with the detection in decode-wa-message.ts. Prevents companion DSM filtering from incorrectly applying to interactive messages. P3 (messages-send.ts): move assertSessions to use only the recipients actually encrypted for. For view-once, omitted companions no longer cause assertSessions to fail or do unnecessary session validation work. Co-Authored-By: Renato Alcara --- src/Socket/messages-recv.ts | 11 ----------- src/Socket/messages-send.ts | 23 +++++++++++++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index 58848a05..9318a1b3 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -2258,17 +2258,6 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => { return } - // Handle view-once unavailable sync from primary device. - // When the primary device sends a view-once, linked companions receive - // — no enc content, just a sync signal. - // Acknowledge and skip decryption (nothing to decrypt). - const unavailableNode = getBinaryNodeChild(node, 'unavailable') - if (unavailableNode?.attrs?.type === 'view_once') { - logger.debug({ id: node.attrs.id, from: node.attrs.from }, 'received view_once unavailable sync from primary device') - await sendMessageAck(node) - return - } - const { fullMessage: msg, category, diff --git a/src/Socket/messages-send.ts b/src/Socket/messages-send.ts index d1ec3132..4724c059 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -1277,21 +1277,32 @@ 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 isViewOnceMsg = !!( - message.viewOnceMessageV2 || - message.viewOnceMessage || - message.viewOnceMessageV2Extension - ) 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 }