@badzz88/baileys 8.4.9 → 8.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +1 -1
  2. package/WAProto/WAProto.proto +6 -347
  3. package/WAProto/fix-imports.js +85 -86
  4. package/WAProto/index.d.ts +25 -4913
  5. package/WAProto/index.js +93 -14069
  6. package/{src → lib}/Defaults/index.js +2 -13
  7. package/{src → lib}/Signal/Group/group_cipher.js +5 -5
  8. package/{src → lib}/Signal/Group/keyhelper.js +4 -4
  9. package/{src → lib}/Signal/Group/sender-chain-key.js +2 -2
  10. package/{src → lib}/Signal/Group/sender-key-distribution-message.js +1 -1
  11. package/{src → lib}/Signal/Group/sender-key-message.js +3 -3
  12. package/{src → lib}/Signal/Group/sender-key-record.js +1 -1
  13. package/{src → lib}/Signal/Group/sender-message-key.js +2 -2
  14. package/{src → lib}/Signal/libsignal.js +26 -6
  15. package/{src → lib}/Socket/chats.js +58 -616
  16. package/{src → lib}/Socket/communities.js +28 -59
  17. package/{src → lib}/Socket/graphql.js +103 -362
  18. package/{src → lib}/Socket/groups.js +29 -77
  19. package/{src → lib}/Socket/index.js +5 -3
  20. package/{src → lib}/Socket/interactive-handler.js +60 -117
  21. package/{src → lib}/Socket/interop.js +19 -36
  22. package/lib/Socket/luxu.js +387 -0
  23. package/{src → lib}/Socket/managed-account.js +27 -58
  24. package/{src → lib}/Socket/messages-recv.js +144 -715
  25. package/{src → lib}/Socket/messages-send.js +102 -412
  26. package/lib/Socket/mex.js +60 -0
  27. package/{src → lib}/Socket/newsletter.js +23 -266
  28. package/{src → lib}/Socket/privacy.js +38 -41
  29. package/{src → lib}/Socket/registration.js +106 -113
  30. package/{src → lib}/Socket/socket.js +101 -88
  31. package/{src → lib}/Socket/text-router.js +17 -1
  32. package/lib/Socket/username.js +243 -0
  33. package/lib/Store/keyed-db.js +108 -0
  34. package/{src → lib}/Store/make-in-memory-store.js +104 -6
  35. package/lib/Types/Mex.js +112 -0
  36. package/{src → lib}/Types/Newsletter.js +41 -72
  37. package/{src → lib}/Types/index.js +1 -0
  38. package/{src → lib}/Utils/chat-utils.js +9 -73
  39. package/lib/Utils/command-loader.d.ts +31 -0
  40. package/lib/Utils/command-loader.js +100 -0
  41. package/lib/Utils/companion-reg-client-utils.js +42 -0
  42. package/lib/Utils/crypto.js +155 -0
  43. package/lib/Utils/curve25519-js.js +275 -0
  44. package/{src → lib}/Utils/decode-wa-message.js +36 -284
  45. package/{src → lib}/Utils/event-buffer.js +27 -6
  46. package/{src → lib}/Utils/generics.js +37 -114
  47. package/{src → lib}/Utils/index.js +5 -1
  48. package/lib/Utils/lt-hash.js +39 -0
  49. package/{src → lib}/Utils/message-retry-manager.js +39 -0
  50. package/{src → lib}/Utils/messages-media.js +58 -105
  51. package/{src → lib}/Utils/messages.js +529 -115
  52. package/lib/Utils/meta-ai-msmsg.js +262 -0
  53. package/lib/Utils/native-bridge.js +82 -0
  54. package/{src → lib}/Utils/noise-handler.js +15 -13
  55. package/{src → lib}/Utils/process-message.js +55 -54
  56. package/{src → lib}/Utils/reporting-utils.js +2 -2
  57. package/lib/Utils/session-pool.d.ts +16 -0
  58. package/lib/Utils/session-pool.js +94 -0
  59. package/{src → lib}/Utils/signal.js +51 -19
  60. package/{src → lib}/Utils/stanza-ack.js +2 -36
  61. package/lib/Utils/sticker.d.ts +17 -0
  62. package/lib/Utils/sticker.js +145 -0
  63. package/{src → lib}/Utils/tc-token-utils.js +1 -1
  64. package/{src → lib}/Utils/validate-connection.js +11 -40
  65. package/lib/Utils/view-once-cache.d.ts +9 -0
  66. package/lib/Utils/view-once-cache.js +79 -0
  67. package/{src → lib}/WABinary/decode.js +4 -38
  68. package/lib/WABinary/encode.js +228 -0
  69. package/{src → lib}/WABinary/generic-utils.js +93 -1
  70. package/{src → lib}/WABinary/jid-utils.js +3 -16
  71. package/{src → lib}/WAM/encode.js +1 -1
  72. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  73. package/{src → lib}/WAUSync/Protocols/USyncContactProtocol.js +1 -7
  74. package/{src → lib}/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
  75. package/{src → lib}/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
  76. package/{src → lib}/WAUSync/Protocols/USyncFeatureProtocol.js +2 -17
  77. package/{src → lib}/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
  78. package/{src → lib}/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
  79. package/{src → lib}/WAUSync/Protocols/index.js +0 -1
  80. package/{src → lib}/WAUSync/USyncQuery.js +6 -77
  81. package/{src → lib}/WAUSync/USyncUser.js +8 -16
  82. package/{src → lib}/WAUSync/index.js +0 -1
  83. package/{src → lib}/antiban.js +266 -355
  84. package/{src → lib}/index.js +2 -2
  85. package/package.json +19 -16
  86. package/WAProto/fix-import.js +0 -38
  87. package/src/Socket/mex.js +0 -47
  88. package/src/Socket/username.js +0 -234
  89. package/src/Utils/crypto.js +0 -125
  90. package/src/Utils/lt-hash.js +0 -10
  91. package/src/Utils/message-inspect.js +0 -400
  92. package/src/Utils/meta-ai-msmsg.js +0 -133
  93. package/src/WABinary/encode.js +0 -58
  94. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -100
  95. package/src/WAUSync/USyncBackoff.js +0 -31
  96. /package/{src → lib}/Defaults/phonenumber-mcc.json +0 -0
  97. /package/{src → lib}/Signal/Group/ciphertext-message.js +0 -0
  98. /package/{src → lib}/Signal/Group/group-session-builder.js +0 -0
  99. /package/{src → lib}/Signal/Group/index.js +0 -0
  100. /package/{src → lib}/Signal/Group/sender-key-name.js +0 -0
  101. /package/{src → lib}/Signal/Group/sender-key-state.js +0 -0
  102. /package/{src → lib}/Signal/lid-mapping.js +0 -0
  103. /package/{src → lib}/Socket/Client/index.js +0 -0
  104. /package/{src → lib}/Socket/Client/types.js +0 -0
  105. /package/{src → lib}/Socket/Client/websocket.js +0 -0
  106. /package/{src → lib}/Socket/aigroups.js +0 -0
  107. /package/{src → lib}/Socket/business.js +0 -0
  108. /package/{src → lib}/Store/index.js +0 -0
  109. /package/{src → lib}/Store/make-cache-manager-store.js +0 -0
  110. /package/{src → lib}/Store/make-ordered-dictionary.js +0 -0
  111. /package/{src → lib}/Store/object-repository.js +0 -0
  112. /package/{src → lib}/Types/Auth.js +0 -0
  113. /package/{src → lib}/Types/Bussines.js +0 -0
  114. /package/{src → lib}/Types/Call.js +0 -0
  115. /package/{src → lib}/Types/Chat.js +0 -0
  116. /package/{src → lib}/Types/Contact.js +0 -0
  117. /package/{src → lib}/Types/Events.js +0 -0
  118. /package/{src → lib}/Types/GroupMetadata.js +0 -0
  119. /package/{src → lib}/Types/Label.js +0 -0
  120. /package/{src → lib}/Types/LabelAssociation.js +0 -0
  121. /package/{src → lib}/Types/Message.js +0 -0
  122. /package/{src → lib}/Types/Product.js +0 -0
  123. /package/{src → lib}/Types/Signal.js +0 -0
  124. /package/{src → lib}/Types/Socket.js +0 -0
  125. /package/{src → lib}/Types/State.js +0 -0
  126. /package/{src → lib}/Types/USync.js +0 -0
  127. /package/{src → lib}/Utils/auth-utils.js +0 -0
  128. /package/{src → lib}/Utils/browser-utils.js +0 -0
  129. /package/{src → lib}/Utils/business.js +0 -0
  130. /package/{src → lib}/Utils/consumer-application.js +0 -0
  131. /package/{src → lib}/Utils/group-history.js +0 -0
  132. /package/{src → lib}/Utils/history.js +0 -0
  133. /package/{src → lib}/Utils/identity-change-handler.js +0 -0
  134. /package/{src → lib}/Utils/jid-display-normalization.js +0 -0
  135. /package/{src → lib}/Utils/link-preview.js +0 -0
  136. /package/{src → lib}/Utils/logger.js +0 -0
  137. /package/{src → lib}/Utils/make-mutex.js +0 -0
  138. /package/{src → lib}/Utils/message-composer.js +0 -0
  139. /package/{src → lib}/Utils/offline-node-processor.js +0 -0
  140. /package/{src → lib}/Utils/pre-key-manager.js +0 -0
  141. /package/{src → lib}/Utils/sync-action-utils.js +0 -0
  142. /package/{src → lib}/Utils/use-multi-file-auth-state.js +0 -0
  143. /package/{src → lib}/Utils/voip-rekey.js +0 -0
  144. /package/{src → lib}/WABinary/constants.js +0 -0
  145. /package/{src → lib}/WABinary/index.js +0 -0
  146. /package/{src → lib}/WABinary/types.js +0 -0
  147. /package/{src → lib}/WAM/BinaryInfo.js +0 -0
  148. /package/{src → lib}/WAM/constants.js +0 -0
  149. /package/{src → lib}/WAM/index.js +0 -0
  150. /package/{src → lib}/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -0
  151. /package/{src → lib}/WAUSync/Protocols/USyncLIDProtocol.js +0 -0
  152. /package/{src → lib}/WAUSync/Protocols/USyncPictureProtocol.js +0 -0
  153. /package/{src → lib}/WAUSync/Protocols/USyncSidelistProtocol.js +0 -0
  154. /package/{src → lib}/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -0
