diff --git a/src/Socket/messages-recv.ts b/src/Socket/messages-recv.ts index 9318a1b3..58848a05 100644 --- a/src/Socket/messages-recv.ts +++ b/src/Socket/messages-recv.ts @@ -2258,6 +2258,17 @@ 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 211b6280..d1ec3132 100644 --- a/src/Socket/messages-send.ts +++ b/src/Socket/messages-send.ts @@ -1279,19 +1279,35 @@ export const makeMessagesSocket = (config: SocketConfig) => { await assertSessions(effectiveAllRecipients) + // 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 + 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