fix: detect view-once media on stanza 1 for linked devices + fix mediatype in enc node#2435
fix: detect view-once media on stanza 1 for linked devices + fix mediatype in enc node#2435rsalcara wants to merge 4 commits intoWhiskeySockets:masterfrom
Conversation
…d devices When a view-once arrives at a linked device (InfiniteAPI), the server sends: - Stanza 1: enc payload with full media metadata (mediaKey, directPath) wrapped in viewOnceMessage > imageMessage/videoMessage/audioMessage with viewOnce: true - Stanza 2: unavailable view_once fanout placeholder (already handled) Previously only stanza 2 set key.isViewOnce = true. Stanza 1 was emitted as plain media with no view-once indicator, making it indistinguishable from regular media on the consumer side (messages.upsert). This fix inspects the decrypted proto for viewOnceMessage (v1/v2/v2Ext) wrappers containing a media message with viewOnce=true and propagates key.isViewOnce=true. The viewOnceMessage wrapper is also used for interactive messages (carousel, buttons, lists) but those carry interactiveMessage/listMessage inside -- never imageMessage.viewOnce / videoMessage.viewOnce / audioMessage.viewOnce. The distinction is unambiguous and does not affect interactive message handling. Verified via WA Desktop CDP capture (2026-03-19) and Android Frida DB capture of view-once types 42/43/82 in msgstore.db (2026-03-20). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fullMessage.key is always present on WAMessage. Use consistent style with stanza-2 handling at line 297. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e attribute Without this fix, view-once media (image/video/audio) was sent without mediatype="image/video/audio" on the enc node because getMediaType only checked the top-level message fields. The viewOnceMessage wrapper hides the inner imageMessage, causing mediatype to be empty and WA servers to silently drop the message on the recipient side. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for opening this pull request and contributing to the project! The next step is for the maintainers to review your changes. If everything looks good, it will be approved and merged into the main branch. In the meantime, anyone in the community is encouraged to test this pull request and provide feedback. ✅ How to confirm it worksIf you’ve tested this PR, please comment below with: This helps us speed up the review and merge process. 📦 To test this PR locally:If you encounter any issues or have feedback, feel free to comment as well. |
There was a problem hiding this comment.
Pull request overview
Fixes two linked-device view-once media bugs by correctly (1) marking incoming stanza-1 view-once media as view-once, and (2) ensuring outgoing view-once media sets the correct mediatype attribute on the enc node so servers don’t drop it.
Changes:
- Detect view-once media in stanza 1 by inspecting view-once wrappers and setting
fullMessage.key.isViewOnce = trueduring decryption. - Fix
getMediaType()to unwrap view-once wrappers before checking message media fields, restoringmediatype="image"/"video"/"audio"for view-once sends.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Utils/decode-wa-message.ts | Sets key.isViewOnce for linked-device stanza-1 view-once media by inspecting view-once wrappers’ inner media flags. |
| src/Socket/messages-send.ts | Unwraps view-once wrappers in getMediaType() so enc node mediatype is populated for view-once media. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I'm almost certain they'll ask you to split that PR into two, one for each fix. |
|
Thanks for the feedback! 🙌 |
For view-once messages sent from a linked device (API/companion): - Only send DSM to primary phone (device=0). Companion devices (device>0) are omitted from participants — the WA server generates <unavailable type="view_once"/> for them automatically. Sending an explicit <unavailable> from a companion is silently rejected by the server and causes the message to not be delivered. - Detect actual view-once media by inspecting the inner message's viewOnce flag (imageMessage.viewOnce / videoMessage.viewOnce / audioMessage.viewOnce). viewOnceMessage* wrappers are also used for interactive messages (buttons, lists) which must not be filtered. Consistent with the detection added in decode-wa-message.ts. - assertSessions now uses only the recipients actually encrypted for, avoiding unnecessary session validation for omitted companions.
Problem
Two bugs affecting view-once media (image/video/audio) on linked devices:
Bug 1 —
decode-wa-message.tsWhen a view-once message arrives at a linked device, the server sends two stanzas:
enc): full media metadata —viewOnceMessage > imageMessage/videoMessage/audioMessage { viewOnce: true, mediaKey, directPath, ... }unavailable type="view_once"): fanout placeholder (already handled at line 294)Stanza 1 was decoded but
key.isViewOncewas never set, making it indistinguishable from regular media inmessages.upsert. Consumers had no way to know the message was view-once.Bug 2 —
messages-send.tsgetMediaType()checksmessage.imageMessage,message.videoMessage, etc. directly. WhenviewOnce: trueis set,generateWAMessageContentwraps the media insideviewOnceMessage.message, somessage.imageMessageisundefinedat the top level.Result:
mediatypeattribute was missing from theencnode for all view-once media. WA servers silently dropped view-once video and audio messages (images were more lenient). Verified on production: image view-once arrived, video/audio did not — until this fix.Fix
src/Utils/decode-wa-message.ts(+17 lines)After decoding
proto.Message, inspectviewOnceMessage/viewOnceMessageV2/viewOnceMessageV2Extensionwrappers for inner media withviewOnce: true. If found, setfullMessage.key.isViewOnce = true.The
viewOnceMessagewrapper is also used for interactive messages (carousel, buttons, lists) which carryinteractiveMessage/listMessageinside — neverimageMessage.viewOnce = true. The check is unambiguous.src/Socket/messages-send.ts(+9 lines)Before checking media fields in
getMediaType(), unwrapviewOnceMessage/viewOnceMessageV2/viewOnceMessageV2Extensionrecursively (same pattern asnormalizeMessageContent). This ensuresmediatype="image"/"video"/"audio"is correctly set on theencnode.Testing
Verified on production (2026-03-20):
key.isViewOnce = true,mediatype="image"on encviewOnceMessagewrapper withinteractiveMessageinside is correctly not flagged as view-once mediaChecklist
decode-wa-message.tsandmessages-send.tsWhiskeySockets/Baileysmaster (not fork-specific commits)