@@ -4,6 +4,7 @@ exports.getNextPreKeysNode =
4
4
  exports.getNextPreKeys =
5
5
  exports.extractDeviceJids =
6
6
  exports.parseAndInjectE2ESessions =
7
+ exports.extractE2ESessionFromRetryReceipt =
7
8
  exports.xmppPreKey =
8
9
  exports.xmppSignedPreKey =
9
10
  exports.generateOrGetPreKeys =
@@ -53,11 +54,9 @@ const generateOrGetPreKeys = (creds, range) => {
53
54
  }
54
55
  }
55
56
  exports.generateOrGetPreKeys = generateOrGetPreKeys
56
- // cipherSuite: 0x05 = X25519/Curve25519 (standard Signal), 0x2a = Kyber-1024 (PQXDH) —
57
- // only pass it when mixing in a non-default cipher; omit for plain Curve25519 uploads.
58
- const xmppSignedPreKey = (key, cipherSuite) => ({
57
+ const xmppSignedPreKey = key => ({
59
58
  tag: 'skey',
60
- attrs: cipherSuite !== undefined ? { key_cipher_suite: cipherSuite } : {},
59
+ attrs: {},
61
60
  content: [
62
61
  { tag: 'id', attrs: {}, content: (0, generics_1.encodeBigEndian)(key.keyId, 3) },
63
62
  { tag: 'value', attrs: {}, content: key.keyPair.public },
@@ -65,27 +64,63 @@ const xmppSignedPreKey = (key, cipherSuite) => ({
65
64
  ]
66
65
  })
67
66
  exports.xmppSignedPreKey = xmppSignedPreKey
68
- const xmppPreKey = (pair, id, cipherSuite) => ({
67
+ const xmppPreKey = (pair, id) => ({
69
68
  tag: 'key',
70
- attrs: cipherSuite !== undefined ? { key_cipher_suite: cipherSuite } : {},
69
+ attrs: {},
71
70
  content: [
72
71
  { tag: 'id', attrs: {}, content: (0, generics_1.encodeBigEndian)(id, 3) },
73
72
  { tag: 'value', attrs: {}, content: pair.public }
74
73
  ]
75
74
  })
76
75
  exports.xmppPreKey = xmppPreKey
77
- const parseAndInjectE2ESessions = async (node, repository) => {
78
- const extractKey = key => {
79
- if (!key) return undefined
80
- const cipherSuite = key.attrs?.key_cipher_suite
81
- return {
82
- keyId: (0, WABinary_1.getBinaryNodeChildUInt)(key, 'id', 3),
83
- publicKey: (0, crypto_1.generateSignalPubKey)((0, WABinary_1.getBinaryNodeChildBuffer)(key, 'value')),
84
- signature: (0, WABinary_1.getBinaryNodeChildBuffer)(key, 'signature'),
85
- // 0x05 = Curve25519 (standard Signal), 0x2a = Kyber-1024 (PQXDH)
86
- ...(cipherSuite !== undefined ? { cipherSuite } : {})
76
+ const isValidUInt = n => typeof n === 'number' && Number.isInteger(n)
77
+ const extractE2ESessionFromRetryReceipt = receipt => {
78
+ const keysNode = (0, WABinary_1.getBinaryNodeChild)(receipt, 'keys')
79
+ if (!keysNode) return null
80
+ const typeBuf = (0, WABinary_1.getBinaryNodeChildBuffer)(keysNode, 'type')
81
+ if (!typeBuf || typeBuf.length !== 1 || typeBuf[0] !== Defaults_1.KEY_BUNDLE_TYPE[0]) return null
82
+ const identity = (0, WABinary_1.getBinaryNodeChildBuffer)(keysNode, 'identity')
83
+ const skey = (0, WABinary_1.getBinaryNodeChild)(keysNode, 'skey')
84
+ if (!identity || identity.length !== 32 || !skey) return null
85
+ const registrationId = (0, WABinary_1.getBinaryNodeChildUInt)(receipt, 'registration', 4)
86
+ if (!isValidUInt(registrationId)) return null
87
+ const signedPubKey = (0, WABinary_1.getBinaryNodeChildBuffer)(skey, 'value')
88
+ const signedSig = (0, WABinary_1.getBinaryNodeChildBuffer)(skey, 'signature')
89
+ const signedKeyId = (0, WABinary_1.getBinaryNodeChildUInt)(skey, 'id', 3)
90
+ if (!signedPubKey || signedPubKey.length !== 32 || !signedSig || !isValidUInt(signedKeyId)) {
91
+ return null
92
+ }
93
+ const preKeyNode = (0, WABinary_1.getBinaryNodeChild)(keysNode, 'key')
94
+ let preKey
95
+ if (preKeyNode) {
96
+ const preKeyPub = (0, WABinary_1.getBinaryNodeChildBuffer)(preKeyNode, 'value')
97
+ const preKeyId = (0, WABinary_1.getBinaryNodeChildUInt)(preKeyNode, 'id', 3)
98
+ if (!preKeyPub || preKeyPub.length !== 32 || !isValidUInt(preKeyId)) {
99
+ return null
87
100
  }
101
+ preKey = { keyId: preKeyId, publicKey: (0, crypto_1.generateSignalPubKey)(preKeyPub) }
102
+ }
103
+ return {
104
+ registrationId,
105
+ identityKey: (0, crypto_1.generateSignalPubKey)(identity),
106
+ signedPreKey: {
107
+ keyId: signedKeyId,
108
+ publicKey: (0, crypto_1.generateSignalPubKey)(signedPubKey),
109
+ signature: signedSig
110
+ },
111
+ preKey
88
112
  }
113
+ }
114
+ exports.extractE2ESessionFromRetryReceipt = extractE2ESessionFromRetryReceipt
115
+ const parseAndInjectE2ESessions = async (node, repository) => {
116
+ const extractKey = key =>
117
+ key
118
+ ? {
119
+ keyId: (0, WABinary_1.getBinaryNodeChildUInt)(key, 'id', 3),
120
+ publicKey: (0, crypto_1.generateSignalPubKey)((0, WABinary_1.getBinaryNodeChildBuffer)(key, 'value')),
121
+ signature: (0, WABinary_1.getBinaryNodeChildBuffer)(key, 'signature')
122
+ }
123
+ : undefined
89
124
  const nodes = (0, WABinary_1.getBinaryNodeChildren)((0, WABinary_1.getBinaryNodeChild)(node, 'list'), 'user')
90
125
  for (const node of nodes) {
91
126
  ;(0, WABinary_1.assertNodeErrorFree)(node)
@@ -180,9 +215,6 @@ const getNextPreKeysNode = async (state, count) => {
180
215
  { tag: 'registration', attrs: {}, content: (0, generics_1.encodeBigEndian)(creds.registrationId) },
181
216
  { tag: 'type', attrs: {}, content: Defaults_1.KEY_BUNDLE_TYPE },
182
217
  { tag: 'identity', attrs: {}, content: creds.signedIdentityKey.public },
183
- // key_cipher_suite is only sent when the bundle mixes in a non-default cipher
184
- // (e.g. Kyber/PQXDH alongside Curve25519) — a plain Curve25519-only upload
185
- // omits it entirely on real clients, confirmed against live capture.
186
218
  { tag: 'list', attrs: {}, content: Object.keys(preKeys).map(k => (0, exports.xmppPreKey)(preKeys[+k], +k)) },
187
219
  (0, exports.xmppSignedPreKey)(creds.signedPreKey)
188
220
  ]
@@ -1,34 +1,6 @@
1
1
  'use strict'
2
2
  Object.defineProperty(exports, '__esModule', { value: true })
3
3
  exports.buildAckStanza = buildAckStanza
4
- // Standalone call stanzas use class="call" + type=<tag>, not class=<tag>.
5
- // Kept in sync with messages-recv.js's CALL_STATE_TAGS + CB: registration loop.
6
- const CALL_STANZA_TAGS = new Set([
7
- 'offer',
8
- 'offer_notice',
9
- 'terminate',
10
- 'accept',
11
- 'reject',
12
- 'preaccept',
13
- 'transport',
14
- 'video',
15
- 'duration',
16
- 'mute_v2',
17
- 'lobby',
18
- 'heartbeat',
19
- 'relaylatency',
20
- 'link_query',
21
- 'group_update',
22
- 'accept_ack',
23
- 'enc-rekey',
24
- 'enc_rekey',
25
- 'peer_state',
26
- 'group_info',
27
- 'video_state',
28
- 'video_state_ack',
29
- 'flow_control',
30
- 'waiting_room_request'
31
- ])
32
4
  /**
33
5
  * Builds an ACK stanza for a received node.
34
6
  * Pure function -- no I/O, no side effects.
@@ -39,19 +11,14 @@ const CALL_STANZA_TAGS = new Set([
39
11
  */
40
12
  function buildAckStanza(node, errorCode, meId) {
41
13
  const { tag, attrs } = node
42
- const isCallStanza = CALL_STANZA_TAGS.has(tag)
43
14
  const stanza = {
44
15
  tag: 'ack',
45
16
  attrs: {
46
17
  id: attrs.id,
47
18
  to: attrs.from,
48
- class: isCallStanza ? 'call' : tag
19
+ class: tag
49
20
  }
50
21
  }
51
- // For standalone call stanzas, WA Web puts the original tag into `type`
52
- if (isCallStanza) {
53
- stanza.attrs.type = tag
54
- }
55
22
  if (errorCode) {
56
23
  stanza.attrs.error = errorCode.toString()
57
24
  }
@@ -62,8 +29,7 @@ function buildAckStanza(node, errorCode, meId) {
62
29
  stanza.attrs.recipient = attrs.recipient
63
30
  }
64
31
  // WA Web always includes type when present: `n.type || DROP_ATTR`
65
- // For call stanzas the type is already set to the tag name above; don't override it.
66
- if (attrs.type && !isCallStanza) {
32
+ if (attrs.type) {
67
33
  stanza.attrs.type = attrs.type
68
34
  }
69
35
  // WA Web WAWebHandleMsgSendAck.sendAck/sendNack always include `from` for message-class ACKs
@@ -0,0 +1,17 @@
1
+ export interface StickerMetaOptions {
2
+ packName?: string
3
+ packPublisher?: string
4
+ categories?: string[]
5
+ }
6
+
7
+ export function addExifToWebp(webpBuffer: Buffer, options?: StickerMetaOptions): Promise<Buffer>
8
+
9
+ export function imageToWebpSticker(
10
+ imageBuffer: Buffer,
11
+ options?: StickerMetaOptions & { quality?: number }
12
+ ): Promise<Buffer>
13
+
14
+ export function videoToWebpSticker(
15
+ videoBuffer: Buffer,
16
+ options?: StickerMetaOptions & { fps?: number; seconds?: number }
17
+ ): Promise<Buffer>
@@ -0,0 +1,145 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.imageToWebpSticker = exports.videoToWebpSticker = exports.addExifToWebp = void 0
4
+ const os = require('os')
5
+ const path = require('path')
6
+ const fs = require('fs')
7
+ const crypto = require('crypto')
8
+ const { exec } = require('child_process')
9
+
10
+ /**
11
+ * Builds and writes the EXIF metadata WhatsApp expects on sticker webp files
12
+ * (pack name / author, shown when a user long-presses a sticker).
13
+ * Converts the file to the "extended" (VP8X) WebP container if it isn't already,
14
+ * since that's the container format that actually carries an EXIF chunk.
15
+ * Returns the new buffer with EXIF attached.
16
+ */
17
+ const addExifToWebp = async (webpBuffer, { packName = '', packPublisher = '', categories = [] } = {}) => {
18
+ const json = {
19
+ 'sticker-pack-id': crypto.randomBytes(16).toString('hex'),
20
+ 'sticker-pack-name': packName,
21
+ 'sticker-pack-publisher': packPublisher,
22
+ emojis: categories.length ? categories : ['😀']
23
+ }
24
+ const jsonBuffer = Buffer.from(JSON.stringify(json), 'utf-8')
25
+ // TIFF header + IFD entry pointing at a single custom tag (0x5741 "AW") holding
26
+ // the JSON payload -- this is the exact structure WhatsApp's client parses.
27
+ const exifAttr = Buffer.from([
28
+ 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
29
+ 0x00, 0x00, 0x00
30
+ ])
31
+ exifAttr.writeUIntLE(jsonBuffer.length, 14, 4)
32
+ const exifPayload = Buffer.concat([exifAttr, jsonBuffer])
33
+ const pad = buf => (buf.length % 2 === 1 ? Buffer.concat([buf, Buffer.from([0x00])]) : buf)
34
+ const makeChunk = (tag, data) => {
35
+ const sizeField = Buffer.alloc(4)
36
+ sizeField.writeUInt32LE(data.length, 0)
37
+ return Buffer.concat([Buffer.from(tag, 'ascii'), sizeField, pad(data)])
38
+ }
39
+ const exifChunk = makeChunk('EXIF', exifPayload)
40
+ if (webpBuffer.slice(0, 4).toString('ascii') !== 'RIFF' || webpBuffer.slice(8, 12).toString('ascii') !== 'WEBP') {
41
+ throw new Error('addExifToWebp expects a valid WebP buffer')
42
+ }
43
+ let offset = 12
44
+ let vp8xChunk = null
45
+ let vp8xOffset = -1
46
+ const otherChunks = []
47
+ while (offset < webpBuffer.length) {
48
+ const tag = webpBuffer.slice(offset, offset + 4).toString('ascii')
49
+ const size = webpBuffer.readUInt32LE(offset + 4)
50
+ const dataStart = offset + 8
51
+ const dataEnd = dataStart + size
52
+ const chunkTotal = 8 + size + (size % 2)
53
+ if (tag === 'VP8X') {
54
+ vp8xChunk = webpBuffer.slice(offset, offset + chunkTotal)
55
+ vp8xOffset = offset
56
+ } else if (tag !== 'EXIF') {
57
+ // drop any pre-existing EXIF chunk, keep everything else (VP8/VP8L/ANIM/ANMF/ICCP/...)
58
+ otherChunks.push(webpBuffer.slice(offset, offset + chunkTotal))
59
+ }
60
+ offset += chunkTotal
61
+ }
62
+ let flags
63
+ let canvasWidth
64
+ let canvasHeight
65
+ if (vp8xChunk) {
66
+ flags = vp8xChunk.readUInt8(8)
67
+ canvasWidth = vp8xChunk.readUIntLE(12, 3) + 1
68
+ canvasHeight = vp8xChunk.readUIntLE(15, 3) + 1
69
+ } else {
70
+ flags = 0
71
+ let sharp
72
+ try {
73
+ sharp = require('sharp')
74
+ } catch (error) {
75
+ throw new Error('addExifToWebp needs the optional "sharp" dependency to read image dimensions')
76
+ }
77
+ const meta = await sharp(webpBuffer).metadata()
78
+ canvasWidth = meta.width
79
+ canvasHeight = meta.height
80
+ }
81
+ flags |= 0x08 // set the "has EXIF metadata" bit
82
+ const vp8xData = Buffer.alloc(10)
83
+ vp8xData.writeUInt8(flags, 0)
84
+ vp8xData.writeUIntLE(canvasWidth - 1, 4, 3)
85
+ vp8xData.writeUIntLE(canvasHeight - 1, 7, 3)
86
+ const newVp8xChunk = makeChunk('VP8X', vp8xData)
87
+ const body = Buffer.concat([newVp8xChunk, ...otherChunks, exifChunk])
88
+ const fileSize = Buffer.alloc(4)
89
+ fileSize.writeUInt32LE(4 + body.length, 0) // 4 = 'WEBP'
90
+ return Buffer.concat([Buffer.from('RIFF', 'ascii'), fileSize, Buffer.from('WEBP', 'ascii'), body])
91
+ }
92
+ exports.addExifToWebp = addExifToWebp
93
+
94
+ /**
95
+ * Converts a static image buffer to a WhatsApp sticker-ready webp buffer using `sharp`.
96
+ * @param {Buffer} imageBuffer
97
+ * @param {{ packName?: string, packPublisher?: string, categories?: string[], quality?: number }} [options]
98
+ * @returns {Promise<Buffer>}
99
+ */
100
+ const imageToWebpSticker = async (imageBuffer, options = {}) => {
101
+ let sharp
102
+ try {
103
+ sharp = require('sharp')
104
+ } catch (error) {
105
+ throw new Error('imageToWebpSticker requires the optional "sharp" dependency to be installed')
106
+ }
107
+ const webp = await sharp(imageBuffer)
108
+ .resize(512, 512, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } })
109
+ .webp({ quality: options.quality ?? 80 })
110
+ .toBuffer()
111
+ return addExifToWebp(webp, options)
112
+ }
113
+ exports.imageToWebpSticker = imageToWebpSticker
114
+
115
+ /**
116
+ * Converts a video buffer to an animated WhatsApp sticker-ready webp buffer.
117
+ * Requires the `ffmpeg` binary to be available on PATH.
118
+ * @param {Buffer} videoBuffer
119
+ * @param {{ packName?: string, packPublisher?: string, categories?: string[], fps?: number, seconds?: number }} [options]
120
+ * @returns {Promise<Buffer>}
121
+ */
122
+ const videoToWebpSticker = async (videoBuffer, options = {}) => {
123
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'xazbails-sticker-'))
124
+ const inputPath = path.join(tmpDir, 'input.mp4')
125
+ const outputPath = path.join(tmpDir, 'output.webp')
126
+ fs.writeFileSync(inputPath, videoBuffer)
127
+ const fps = options.fps ?? 10
128
+ const seconds = options.seconds ?? 5
129
+ const cmd =
130
+ `ffmpeg -y -i ${inputPath} -t ${seconds} -vf ` +
131
+ `"fps=${fps},scale=512:512:force_original_aspect_ratio=decrease,` +
132
+ `pad=512:512:(ow-iw)/2:(oh-ih)/2:color=0x00000000,split[a][b];` +
133
+ `[a]palettegen=reserve_transparent=1[p];[b][p]paletteuse" ` +
134
+ `-loop 0 -preset default -an -vsync 0 ${outputPath}`
135
+ try {
136
+ await new Promise((resolve, reject) => {
137
+ exec(cmd, err => (err ? reject(err) : resolve()))
138
+ })
139
+ const webp = fs.readFileSync(outputPath)
140
+ return await addExifToWebp(webp, options)
141
+ } finally {
142
+ fs.rmSync(tmpDir, { recursive: true, force: true })
143
+ }
144
+ }
145
+ exports.videoToWebpSticker = videoToWebpSticker
@@ -64,7 +64,7 @@ async function buildMergedTcTokenIndexWrite(keys, addedJids) {
64
64
  // WA Web has separate sender/receiver AB props for these but they're identical today
65
65
  function isTcTokenExpired(timestamp) {
66
66
  if (timestamp === null || timestamp === undefined) return true
67
- const ts = typeof timestamp === 'string' ? parseInt(timestamp, 10) : timestamp
67
+ const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp
68
68
  if (isNaN(ts)) return true
69
69
  const now = Math.floor(Date.now() / 1000)
70
70
  const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION)
@@ -7,40 +7,24 @@ exports.encodeSignedDeviceIdentity =
7
7
  void 0
8
8
  const boom_1 = require('@hapi/boom')
9
9
  const crypto_1 = require('crypto')
10
- const rb = require('whatsapp-rust-bridge-baron')
10
+ const Crypto_1 = require('./crypto')
11
11
  const index_js_1 = require('../../WAProto/index.js')
12
12
  const Defaults_1 = require('../Defaults')
13
13
  const WABinary_1 = require('../WABinary')
14
14
  const crypto_2 = require('./crypto')
15
15
  const generics_1 = require('./generics')
16
16
  const signal_1 = require('./signal')
17
- const MOBILE_PLATFORMS = new Set(['Android', 'iOS'])
18
17
  const getUserAgent = config => {
19
- const os = config.browser[0]
20
- let platform, device, osVersion
21
- if (os === 'Android') {
22
- platform = index_js_1.proto.ClientPayload.UserAgent.Platform.ANDROID
23
- device = 'Android'
24
- osVersion = config.browser[2] || '15.0.0'
25
- } else if (os === 'iOS') {
26
- platform = index_js_1.proto.ClientPayload.UserAgent.Platform.IOS
27
- device = 'iPhone'
28
- osVersion = config.browser[2] || '18.0'
29
- } else {
30
- platform = index_js_1.proto.ClientPayload.UserAgent.Platform.WEB
31
- device = 'Desktop'
32
- osVersion = '0.1'
33
- }
34
18
  return {
35
19
  appVersion: {
36
20
  primary: config.version[0],
37
21
  secondary: config.version[1],
38
22
  tertiary: config.version[2]
39
23
  },
40
- platform,
41
- releaseChannel: index_js_1.proto.ClientPayload.UserAgent.ReleaseChannel.DEBUG,
42
- osVersion,
43
- device,
24
+ platform: index_js_1.proto.ClientPayload.UserAgent.Platform.WEB,
25
+ releaseChannel: index_js_1.proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
26
+ osVersion: '0.1',
27
+ device: 'Desktop',
44
28
  osBuildNumber: '0.1',
45
29
  localeLanguageIso6391: 'en',
46
30
  mnc: '000',
@@ -50,9 +34,7 @@ const getUserAgent = config => {
50
34
  }
51
35
  const PLATFORM_MAP = {
52
36
  'Mac OS': index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.DARWIN,
53
- Windows: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN32,
54
- Android: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER,
55
- iOS: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
37
+ Windows: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN32
56
38
  }
57
39
  const getWebInfo = config => {
58
40
  let webSubPlatform = index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
@@ -67,9 +49,7 @@ const getClientPayload = config => {
67
49
  connectReason: index_js_1.proto.ClientPayload.ConnectReason.USER_ACTIVATED,
68
50
  userAgent: getUserAgent(config)
69
51
  }
70
- if (!MOBILE_PLATFORMS.has(config.browser[0])) {
71
- payload.webInfo = getWebInfo(config)
72
- }
52
+ payload.webInfo = getWebInfo(config)
73
53
  return payload
74
54
  }
75
55
  const generateLoginNode = (userJid, config, creds) => {
@@ -105,18 +85,16 @@ const getPlatformType = platform => {
105
85
  const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentityKey }, config) => {
106
86
  // the app version needs to be md5 hashed
107
87
  // and passed in
108
- const appVersionBuf = Buffer.from(rb.md5(Buffer.from(config.version.join('.'))))
88
+ const appVersionBuf = Buffer.from(Crypto_1.md5(Buffer.from(config.version.join('.'))))
109
89
  const companion = {
110
90
  os: config.browser[0],
111
91
  platformType: getPlatformType(config.browser[1]),
112
92
  requireFullSync: config.syncFullHistory,
113
93
  historySyncConfig: {
114
- fullSyncDaysLimit: undefined,
115
- fullSyncSizeMbLimit: undefined,
116
94
  storageQuotaMb: 10240,
117
95
  inlineInitialPayloadInE2EeMsg: true,
118
96
  recentSyncDaysLimit: undefined,
119
- supportCallLogHistory: true,
97
+ supportCallLogHistory: false,
120
98
  supportBotUserAgentChatHistory: true,
121
99
  supportCagReactionsAndPolls: true,
122
100
  supportBizHostedMsg: true,
@@ -125,16 +103,9 @@ const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentity
125
103
  supportFbidBotChatHistory: true,
126
104
  supportAddOnHistorySyncMigration: undefined,
127
105
  supportMessageAssociation: true,
128
- supportGroupHistory: true,
106
+ supportGroupHistory: false,
129
107
  onDemandReady: undefined,
130
- supportGuestChat: true,
131
- completeOnDemandReady: undefined,
132
- thumbnailSyncDaysLimit: undefined,
133
- initialSyncMaxMessagesPerChat: undefined,
134
- supportManusHistory: true,
135
- supportHatchHistory: true,
136
- supportedBotChannelFbids: undefined,
137
- supportInlineContacts: true
108
+ supportGuestChat: undefined
138
109
  },
139
110
  version: {
140
111
  primary: 10,
@@ -0,0 +1,9 @@
1
+ export interface AutoCacheViewOnceOptions {
2
+ cacheDir?: string
3
+ logger?: any
4
+ onCached?: (info: { id: string; jid: string; filePath: string; type: string }) => void
5
+ }
6
+
7
+ export function getViewOnceContent(message: any): Record<string, any> | null
8
+
9
+ export function autoCacheViewOnceMedia(sock: any, options?: AutoCacheViewOnceOptions): () => void
@@ -0,0 +1,79 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.autoCacheViewOnceMedia = void 0
4
+ const path = require('path')
5
+ const fs = require('fs')
6
+ const Messages_1 = require('./messages')
7
+ const MessagesMedia_1 = require('./messages-media')
8
+
9
+ const VIEW_ONCE_WRAPPER_KEYS = ['viewOnceMessage', 'viewOnceMessageV2', 'viewOnceMessageV2Extension']
10
+ const VIEW_ONCE_INNER_KEYS = ['imageMessage', 'videoMessage', 'audioMessage']
11
+
12
+ /**
13
+ * Unwraps a message and returns the inner view-once media content if present, otherwise null.
14
+ * Handles both the wrapper style (viewOnceMessage[V2/V2Extension] -> message -> ...)
15
+ * and the inline style (imageMessage/videoMessage with viewOnce: true set directly).
16
+ */
17
+ const getViewOnceContent = message => {
18
+ if (!message) return null
19
+ const content = (0, Messages_1.extractMessageContent)(message)
20
+ if (!content) return null
21
+ for (const wrapperKey of VIEW_ONCE_WRAPPER_KEYS) {
22
+ const wrapper = content[wrapperKey]
23
+ if (wrapper?.message) {
24
+ const inner = (0, Messages_1.extractMessageContent)(wrapper.message)
25
+ if (inner) return inner
26
+ }
27
+ }
28
+ for (const key of VIEW_ONCE_INNER_KEYS) {
29
+ if (content[key]?.viewOnce) {
30
+ return { [key]: content[key] }
31
+ }
32
+ }
33
+ return null
34
+ }
35
+
36
+ /**
37
+ * Registers a `messages.upsert` listener on the given socket that automatically
38
+ * downloads and saves view-once media (image/video/audio) to `cacheDir` as soon
39
+ * as it arrives, before the sender's app can mark it as opened/consumed.
40
+ *
41
+ * @param {ReturnType<typeof import('../Socket').default>} sock - an active Baileys socket
42
+ * @param {{ cacheDir?: string, logger?: any, onCached?: (info: { id: string, jid: string, filePath: string, type: string }) => void }} [options]
43
+ * @returns {() => void} a function that removes the listener
44
+ */
45
+ const autoCacheViewOnceMedia = (sock, options = {}) => {
46
+ const cacheDir = options.cacheDir || './viewonce-cache'
47
+ const logger = options.logger || sock.logger
48
+ if (!fs.existsSync(cacheDir)) {
49
+ fs.mkdirSync(cacheDir, { recursive: true })
50
+ }
51
+ const handler = async ({ messages, type }) => {
52
+ if (type !== 'notify' && type !== 'append') return
53
+ for (const msg of messages) {
54
+ try {
55
+ const viewOnceContent = getViewOnceContent(msg.message)
56
+ if (!viewOnceContent) continue
57
+ const mediaType = Object.keys(viewOnceContent)[0]
58
+ const buffer = await (0, Messages_1.downloadMediaMessage)(
59
+ msg,
60
+ 'buffer',
61
+ {},
62
+ { logger, reuploadRequest: sock.updateMediaMessage }
63
+ )
64
+ const extension = (0, MessagesMedia_1.extensionForMediaMessage)(viewOnceContent) || 'bin'
65
+ const fileName = `${msg.key.id}.${extension}`
66
+ const filePath = path.join(cacheDir, fileName)
67
+ fs.writeFileSync(filePath, buffer)
68
+ logger?.debug?.({ id: msg.key.id, filePath, mediaType }, 'cached view-once media')
69
+ options.onCached?.({ id: msg.key.id, jid: msg.key.remoteJid, filePath, type: mediaType })
70
+ } catch (error) {
71
+ logger?.warn?.({ error, id: msg.key?.id }, 'failed to cache view-once media')
72
+ }
73
+ }
74
+ }
75
+ sock.ev.on('messages.upsert', handler)
76
+ return () => sock.ev.off('messages.upsert', handler)
77
+ }
78
+ exports.autoCacheViewOnceMedia = autoCacheViewOnceMedia
79
+ exports.getViewOnceContent = getViewOnceContent
@@ -56,7 +56,6 @@ const util_1 = require('util')
56
56
  const zlib_1 = require('zlib')
57
57
  const constants = __importStar(require('./constants'))
58
58
  const jid_utils_1 = require('./jid-utils')
59
- const rb = require('whatsapp-rust-bridge-baron')
60
59
  const inflatePromise = (0, util_1.promisify)(zlib_1.inflate)
61
60
  const decompressingIfRequired = async buffer => {
62
61
  if (2 & buffer.readUInt8()) {
@@ -68,25 +67,6 @@ const decompressingIfRequired = async buffer => {
68
67
  return buffer
69
68
  }
70
69
  exports.decompressingIfRequired = decompressingIfRequired
71
-
72
- // Converts InternalBinaryNode.toJSON() output to a plain JS object with Buffer content
73
- const rustNodeToJs = node => {
74
- if (!node || typeof node !== 'object') return node
75
- const result = { tag: node.tag, attrs: node.attrs || {} }
76
- const content = node.content
77
- if (content === undefined || content === null) {
78
- // no content
79
- } else if (content instanceof Uint8Array) {
80
- result.content = Buffer.from(content)
81
- } else if (typeof content === 'string') {
82
- result.content = content
83
- } else if (Array.isArray(content)) {
84
- result.content = content.map(rustNodeToJs)
85
- } else {
86
- result.content = content
87
- }
88
- return result
89
- }
90
70
  const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) => {
91
71
  const { DOUBLE_BYTE_TOKENS, SINGLE_BYTE_TOKENS, TAGS } = opts
92
72
  const checkEOS = length => {
@@ -110,7 +90,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
110
90
  return value
111
91
  }
112
92
  const readStringFromChars = length => {
113
- return readBytes(length).toString('utf8')
93
+ return readBytes(length).toString('utf-8')
114
94
  }
115
95
  const readInt = (n, littleEndian = false) => {
116
96
  checkEOS(n)
@@ -304,9 +284,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
304
284
  const value = dict[index2]
305
285
  if (typeof value === 'undefined') {
306
286
  // Unknown token in known dictionary — token table may be outdated
307
- process.stderr.write(
308
- `[WABinary] unknown double-byte token dict=${index1} idx=${index2} — token table outdated?\n`
309
- )
287
+ process.stderr.write(`[WABinary] unknown double-byte token dict=${index1} idx=${index2} — token table outdated?\n`)
310
288
  return ''
311
289
  }
312
290
  return value
@@ -358,20 +336,8 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
358
336
  }
359
337
  }
360
338
  exports.decodeDecompressedBinaryNode = decodeDecompressedBinaryNode
361
- const JS_DECODE_OPTS = {
362
- TAGS: constants.TAGS,
363
- SINGLE_BYTE_TOKENS: constants.SINGLE_BYTE_TOKENS,
364
- DOUBLE_BYTE_TOKENS: constants.DOUBLE_BYTE_TOKENS,
365
- }
366
339
  const decodeBinaryNode = buff => {
367
- const u8 = buff instanceof Uint8Array ? buff : new Uint8Array(buff.buffer, buff.byteOffset, buff.byteLength)
368
- try {
369
- const internal = rb.decodeNode(u8)
370
- return rustNodeToJs(internal.toJSON())
371
- } catch (_) {
372
- // WASM token table is outdated (e.g. INTEROP_JID_TUPLE=244, INTEROP_JID=245).
373
- // Fall back to the JS decoder which knows all current tags.
374
- return (0, exports.decodeDecompressedBinaryNode)(buff, JS_DECODE_OPTS)
375
- }
340
+ const buffer = Buffer.isBuffer(buff) ? buff : Buffer.from(buff)
341
+ return decodeDecompressedBinaryNode(buffer, constants)
376
342
  }
377
343
  exports.decodeBinaryNode = decodeBinaryNode