Compare commits

...

5 Commits

Author SHA1 Message Date
Renato Alcara 9b5104b6c7 style: fix prettier formatting in viewOnceInner chain
Why: check-lint CI failed on decode-wa-message.ts:329 with prettier/prettier
error. Joining the three optional-chain operands onto one continuation line
matches printWidth=120 / useTabs config.
2026-04-26 13:35:34 -03:00
Renato Alcara 16aa02519e 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.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:21:04 -03:00
Renato Alcara 8150587de3 fix: remove unnecessary non-null assertion on fullMessage.key
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>
2026-03-20 11:21:04 -03:00
Renato Alcara 8f1fc42cc7 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 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>
2026-03-20 11:21:04 -03:00
github-actions[bot] d077902695 chore: update WhatsApp Web version (#2420)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-15 22:49:39 +02:00
5 changed files with 27 additions and 3 deletions
+1 -1
View File
@@ -1 +1 @@
{"version":[2,3000,1033846690]}
{"version":[2,3000,1035194821]}
+1 -1
View File
@@ -4,7 +4,7 @@ import type { AuthenticationState, SocketConfig, WAVersion } from '../Types'
import { Browsers } from '../Utils/browser-utils'
import logger from '../Utils/logger'
const version = [2, 3000, 1033846690]
const version = [2, 3000, 1035194821]
export const UNAUTHORIZED_CODES = [401, 403, 419]
+9
View File
@@ -1072,6 +1072,15 @@ export const makeMessagesSocket = (config: SocketConfig) => {
}
const getMediaType = (message: proto.IMessage) => {
// For view-once media, unwrap the viewOnceMessage wrapper before checking media type
const inner =
message.viewOnceMessage?.message ||
message.viewOnceMessageV2?.message ||
message.viewOnceMessageV2Extension?.message
if (inner) {
return getMediaType(inner)
}
if (message.imageMessage) {
return 'image'
} else if (message.videoMessage) {
+15
View File
@@ -319,6 +319,21 @@ export const decryptMessageNode = (
} else {
fullMessage.message = msg
}
// Detect view-once media on stanza 1 received by linked device.
// viewOnceMessage wrapper is also used for interactive messages
// (interactiveMessage, listMessage, nativeFlowMessage) -- those do NOT have
// imageMessage.viewOnce / videoMessage.viewOnce / audioMessage.viewOnce = true.
// Only real view-once media carries viewOnce: true on the inner media message.
const viewOnceInner =
msg.viewOnceMessage?.message || msg.viewOnceMessageV2?.message || msg.viewOnceMessageV2Extension?.message
if (
viewOnceInner?.imageMessage?.viewOnce ||
viewOnceInner?.videoMessage?.viewOnce ||
viewOnceInner?.audioMessage?.viewOnce
) {
fullMessage.key.isViewOnce = true
}
} catch (err: any) {
const errorContext = {
key: fullMessage.key,
+1 -1
View File
@@ -1,7 +1,7 @@
import { Boom } from '@hapi/boom'
import { createHash, randomBytes } from 'crypto'
import { proto } from '../../WAProto/index.js'
const baileysVersion = [2, 3000, 1033846690]
const baileysVersion = [2, 3000, 1035194821]
import type {
BaileysEventEmitter,
BaileysEventMap,