fix: handle view-once send/recv on linked devices
- messages-send.ts: for view-once messages, send DSM only to device=0 (primary phone). Companion devices (device>0) are omitted from participants — the WA server generates <unavailable type="view_once"/> for them automatically. Sending explicit <unavailable> from a companion is rejected by the server and breaks delivery. phash is recalculated to match the actual participants sent. - messages-recv.ts: add early return for <unavailable type="view_once"/> stanzas received by the companion (API). When the primary phone sends a view-once, linked companions receive this node with no enc content. Previously the code tried to decrypt it and crashed with a 500 error. Co-Authored-By: Renato Alcara
This commit is contained in:
@@ -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
|
||||
// <unavailable type="view_once"/> — 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,
|
||||
|
||||
@@ -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
|
||||
// <unavailable type="view_once"/> for them automatically.
|
||||
// Sending explicit <unavailable> 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
|
||||
|
||||
Reference in New Issue
Block a user