@badzz88/baileys 8.5.2 → 8.5.4

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 (110) hide show
  1. package/README.md +1 -1
  2. package/WAProto/WAProto.proto +1256 -75
  3. package/WAProto/index.d.ts +36042 -19649
  4. package/WAProto/index.js +181990 -135645
  5. package/engine-requirements.js +7 -15
  6. package/lib/Defaults/index.js +22 -17
  7. package/lib/Signal/Group/group_cipher.js +5 -5
  8. package/lib/Signal/Group/keyhelper.js +4 -4
  9. package/lib/Signal/Group/sender-chain-key.js +2 -2
  10. package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
  11. package/lib/Signal/Group/sender-key-message.js +3 -3
  12. package/lib/Signal/Group/sender-key-record.js +1 -1
  13. package/lib/Signal/Group/sender-message-key.js +5 -5
  14. package/lib/Signal/libsignal.js +39 -59
  15. package/lib/Signal/lid-mapping.js +28 -10
  16. package/lib/Socket/aigroups.js +5 -5
  17. package/lib/Socket/business.js +3 -11
  18. package/lib/Socket/chats.js +668 -319
  19. package/lib/Socket/communities.js +85 -89
  20. package/lib/Socket/graphql.js +435 -228
  21. package/lib/Socket/groups.js +132 -211
  22. package/lib/Socket/index.js +9 -7
  23. package/lib/Socket/interactive-handler.js +117 -60
  24. package/lib/Socket/interop.js +56 -175
  25. package/lib/Socket/managed-account.js +79 -48
  26. package/lib/Socket/messages-recv.js +898 -340
  27. package/lib/Socket/messages-send.js +462 -160
  28. package/lib/Socket/mex.js +40 -53
  29. package/lib/Socket/newsletter.js +291 -291
  30. package/lib/Socket/privacy.js +77 -265
  31. package/lib/Socket/registration.js +164 -157
  32. package/lib/Socket/socket.js +181 -199
  33. package/lib/Socket/username.js +144 -227
  34. package/lib/Store/make-cache-manager-store.js +2 -2
  35. package/lib/Store/make-in-memory-store.js +25 -141
  36. package/lib/Types/Auth.js +3 -3
  37. package/lib/Types/Label.js +1 -1
  38. package/lib/Types/LabelAssociation.js +1 -1
  39. package/lib/Types/Newsletter.js +96 -65
  40. package/lib/Types/State.js +4 -4
  41. package/lib/Types/index.js +1 -1
  42. package/lib/Utils/auth-utils.js +52 -57
  43. package/lib/Utils/browser-utils.js +1 -1
  44. package/lib/Utils/business.js +2 -6
  45. package/lib/Utils/chat-utils.js +102 -38
  46. package/lib/Utils/crypto.js +54 -84
  47. package/lib/Utils/decode-wa-message.js +316 -83
  48. package/lib/Utils/event-buffer.js +37 -61
  49. package/lib/Utils/generics.js +157 -103
  50. package/lib/Utils/group-history.js +3 -12
  51. package/lib/Utils/history.js +15 -15
  52. package/lib/Utils/index.js +1 -5
  53. package/lib/Utils/link-preview.js +3 -8
  54. package/lib/Utils/lt-hash.js +2 -35
  55. package/lib/Utils/make-mutex.js +1 -1
  56. package/lib/Utils/message-inspect.js +393 -0
  57. package/lib/Utils/message-retry-manager.js +27 -92
  58. package/lib/Utils/messages-media.js +159 -130
  59. package/lib/Utils/messages.js +144 -566
  60. package/lib/Utils/meta-ai-msmsg.js +62 -202
  61. package/lib/Utils/noise-handler.js +19 -21
  62. package/lib/Utils/offline-node-processor.js +3 -8
  63. package/lib/Utils/pre-key-manager.js +13 -23
  64. package/lib/Utils/process-message.js +197 -106
  65. package/lib/Utils/reporting-utils.js +2 -2
  66. package/lib/Utils/signal.js +28 -63
  67. package/lib/Utils/stanza-ack.js +39 -12
  68. package/lib/Utils/sync-action-utils.js +4 -7
  69. package/lib/Utils/tc-token-utils.js +20 -25
  70. package/lib/Utils/use-multi-file-auth-state.js +30 -14
  71. package/lib/Utils/validate-connection.js +59 -19
  72. package/lib/WABinary/decode.js +50 -16
  73. package/lib/WABinary/encode.js +54 -224
  74. package/lib/WABinary/generic-utils.js +7 -97
  75. package/lib/WABinary/jid-utils.js +27 -14
  76. package/lib/WAM/encode.js +7 -7
  77. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +58 -1
  78. package/lib/WAUSync/Protocols/USyncContactProtocol.js +7 -1
  79. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +13 -5
  80. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -4
  81. package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +20 -7
  82. package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +865 -0
  83. package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
  84. package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
  85. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -2
  86. package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
  87. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +2 -1
  88. package/lib/WAUSync/Protocols/index.js +1 -0
  89. package/lib/WAUSync/USyncBackoff.js +26 -0
  90. package/lib/WAUSync/USyncQuery.js +77 -6
  91. package/lib/WAUSync/USyncUser.js +16 -8
  92. package/lib/WAUSync/index.js +1 -0
  93. package/lib/antiban.js +608 -854
  94. package/lib/index.js +3 -3
  95. package/package.json +16 -19
  96. package/lib/Socket/luxu.js +0 -387
  97. package/lib/Socket/text-router.js +0 -83
  98. package/lib/Store/keyed-db.js +0 -108
  99. package/lib/Types/Mex.js +0 -112
  100. package/lib/Utils/command-loader.d.ts +0 -31
  101. package/lib/Utils/command-loader.js +0 -100
  102. package/lib/Utils/companion-reg-client-utils.js +0 -42
  103. package/lib/Utils/curve25519-js.js +0 -275
  104. package/lib/Utils/native-bridge.js +0 -82
  105. package/lib/Utils/session-pool.d.ts +0 -16
  106. package/lib/Utils/session-pool.js +0 -94
  107. package/lib/Utils/sticker.d.ts +0 -17
  108. package/lib/Utils/sticker.js +0 -145
  109. package/lib/Utils/view-once-cache.d.ts +0 -9
  110. package/lib/Utils/view-once-cache.js +0 -79
