Feat/view once receive#316
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>
- 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
|
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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d870ffb2a3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
Adds view-once handling for linked devices by marking view-once messages during decrypt, adjusting send-side fanout behavior for view-once, and short-circuiting receipt of <unavailable type="view_once"/> sync stanzas.
Changes:
- Detects actual view-once media after decrypt and sets
fullMessage.key.isViewOnce. - Alters send fanout for view-once wrappers (DSM recipients + participant hash selection) and unwraps view-once wrappers when deriving
mediatype. - In receive path, ACKs and skips decryption for
<unavailable type="view_once"/>stanzas.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Utils/decode-wa-message.ts | Marks decrypted messages as view-once when inner media has viewOnce: true. |
| src/Socket/messages-send.ts | Changes DSM recipient selection / phash recipients for view-once; unwraps view-once wrapper in getMediaType. |
| src/Socket/messages-recv.ts | ACKs and returns early on <unavailable type="view_once"/> messages. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
P1 (messages-recv.ts): remove early return for <unavailable type="view_once"/>. 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
Feat/view once receive