Compare commits

...

1 Commits

Author SHA1 Message Date
Renato Alcara 38fb9bcf81 fix: allow msmsg decryption and emit view-once unavailable fanout
Two issues found during view-once testing on linked devices:

1. msmsg block removal:
   The "temporary fix" that discarded all msmsg-type encrypted messages
   was also blocking legitimate view-once messages. On linked devices,
   view-once arrives as msmsg encryption type. The block was originally
   added to prevent crashes from missing messageSecret, but the existing
   try/catch in the decrypt path already handles those errors gracefully
   by marking the message as a CIPHERTEXT stub.

2. view_once_unavailable_fanout emission:
   When a linked device receives a view-once, the WA server sends only
   <unavailable type="view_once"/> (no <enc> with actual media content).
   Previously this was silently acknowledged without emitting any event,
   so consumers (zpro, astra-api, etc.) never knew a view-once arrived.
   Now it emits the message as a CIPHERTEXT stub with key.isViewOnce=true
   so consumers can display "view-once message received" in their UI.

Co-Authored-By: Renato Alcara
2026-04-11 23:13:10 -03:00
+10 -6
View File
@@ -2251,12 +2251,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
}
const encNode = getBinaryNodeChild(node, 'enc')
// TODO: temporary fix for crashes and issues resulting of failed msmsg decryption
if (encNode?.attrs.type === 'msmsg') {
logger.debug({ key: node.attrs.key }, 'ignored msmsg')
await sendMessageAck(node, NACK_REASONS.MissingMessageSecret)
return
}
// 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
@@ -2382,6 +2377,15 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
{ msgId: msg.key?.id, messageType },
'CTWA: Skipping placeholder resend for unavailable fanout type'
)
// For view-once: emit the message as a stub so consumers know it arrived.
// Use 'notify' for live messages so realtime consumers get the event.
if (messageType === 'view_once_unavailable_fanout') {
msg.key.isViewOnce = true
msg.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT
msg.messageStubParameters = ['view_once_unavailable']
const upsertType = node.attrs.offline ? 'append' : 'notify'
await upsertMessage(msg, upsertType)
}
metrics.ctwaRecoveryFailures.inc({ reason: 'unavailable_fanout' })
return sendMessageAck(node)
}