@@ -1,24 +1,50 @@
1
1
  'use strict'
2
2
  Object.defineProperty(exports, '__esModule', { value: true })
3
3
  exports.buildAckStanza = buildAckStanza
4
- /**
5
- * Builds an ACK stanza for a received node.
6
- * Pure function -- no I/O, no side effects.
7
- *
8
- * Mirrors WhatsApp Web's ACK construction:
9
- * - WAWebHandleMsgSendAck.sendAck / sendNack
10
- * - WAWebCreateNackFromStanza.createNackFromStanza
11
- */
4
+
5
+
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
+
12
33
  function buildAckStanza(node, errorCode, meId) {
13
34
  const { tag, attrs } = node
35
+ const isCallStanza = CALL_STANZA_TAGS.has(tag)
14
36
  const stanza = {
15
37
  tag: 'ack',
16
38
  attrs: {
17
39
  id: attrs.id,
18
40
  to: attrs.from,
19
- class: tag
41
+ class: isCallStanza ? 'call' : tag
20
42
  }
21
43
  }
44
+
45
+ if (isCallStanza) {
46
+ stanza.attrs.type = tag
47
+ }
22
48
  if (errorCode) {
23
49
  stanza.attrs.error = errorCode.toString()
24
50
  }
@@ -28,11 +54,12 @@ function buildAckStanza(node, errorCode, meId) {
28
54
  if (attrs.recipient) {
29
55
  stanza.attrs.recipient = attrs.recipient
30
56
  }
31
- // WA Web always includes type when present: `n.type || DROP_ATTR`
32
- if (attrs.type) {
57
+
58
+
59
+ if (attrs.type && !isCallStanza) {
33
60
  stanza.attrs.type = attrs.type
34
61
  }
35
- // WA Web WAWebHandleMsgSendAck.sendAck/sendNack always include `from` for message-class ACKs
62
+
36
63
  if (tag === 'message' && meId) {
37
64
  stanza.attrs.from = meId
38
65
  }
@@ -2,10 +2,7 @@
2
2
  Object.defineProperty(exports, '__esModule', { value: true })
3
3
  exports.emitSyncActionResults = exports.processContactAction = void 0
4
4
  const WABinary_1 = require('../WABinary')
5
- /**
6
- * Process contactAction and return events to emit.
7
- * Pure function - no side effects.
8
- */
5
+
9
6
  const processContactAction = (action, id, logger) => {
10
7
  const results = []
11
8
  if (!id) {
@@ -17,9 +14,9 @@ const processContactAction = (action, id, logger) => {
17
14
  }
18
15
  const lidJid = action.lidJid
19
16
  const idIsPn = (0, WABinary_1.isPnUser)(id)
20
- // PN is in index[1], not in contactAction.pnJid which is usually null
17
+
21
18
  const phoneNumber = idIsPn ? id : action.pnJid || undefined
22
- // Always emit contacts.upsert
19
+
23
20
  results.push({
24
21
  event: 'contacts.upsert',
25
22
  data: [
@@ -32,7 +29,7 @@ const processContactAction = (action, id, logger) => {
32
29
  }
33
30
  ]
34
31
  })
35
- // Emit lid-mapping.update if we have valid LID-PN pair
32
+
36
33
  if (lidJid && (0, WABinary_1.isLidUser)(lidJid) && idIsPn) {
37
34
  results.push({
38
35
  event: 'lid-mapping.update',
@@ -10,21 +10,16 @@ exports.resolveIssuanceJid = resolveIssuanceJid
10
10
  exports.buildTcTokenFromJid = buildTcTokenFromJid
11
11
  exports.storeTcTokensFromIqResult = storeTcTokensFromIqResult
12
12
  const WABinary_1 = require('../WABinary')
13
- // Same phone-number pattern as WABinary's isJidBot, applied against the user
14
- // part so the check is invariant to @c.us ↔ @s.whatsapp.net normalization.
13
+
14
+
15
15
  const BOT_PHONE_REGEX = /^1313555\d{4}$|^131655500\d{2}$/
16
- /**
17
- * Mirrors WA Web's `Wid.isRegularUser()` (user ∧ ¬PSA ∧ ¬Bot). Used to gate tctoken
18
- * storage against malformed notifications — WA Web filters server-side but we
19
- * defend here for parity with `WAWebSetTcTokenChatAction.handleIncomingTcToken`.
20
- * Works for both pre- and post-normalized JIDs (`@c.us` vs `@s.whatsapp.net`).
21
- */
16
+
22
17
  function isRegularUser(jid) {
23
18
  if (!jid) return false
24
19
  const user = jid.split('@')[0] ?? ''
25
- if (user === '0') return false // PSA
26
- if (BOT_PHONE_REGEX.test(user)) return false // Bot by phone pattern
27
- if ((0, WABinary_1.isJidMetaAI)(jid)) return false // MetaAI (@bot server)
20
+ if (user === '0') return false
21
+ if (BOT_PHONE_REGEX.test(user)) return false
22
+ if ((0, WABinary_1.isJidMetaAI)(jid)) return false
28
23
  return !!(
29
24
  (0, WABinary_1.isPnUser)(jid) ||
30
25
  (0, WABinary_1.isLidUser)(jid) ||
@@ -33,11 +28,11 @@ function isRegularUser(jid) {
33
28
  jid.endsWith('@c.us')
34
29
  )
35
30
  }
36
- const TC_TOKEN_BUCKET_DURATION = 604800 // 7 days
37
- const TC_TOKEN_NUM_BUCKETS = 4 // ~28-day rolling window
38
- /** Sentinel key under `tctoken` store holding a JSON array of tracked storage JIDs for cross-session pruning. */
31
+ const TC_TOKEN_BUCKET_DURATION = 604800
32
+ const TC_TOKEN_NUM_BUCKETS = 4
33
+
39
34
  exports.TC_TOKEN_INDEX_KEY = '__index'
40
- /** Read the persisted tctoken JID index and return its entries (never contains the sentinel key itself). */
35
+
41
36
  async function readTcTokenIndex(keys) {
42
37
  const data = await keys.get('tctoken', [exports.TC_TOKEN_INDEX_KEY])
43
38
  const entry = data[exports.TC_TOKEN_INDEX_KEY]
@@ -50,7 +45,7 @@ async function readTcTokenIndex(keys) {
50
45
  return []
51
46
  }
52
47
  }
53
- /** Build a SignalDataSet fragment that writes the merged index (persisted ∪ added) under the sentinel key. */
48
+
54
49
  async function buildMergedTcTokenIndexWrite(keys, addedJids) {
55
50
  const persisted = await readTcTokenIndex(keys)
56
51
  const merged = new Set(persisted)
@@ -61,10 +56,10 @@ async function buildMergedTcTokenIndexWrite(keys, addedJids) {
61
56
  [exports.TC_TOKEN_INDEX_KEY]: { token: Buffer.from(JSON.stringify([...merged])) }
62
57
  }
63
58
  }
64
- // WA Web has separate sender/receiver AB props for these but they're identical today
59
+
65
60
  function isTcTokenExpired(timestamp) {
66
61
  if (timestamp === null || timestamp === undefined) return true
67
- const ts = typeof timestamp === 'string' ? parseInt(timestamp) : timestamp
62
+ const ts = typeof timestamp === 'string' ? parseInt(timestamp, 10) : timestamp
68
63
  if (isNaN(ts)) return true
69
64
  const now = Math.floor(Date.now() / 1000)
70
65
  const currentBucket = Math.floor(now / TC_TOKEN_BUCKET_DURATION)
@@ -79,13 +74,13 @@ function shouldSendNewTcToken(senderTimestamp) {
79
74
  const senderBucket = Math.floor(senderTimestamp / TC_TOKEN_BUCKET_DURATION)
80
75
  return currentBucket > senderBucket
81
76
  }
82
- /** Resolve JID to LID for tctoken storage (WA Web stores under LID) */
77
+
83
78
  async function resolveTcTokenJid(jid, getLIDForPN) {
84
79
  if ((0, WABinary_1.isLidUser)(jid)) return jid
85
80
  const lid = await getLIDForPN(jid)
86
81
  return lid ?? jid
87
82
  }
88
- /** Resolve target JID for issuing privacy token based on AB prop 14303 */
83
+
89
84
  async function resolveIssuanceJid(jid, issueToLid, getLIDForPN, getPNForLID) {
90
85
  if (issueToLid) {
91
86
  if ((0, WABinary_1.isLidUser)(jid)) return jid
@@ -107,9 +102,9 @@ async function buildTcTokenFromJid({ authState, jid, baseContent = [], getLIDFor
107
102
  const tcTokenBuffer = entry?.token
108
103
  if (!tcTokenBuffer?.length || isTcTokenExpired(entry?.timestamp)) {
109
104
  if (tcTokenBuffer) {
110
- // Preserve senderTimestamp so shouldSendNewTcToken() keeps its dedupe state
111
- // after we drop the unusable peer token. Only wipe the record entirely when
112
- // there's nothing worth keeping.
105
+
106
+
107
+
113
108
  const cleared =
114
109
  entry?.senderTimestamp !== undefined
115
110
  ? { token: Buffer.alloc(0), senderTimestamp: entry.senderTimestamp }
@@ -136,7 +131,7 @@ async function storeTcTokensFromIqResult({ result, fallbackJid, keys, getLIDForP
136
131
  if (tokenNode.attrs.type !== 'trusted_contact' || !(tokenNode.content instanceof Uint8Array)) {
137
132
  continue
138
133
  }
139
- // In notifications tokenNode.attrs.jid is your own device JID, not the sender's
134
+
140
135
  const rawJid = (0, WABinary_1.jidNormalizedUser)(fallbackJid || tokenNode.attrs.jid)
141
136
  if (!isRegularUser(rawJid)) continue
142
137
  const storageJid = await resolveTcTokenJid(rawJid, getLIDForPN)
@@ -144,7 +139,7 @@ async function storeTcTokensFromIqResult({ result, fallbackJid, keys, getLIDForP
144
139
  const existingEntry = existingTcData[storageJid]
145
140
  const existingTs = existingEntry?.timestamp ? Number(existingEntry.timestamp) : 0
146
141
  const incomingTs = tokenNode.attrs.t ? Number(tokenNode.attrs.t) : 0
147
- // timestamp-less tokens would be immediately expired
142
+
148
143
  if (!incomingTs) continue
149
144
  if (existingTs > 0 && existingTs > incomingTs) continue
150
145
  await keys.set({
@@ -7,12 +7,12 @@ const path_1 = require('path')
7
7
  const index_js_1 = require('../../WAProto/index.js')
8
8
  const auth_utils_1 = require('./auth-utils')
9
9
  const generics_1 = require('./generics')
10
- // We need to lock files due to the fact that we are using async functions to read and write files
11
- // https://github.com/WhiskeySockets/Baileys/issues/794
12
- // https://github.com/nodejs/node/issues/26338
13
- // Use a Map to store mutexes for each file path
10
+
11
+
12
+
13
+
14
14
  const fileLocks = new Map()
15
- // Get or create a mutex for a specific file path
15
+
16
16
  const getFileLock = path => {
17
17
  let mutex = fileLocks.get(path)
18
18
  if (!mutex) {
@@ -21,21 +21,37 @@ const getFileLock = path => {
21
21
  }
22
22
  return mutex
23
23
  }
24
- /**
25
- * stores the full authentication state in a single folder.
26
- * Far more efficient than singlefileauthstate
27
- *
28
- * Again, I wouldn't endorse this for any production level use other than perhaps a bot.
29
- * Would recommend writing an auth state for use with a proper SQL or No-SQL DB
30
- * */
24
+
31
25
  const useMultiFileAuthState = async folder => {
32
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+
33
27
  const writeData = async (data, file) => {
34
28
  const filePath = (0, path_1.join)(folder, fixFileName(file))
35
29
  const mutex = getFileLock(filePath)
36
30
  return mutex.acquire().then(async release => {
31
+ const tmpPath = `${filePath}.tmp`
32
+
33
+
34
+
35
+
36
+
37
+ const existingStat = await (0, promises_1.stat)(filePath).catch(() => undefined)
37
38
  try {
38
- await (0, promises_1.writeFile)(filePath, JSON.stringify(data, generics_1.BufferJSON.replacer))
39
+ await (0, promises_1.writeFile)(
40
+ tmpPath,
41
+ JSON.stringify(data, generics_1.BufferJSON.replacer),
42
+ existingStat ? { mode: existingStat.mode & 0o777 } : undefined
43
+ )
44
+ try {
45
+ await (0, promises_1.rename)(tmpPath, filePath)
46
+ } catch {
47
+
48
+
49
+ await (0, promises_1.writeFile)(
50
+ filePath,
51
+ JSON.stringify(data, generics_1.BufferJSON.replacer),
52
+ existingStat ? { mode: existingStat.mode & 0o777 } : undefined
53
+ )
54
+ }
39
55
  } finally {
40
56
  release()
41
57
  }
@@ -7,24 +7,48 @@ exports.encodeSignedDeviceIdentity =
7
7
  void 0
8
8
  const boom_1 = require('@hapi/boom')
9
9
  const crypto_1 = require('crypto')
10
- const Crypto_1 = require('./crypto')
10
+ const rb = require('whatsapp-rust-bridge')
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'])
17
18
  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 if (config.syncFullHistory && os === 'Mac OS' && config.browser[1] === 'Desktop') {
30
+
31
+
32
+ platform = index_js_1.proto.ClientPayload.UserAgent.Platform.MACOS
33
+ device = 'Desktop'
34
+ osVersion = '0.1'
35
+ } else {
36
+ platform = index_js_1.proto.ClientPayload.UserAgent.Platform.WEB
37
+ device = 'Desktop'
38
+ osVersion = '0.1'
39
+ }
18
40
  return {
19
41
  appVersion: {
20
42
  primary: config.version[0],
21
43
  secondary: config.version[1],
22
44
  tertiary: config.version[2]
23
45
  },
24
- platform: index_js_1.proto.ClientPayload.UserAgent.Platform.WEB,
46
+ platform,
47
+
48
+
25
49
  releaseChannel: index_js_1.proto.ClientPayload.UserAgent.ReleaseChannel.RELEASE,
26
- osVersion: '0.1',
27
- device: 'Desktop',
50
+ osVersion,
51
+ device,
28
52
  osBuildNumber: '0.1',
29
53
  localeLanguageIso6391: 'en',
30
54
  mnc: '000',
@@ -34,7 +58,9 @@ const getUserAgent = config => {
34
58
  }
35
59
  const PLATFORM_MAP = {
36
60
  'Mac OS': index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.DARWIN,
37
- Windows: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN32
61
+ Windows: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WIN_HYBRID,
62
+ Android: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER,
63
+ iOS: index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
38
64
  }
39
65
  const getWebInfo = config => {
40
66
  let webSubPlatform = index_js_1.proto.ClientPayload.WebInfo.WebSubPlatform.WEB_BROWSER
@@ -49,14 +75,16 @@ const getClientPayload = config => {
49
75
  connectReason: index_js_1.proto.ClientPayload.ConnectReason.USER_ACTIVATED,
50
76
  userAgent: getUserAgent(config)
51
77
  }
52
- payload.webInfo = getWebInfo(config)
78
+ if (!MOBILE_PLATFORMS.has(config.browser[0])) {
79
+ payload.webInfo = getWebInfo(config)
80
+ }
53
81
  return payload
54
82
  }
55
83
  const generateLoginNode = (userJid, config, creds) => {
56
84
  const { user, device } = (0, WABinary_1.jidDecode)(userJid)
57
- // masqueradeAsPrimary: connect as device 0 so the WA server (and interop bridges)
58
- // route primary-device traffic — including interop messages — to this session.
59
- // Trade-off: the real phone (device 0 on TCP) may be kicked or see a conflict.
85
+
86
+
87
+
60
88
  const effectiveDevice = config.masqueradeAsPrimary ? 0 : device
61
89
  const payload = {
62
90
  ...getClientPayload(config),
@@ -66,8 +94,8 @@ const generateLoginNode = (userJid, config, creds) => {
66
94
  device: effectiveDevice,
67
95
  lidDbMigrated: false
68
96
  }
69
- // InteropData is required for Meta/FB-linked accounts (AccountType = HOSTED).
70
- // Populated from creds when present so the server routes interop traffic correctly.
97
+
98
+
71
99
  if (creds?.interopData?.accountId) {
72
100
  payload.interopData = {
73
101
  accountId: creds.interopData.accountId,
@@ -83,18 +111,23 @@ const getPlatformType = platform => {
83
111
  return index_js_1.proto.DeviceProps.PlatformType[platformType] || index_js_1.proto.DeviceProps.PlatformType.CHROME
84
112
  }
85
113
  const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentityKey }, config) => {
86
- // the app version needs to be md5 hashed
87
- // and passed in
88
- const appVersionBuf = Buffer.from(Crypto_1.md5(Buffer.from(config.version.join('.'))))
114
+
115
+
116
+ const appVersionBuf = Buffer.from(rb.md5(Buffer.from(config.version.join('.'))))
89
117
  const companion = {
90
118
  os: config.browser[0],
91
119
  platformType: getPlatformType(config.browser[1]),
92
120
  requireFullSync: config.syncFullHistory,
93
121
  historySyncConfig: {
122
+ fullSyncDaysLimit: 30,
123
+ fullSyncSizeMbLimit: undefined,
94
124
  storageQuotaMb: 10240,
95
125
  inlineInitialPayloadInE2EeMsg: true,
96
126
  recentSyncDaysLimit: undefined,
97
- supportCallLogHistory: false,
127
+
128
+
129
+
130
+ supportCallLogHistory: config.browser[0] === 'Windows',
98
131
  supportBotUserAgentChatHistory: true,
99
132
  supportCagReactionsAndPolls: true,
100
133
  supportBizHostedMsg: true,
@@ -103,9 +136,16 @@ const generateRegistrationNode = ({ registrationId, signedPreKey, signedIdentity
103
136
  supportFbidBotChatHistory: true,
104
137
  supportAddOnHistorySyncMigration: undefined,
105
138
  supportMessageAssociation: true,
106
- supportGroupHistory: false,
139
+ supportGroupHistory: true,
107
140
  onDemandReady: undefined,
108
- supportGuestChat: undefined
141
+ supportGuestChat: true,
142
+ completeOnDemandReady: undefined,
143
+ thumbnailSyncDaysLimit: undefined,
144
+ initialSyncMaxMessagesPerChat: undefined,
145
+ supportManusHistory: true,
146
+ supportHatchHistory: true,
147
+ supportedBotChannelFbids: undefined,
148
+ supportInlineContacts: true
109
149
  },
110
150
  version: {
111
151
  primary: 10,
@@ -209,8 +249,8 @@ const configureSuccessfulPairing = (stanza, { advSecretKey, signedIdentityKey, s
209
249
  exports.configureSuccessfulPairing = configureSuccessfulPairing
210
250
  const encodeSignedDeviceIdentity = (account, includeSignatureKey) => {
211
251
  account = { ...account }
212
- // set to null if we are not to include the signature key
213
- // or if we are including the signature key but it is empty
252
+
253
+
214
254
  if (!includeSignatureKey || !account.accountSignatureKey?.length) {
215
255
  account.accountSignatureKey = null
216
256
  }
@@ -56,17 +56,37 @@ 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')
59
60
  const inflatePromise = (0, util_1.promisify)(zlib_1.inflate)
60
61
  const decompressingIfRequired = async buffer => {
61
62
  if (2 & buffer.readUInt8()) {
62
63
  buffer = await inflatePromise(buffer.slice(1))
63
64
  } else {
64
- // nodes with no compression have a 0x00 prefix, we remove that
65
+
65
66
  buffer = buffer.slice(1)
66
67
  }
67
68
  return buffer
68
69
  }
69
70
  exports.decompressingIfRequired = decompressingIfRequired
71
+
72
+
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
+
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
+ }
70
90
  const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) => {
71
91
  const { DOUBLE_BYTE_TOKENS, SINGLE_BYTE_TOKENS, TAGS } = opts
72
92
  const checkEOS = length => {
@@ -90,7 +110,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
90
110
  return value
91
111
  }
92
112
  const readStringFromChars = length => {
93
- return readBytes(length).toString('utf-8')
113
+ return readBytes(length).toString('utf8')
94
114
  }
95
115
  const readInt = (n, littleEndian = false) => {
96
116
  checkEOS(n)
@@ -176,7 +196,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
176
196
  const domainType = Number(rawDomainType)
177
197
  const device = readByte()
178
198
  const user = readString(readByte())
179
- let server = 's.whatsapp.net' // default whatsapp server
199
+ let server = 's.whatsapp.net'
180
200
  if (domainType === jid_utils_1.WAJIDDomains.LID) {
181
201
  server = 'lid'
182
202
  } else if (domainType === jid_utils_1.WAJIDDomains.HOSTED) {
@@ -197,13 +217,13 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
197
217
  const device = readInt(2)
198
218
  const integrator = readInt(2)
199
219
  let server = 'interop'
200
- // Only attempt to read the optional server field if at least 1 byte remains
201
- // and the next byte is a valid string tag (avoids corrupting the stream on
202
- // unknown tags that readString would swallow silently after a catch).
220
+
221
+
222
+
203
223
  if (indexRef.index < buffer.length) {
204
224
  const serverTag = buffer[indexRef.index]
205
- // BINARY_8 / BINARY_20 / BINARY_32 or a single-byte token (1–235) are
206
- // valid string tags; LIST_EMPTY (0) means no server string follows.
225
+
226
+
207
227
  const isValidStringTag =
208
228
  (serverTag >= 1 && serverTag <= 235) ||
209
229
  serverTag === TAGS.BINARY_8 ||
@@ -212,7 +232,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
212
232
  serverTag === TAGS.NIBBLE_8 ||
213
233
  serverTag === TAGS.HEX_8
214
234
  if (isValidStringTag) {
215
- indexRef.index++ // consume the tag byte
235
+ indexRef.index++
216
236
  server = readString(serverTag)
217
237
  }
218
238
  }
@@ -261,7 +281,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
261
281
  case TAGS.NIBBLE_8:
262
282
  return readPacked8(tag)
263
283
  default:
264
- // Unknown tag — token table may be outdated (new WA protocol version)
284
+
265
285
  process.stderr.write(`[WABinary] unknown string tag 0x${tag.toString(16)} — token table outdated?\n`)
266
286
  return ''
267
287
  }
@@ -277,14 +297,16 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
277
297
  const getTokenDouble = (index1, index2) => {
278
298
  const dict = DOUBLE_BYTE_TOKENS[index1]
279
299
  if (!dict) {
280
- // Unknown dictionary — token table may be outdated (new WA protocol version)
300
+
281
301
  process.stderr.write(`[WABinary] unknown double-byte token dict ${index1} — token table outdated?\n`)
282
302
  return ''
283
303
  }
284
304
  const value = dict[index2]
285
305
  if (typeof value === 'undefined') {
286
- // Unknown token in known dictionary — token table may be outdated
287
- process.stderr.write(`[WABinary] unknown double-byte token dict=${index1} idx=${index2} — token table outdated?\n`)
306
+
307
+ process.stderr.write(
308
+ `[WABinary] unknown double-byte token dict=${index1} idx=${index2} — token table outdated?\n`
309
+ )
288
310
  return ''
289
311
  }
290
312
  return value
@@ -299,7 +321,7 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
299
321
  if (listSize === 0 || !header) {
300
322
  throw new Error('invalid node')
301
323
  }
302
- // read the attributes in
324
+
303
325
  const attributesLength = (listSize - 1) >> 1
304
326
  for (let i = 0; i < attributesLength; i++) {
305
327
  const key = readString(readByte())
@@ -336,8 +358,20 @@ const decodeDecompressedBinaryNode = (buffer, opts, indexRef = { index: 0 }) =>
336
358
  }
337
359
  }
338
360
  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
+ }
339
366
  const decodeBinaryNode = buff => {
340
- const buffer = Buffer.isBuffer(buff) ? buff : Buffer.from(buff)
341
- return decodeDecompressedBinaryNode(buffer, constants)
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
+
373
+
374
+ return (0, exports.decodeDecompressedBinaryNode)(buff, JS_DECODE_OPTS)
375
+ }
342
376
  }
343
377
  exports.decodeBinaryNode = decodeBinaryNode