* fix: respect caller-provided waveform for PTT audio messages
When sending PTT audio with an explicit waveform, Baileys unconditionally
overwrites it with getAudioWaveform() which returns undefined when
audio-decode is not installed — resulting in a flat line on WhatsApp.
Now only invokes getAudioWaveform() when no waveform is already provided,
matching the existing pattern used for jpegThumbnail (line 241).
Validated via CDP + Frida interception: WhatsApp waveform format is
64 bytes Uint8Array, values 0-99, byte-for-byte identical between
Android (audio_data table) and WA Web (model-storage IDB).
Ref: WhiskeySockets/Baileys#2443
Co-Authored-By: Renato Alcara
- Add mentionAll?: boolean to Mentionable type (upstream 6afde71691)
- Set contextInfo.nonJidMentions=1 for @everyone group mentions
- Use viewOnceMessageV2 (field 55) wrapper instead of legacy viewOnceMessage (field 37)
- Set viewOnce=true on inner imageMessage/videoMessage/audioMessage
- Route view-once media uploads to /o1/mms/* CDN via mediaTypeOverride
Co-Authored-By: Renato Alcará
* fix: unwrap viewOnceMessage in getMediaType so enc node gets mediatype 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.
* fix: detect view-once media (image/video/audio) on stanza 1 for linked 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
Address remaining Copilot review comments on PR #306:
- Import RetryReason enum from Utils (already re-exported via Utils/index.ts)
- Replace all numeric literals (0/1/2/3/4/7) in retryErrorCode with the
corresponding enum members (UnknownError / SignalErrorNoSession / etc.)
- Removes inline comments that were only needed to explain magic numbers
- If RetryReason values ever change, the compiler will surface the drift
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Address Copilot review comments on PR #306:
1. Replace ad-hoc regexes with the canonical DECRYPTION_RETRY_CONFIG error
lists from decode-wa-message.ts (single source of truth). Any future
additions to those lists are automatically picked up here.
2. Add coverage for MessageCounterError ('Key used already or never filled')
which was previously returning code 0. It now correctly maps to code 4
(SignalErrorInvalidMessage) via corruptedSessionErrors.
3. Broaden session-error matching to also catch libsignal variants like
'No sessions', 'No open session', 'No sessions available' via the
/no (open )?sessions?/ regex alongside sessionRecordErrors.
4. Fix comment: clarify that code 7 = BadMac and code 4 = InvalidMessage
are distinct codes (previous comment conflated them).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>