@badzz88/baileys 8.4.9 → 8.5.0

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 (153) hide show
  1. package/WAProto/WAProto.proto +6 -347
  2. package/WAProto/fix-imports.js +85 -86
  3. package/WAProto/index.d.ts +25 -4913
  4. package/WAProto/index.js +93 -14069
  5. package/{src → lib}/Defaults/index.js +2 -13
  6. package/{src → lib}/Signal/Group/group_cipher.js +5 -5
  7. package/{src → lib}/Signal/Group/keyhelper.js +4 -4
  8. package/{src → lib}/Signal/Group/sender-chain-key.js +2 -2
  9. package/{src → lib}/Signal/Group/sender-key-distribution-message.js +1 -1
  10. package/{src → lib}/Signal/Group/sender-key-message.js +3 -3
  11. package/{src → lib}/Signal/Group/sender-key-record.js +1 -1
  12. package/{src → lib}/Signal/Group/sender-message-key.js +2 -2
  13. package/{src → lib}/Signal/libsignal.js +26 -6
  14. package/{src → lib}/Socket/chats.js +58 -616
  15. package/{src → lib}/Socket/communities.js +28 -59
  16. package/{src → lib}/Socket/graphql.js +103 -362
  17. package/{src → lib}/Socket/groups.js +29 -77
  18. package/{src → lib}/Socket/index.js +5 -3
  19. package/{src → lib}/Socket/interactive-handler.js +60 -117
  20. package/{src → lib}/Socket/interop.js +19 -36
  21. package/lib/Socket/luxu.js +387 -0
  22. package/{src → lib}/Socket/managed-account.js +27 -58
  23. package/{src → lib}/Socket/messages-recv.js +143 -715
  24. package/{src → lib}/Socket/messages-send.js +102 -412
  25. package/lib/Socket/mex.js +60 -0
  26. package/{src → lib}/Socket/newsletter.js +23 -266
  27. package/{src → lib}/Socket/privacy.js +38 -41
  28. package/{src → lib}/Socket/registration.js +106 -113
  29. package/{src → lib}/Socket/socket.js +101 -88
  30. package/{src → lib}/Socket/text-router.js +17 -1
  31. package/lib/Socket/username.js +243 -0
  32. package/lib/Store/keyed-db.js +108 -0
  33. package/{src → lib}/Store/make-in-memory-store.js +104 -6
  34. package/lib/Types/Mex.js +112 -0
  35. package/{src → lib}/Types/Newsletter.js +41 -72
  36. package/{src → lib}/Types/index.js +1 -0
  37. package/{src → lib}/Utils/chat-utils.js +9 -73
  38. package/lib/Utils/command-loader.d.ts +31 -0
  39. package/lib/Utils/command-loader.js +100 -0
  40. package/lib/Utils/companion-reg-client-utils.js +42 -0
  41. package/lib/Utils/crypto.js +155 -0
  42. package/lib/Utils/curve25519-js.js +275 -0
  43. package/{src → lib}/Utils/decode-wa-message.js +36 -284
  44. package/{src → lib}/Utils/event-buffer.js +27 -6
  45. package/{src → lib}/Utils/generics.js +37 -114
  46. package/{src → lib}/Utils/index.js +5 -1
  47. package/lib/Utils/lt-hash.js +39 -0
  48. package/{src → lib}/Utils/message-retry-manager.js +39 -0
  49. package/{src → lib}/Utils/messages-media.js +58 -105
  50. package/{src → lib}/Utils/messages.js +529 -115
  51. package/lib/Utils/meta-ai-msmsg.js +262 -0
  52. package/lib/Utils/native-bridge.js +82 -0
  53. package/{src → lib}/Utils/noise-handler.js +15 -13
  54. package/{src → lib}/Utils/process-message.js +55 -54
  55. package/{src → lib}/Utils/reporting-utils.js +2 -2
  56. package/lib/Utils/session-pool.d.ts +16 -0
  57. package/lib/Utils/session-pool.js +94 -0
  58. package/{src → lib}/Utils/signal.js +51 -19
  59. package/{src → lib}/Utils/stanza-ack.js +2 -36
  60. package/lib/Utils/sticker.d.ts +17 -0
  61. package/lib/Utils/sticker.js +145 -0
  62. package/{src → lib}/Utils/tc-token-utils.js +1 -1
  63. package/{src → lib}/Utils/validate-connection.js +11 -40
  64. package/lib/Utils/view-once-cache.d.ts +9 -0
  65. package/lib/Utils/view-once-cache.js +79 -0
  66. package/{src → lib}/WABinary/decode.js +4 -38
  67. package/lib/WABinary/encode.js +228 -0
  68. package/{src → lib}/WABinary/generic-utils.js +93 -1
  69. package/{src → lib}/WABinary/jid-utils.js +3 -16
  70. package/{src → lib}/WAM/encode.js +1 -1
  71. package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
  72. package/{src → lib}/WAUSync/Protocols/USyncContactProtocol.js +1 -7
  73. package/{src → lib}/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
  74. package/{src → lib}/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
  75. package/{src → lib}/WAUSync/Protocols/USyncFeatureProtocol.js +2 -17
  76. package/{src → lib}/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
  77. package/{src → lib}/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
  78. package/{src → lib}/WAUSync/Protocols/index.js +0 -1
  79. package/{src → lib}/WAUSync/USyncQuery.js +6 -77
  80. package/{src → lib}/WAUSync/USyncUser.js +8 -16
  81. package/{src → lib}/WAUSync/index.js +0 -1
  82. package/{src → lib}/antiban.js +266 -355
  83. package/{src → lib}/index.js +2 -2
  84. package/package.json +18 -15
  85. package/WAProto/fix-import.js +0 -38
  86. package/src/Socket/mex.js +0 -47
  87. package/src/Socket/username.js +0 -234
  88. package/src/Utils/crypto.js +0 -125
  89. package/src/Utils/lt-hash.js +0 -10
  90. package/src/Utils/message-inspect.js +0 -400
  91. package/src/Utils/meta-ai-msmsg.js +0 -133
  92. package/src/WABinary/encode.js +0 -58
  93. package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -100
  94. package/src/WAUSync/USyncBackoff.js +0 -31
  95. /package/{src → lib}/Defaults/phonenumber-mcc.json +0 -0
  96. /package/{src → lib}/Signal/Group/ciphertext-message.js +0 -0
  97. /package/{src → lib}/Signal/Group/group-session-builder.js +0 -0
  98. /package/{src → lib}/Signal/Group/index.js +0 -0
  99. /package/{src → lib}/Signal/Group/sender-key-name.js +0 -0
  100. /package/{src → lib}/Signal/Group/sender-key-state.js +0 -0
  101. /package/{src → lib}/Signal/lid-mapping.js +0 -0
  102. /package/{src → lib}/Socket/Client/index.js +0 -0
  103. /package/{src → lib}/Socket/Client/types.js +0 -0
  104. /package/{src → lib}/Socket/Client/websocket.js +0 -0
  105. /package/{src → lib}/Socket/aigroups.js +0 -0
  106. /package/{src → lib}/Socket/business.js +0 -0
  107. /package/{src → lib}/Store/index.js +0 -0
  108. /package/{src → lib}/Store/make-cache-manager-store.js +0 -0
  109. /package/{src → lib}/Store/make-ordered-dictionary.js +0 -0
  110. /package/{src → lib}/Store/object-repository.js +0 -0
  111. /package/{src → lib}/Types/Auth.js +0 -0
  112. /package/{src → lib}/Types/Bussines.js +0 -0
  113. /package/{src → lib}/Types/Call.js +0 -0
  114. /package/{src → lib}/Types/Chat.js +0 -0
  115. /package/{src → lib}/Types/Contact.js +0 -0
  116. /package/{src → lib}/Types/Events.js +0 -0
  117. /package/{src → lib}/Types/GroupMetadata.js +0 -0
  118. /package/{src → lib}/Types/Label.js +0 -0
  119. /package/{src → lib}/Types/LabelAssociation.js +0 -0
  120. /package/{src → lib}/Types/Message.js +0 -0
  121. /package/{src → lib}/Types/Product.js +0 -0
  122. /package/{src → lib}/Types/Signal.js +0 -0
  123. /package/{src → lib}/Types/Socket.js +0 -0
  124. /package/{src → lib}/Types/State.js +0 -0
  125. /package/{src → lib}/Types/USync.js +0 -0
  126. /package/{src → lib}/Utils/auth-utils.js +0 -0
  127. /package/{src → lib}/Utils/browser-utils.js +0 -0
  128. /package/{src → lib}/Utils/business.js +0 -0
  129. /package/{src → lib}/Utils/consumer-application.js +0 -0
  130. /package/{src → lib}/Utils/group-history.js +0 -0
  131. /package/{src → lib}/Utils/history.js +0 -0
  132. /package/{src → lib}/Utils/identity-change-handler.js +0 -0
  133. /package/{src → lib}/Utils/jid-display-normalization.js +0 -0
  134. /package/{src → lib}/Utils/link-preview.js +0 -0
  135. /package/{src → lib}/Utils/logger.js +0 -0
  136. /package/{src → lib}/Utils/make-mutex.js +0 -0
  137. /package/{src → lib}/Utils/message-composer.js +0 -0
  138. /package/{src → lib}/Utils/offline-node-processor.js +0 -0
  139. /package/{src → lib}/Utils/pre-key-manager.js +0 -0
  140. /package/{src → lib}/Utils/sync-action-utils.js +0 -0
  141. /package/{src → lib}/Utils/use-multi-file-auth-state.js +0 -0
  142. /package/{src → lib}/Utils/voip-rekey.js +0 -0
  143. /package/{src → lib}/WABinary/constants.js +0 -0
  144. /package/{src → lib}/WABinary/index.js +0 -0
  145. /package/{src → lib}/WABinary/types.js +0 -0
  146. /package/{src → lib}/WAM/BinaryInfo.js +0 -0
  147. /package/{src → lib}/WAM/constants.js +0 -0
  148. /package/{src → lib}/WAM/index.js +0 -0
  149. /package/{src → lib}/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -0
  150. /package/{src → lib}/WAUSync/Protocols/USyncLIDProtocol.js +0 -0
  151. /package/{src → lib}/WAUSync/Protocols/USyncPictureProtocol.js +0 -0
  152. /package/{src → lib}/WAUSync/Protocols/USyncSidelistProtocol.js +0 -0
  153. /package/{src → lib}/WAUSync/Protocols/USyncTextStatusProtocol.js +0 -0
@@ -91,8 +91,6 @@ const makeChatsSocket = config => {
91
91
  // Collections blocked on missing app state sync keys (mirrors WA Web's "Blocked" state).
92
92
  // When a key arrives via APP_STATE_SYNC_KEY_SHARE, these are re-synced.
93
93
  const blockedCollections = new Set()
94
- /** messageId → numeric server_id for newsletter messages (persists within session) */
95
- const _nlServerIdCache = new Map()
96
94
  const placeholderResendCache =
97
95
  config.placeholderResendCache ||
98
96
  new node_cache_1.default({
@@ -118,63 +116,7 @@ const makeChatsSocket = config => {
118
116
  },
119
117
  content: [{ tag: 'privacy', attrs: {} }]
120
118
  })
121
- const privacyNode = content?.[0]
122
- privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(privacyNode, 'category')
123
-
124
- // A. Online Privacy Mode — may arrive as <category name="online" value="..."/> or
125
- // as a dedicated <online_privacy_setting value="..."/> child node.
126
- const onlinePrivacy =
127
- privacySettings['online'] ||
128
- (0, WABinary_1.getBinaryNodeChild)(privacyNode, 'online_privacy_setting')?.attrs?.value ||
129
- 'all'
130
-
131
- // C. Enhanced Block — root attrs or dedicated child node.
132
- const enhancedBlockEnabled =
133
- privacyNode?.attrs?.enhanced_block_enabled === 'true' ||
134
- (0, WABinary_1.getBinaryNodeChild)(privacyNode, 'enhanced_block')?.attrs?.enabled === 'true'
135
-
136
- // E. MD Privacy V2 flag.
137
- const mdPrivacyV2 =
138
- privacyNode?.attrs?.md_privacy_v2 === 'true' ||
139
- (0, WABinary_1.getBinaryNodeChild)(privacyNode, 'md_privacy_v2')?.attrs?.value === 'true'
140
-
141
- // F. Syncd clear-chat / delete-chat flag.
142
- const syncdClearChatDeleteChatEnabled =
143
- privacyNode?.attrs?.syncd_clear_chat_delete_chat_enabled === 'true' ||
144
- (0, WABinary_1.getBinaryNodeChild)(privacyNode, 'syncd_clear_chat')?.attrs?.delete_chat_enabled === 'true'
145
-
146
- // A. Syncd anti-tampering fatal-exception gate.
147
- const syncdAntiTamperingEnabled =
148
- privacyNode?.attrs?.syncd_anti_tampering_fatal_exception_enabled === 'true' ||
149
- (0, WABinary_1.getBinaryNodeChild)(privacyNode, 'syncd_anti_tampering')?.attrs?.fatal_exception_enabled ===
150
- 'true'
151
-
152
- // B. Syncd key-index rotation gate.
153
- const keyRotationEnabled =
154
- privacyNode?.attrs?.syncd_key_rotation_enabled === 'true' ||
155
- (0, WABinary_1.getBinaryNodeChild)(privacyNode, 'syncd_key_rotation')?.attrs?.enabled === 'true'
156
-
157
- // Emit combined settings update so consumers can react without parsing raw creds.
158
- ev.emit('settings.update', {
159
- onlinePrivacy,
160
- enhancedBlockEnabled,
161
- mdPrivacyV2,
162
- syncdClearChatDeleteChatEnabled,
163
- ...(syncdAntiTamperingEnabled ? { syncdAntiTamperingEnabled: true } : {}),
164
- keyRotationEnabled
165
- })
166
-
167
- // Persist extended privacy flags in credentials.
168
- ev.emit('creds.update', {
169
- privacySettings: {
170
- onlinePrivacy,
171
- enhancedBlockEnabled,
172
- mdPrivacyV2,
173
- syncdClearChatDeleteChatEnabled,
174
- syncdAntiTamperingEnabled,
175
- keyRotationEnabled
176
- }
177
- })
119
+ privacySettings = (0, WABinary_1.reduceBinaryNodeToDictionary)(content?.[0], 'category')
178
120
  }
179
121
  return privacySettings
180
122
  }
@@ -237,7 +179,7 @@ const makeChatsSocket = config => {
237
179
  const privacyNode = result?.content?.[0]
238
180
  if (!privacyNode) return null
239
181
  const lists = []
240
- for (const listNode of privacyNode.content || []) {
182
+ for (const listNode of (privacyNode.content || [])) {
241
183
  const { type, id, listname, emoji, selected, deleted } = listNode.attrs || {}
242
184
  const members = (listNode.content || []).map(u => u.attrs?.jid).filter(Boolean)
243
185
  lists.push({ type, id, listname, emoji, selected: selected === 'true', deleted: deleted === 'true', members })
@@ -330,35 +272,12 @@ const makeChatsSocket = config => {
330
272
  const timeframeNode = (0, WABinary_1.getBinaryNodeChild)(result, 'timeframe')
331
273
  if (!limitsNode) return null
332
274
  return {
333
- messagesLeft: parseInt(
334
- limitsNode.attrs?.messages_left ??
335
- (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'messages_left')?.content ??
336
- '0',
337
- 10
338
- ),
339
- totalLimit: parseInt(
340
- limitsNode.attrs?.total_limit ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'total_limit')?.content ?? '0',
341
- 10
342
- ),
343
- isHeavySender:
344
- (limitsNode.attrs?.is_heavy_sender ??
345
- (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'is_heavy_sender')?.content) === 'true',
346
- startTs: parseInt(
347
- timeframeNode?.attrs?.start_ts_s ??
348
- (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'start_ts_s')?.content ??
349
- '0',
350
- 10
351
- ),
352
- endTs: parseInt(
353
- timeframeNode?.attrs?.end_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'end_ts_s')?.content ?? '0',
354
- 10
355
- ),
356
- resetTs: parseInt(
357
- timeframeNode?.attrs?.reset_ts_s ??
358
- (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'reset_ts_s')?.content ??
359
- '0',
360
- 10
361
- )
275
+ messagesLeft: parseInt(limitsNode.attrs?.messages_left ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'messages_left')?.content ?? '0'),
276
+ totalLimit: parseInt(limitsNode.attrs?.total_limit ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'total_limit')?.content ?? '0'),
277
+ isHeavySender: (limitsNode.attrs?.is_heavy_sender ?? (0, WABinary_1.getBinaryNodeChild)(limitsNode, 'is_heavy_sender')?.content) === 'true',
278
+ startTs: parseInt(timeframeNode?.attrs?.start_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'start_ts_s')?.content ?? '0'),
279
+ endTs: parseInt(timeframeNode?.attrs?.end_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'end_ts_s')?.content ?? '0'),
280
+ resetTs: parseInt(timeframeNode?.attrs?.reset_ts_s ?? (0, WABinary_1.getBinaryNodeChild)(timeframeNode, 'reset_ts_s')?.content ?? '0')
362
281
  }
363
282
  }
364
283
 
@@ -446,52 +365,6 @@ const makeChatsSocket = config => {
446
365
  })
447
366
  return (0, WABinary_1.getBinaryNodeChildren)(result, 'notice').map(n => ({ ...n.attrs }))
448
367
  }
449
- /**
450
- * Accept a TOS/disclosure notice (`xmlns="tos"` set with `<trackable>`).
451
- * Mirrors WASmaxUserNoticeTrackDisclosureRPC from WA Web.
452
- * @param {string} noticeId the notice id (e.g. "20250211")
453
- * @param {string|number} result the result code (e.g. "105")
454
- */
455
- const acceptTosNotice = async (noticeId, result = '105') => {
456
- await query({
457
- tag: 'iq',
458
- attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'tos', type: 'set' },
459
- content: [{ tag: 'trackable', attrs: { id: String(noticeId), result: String(result) } }]
460
- })
461
- }
462
- /**
463
- * Report spam for a chat/group with sample messages.
464
- * Mirrors WASmaxReportSpamRPC (xmlns="spam", type="set").
465
- * @param {string} jid the JID of the chat or group being reported
466
- * @param {Array<{id:string, from:string, t:number}>} messages up to ~5 recent messages as evidence
467
- * @param {string} [spamFlow] e.g. "group_info_report" or "contact_info_report"
468
- * @param {string} [subject] human-readable subject string (group name or contact name)
469
- */
470
- const reportSpam = async (jid, messages, spamFlow = 'contact_info_report', subject) => {
471
- const msgNodes = (messages || []).map(m => ({
472
- tag: 'message',
473
- attrs: {
474
- from: String(jid),
475
- t: String(m.t),
476
- id: String(m.id)
477
- }
478
- }))
479
- await query({
480
- tag: 'iq',
481
- attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'spam', type: 'set' },
482
- content: [
483
- {
484
- tag: 'spam_list',
485
- attrs: {
486
- jid: String(jid),
487
- spam_flow: String(spamFlow),
488
- ...(subject ? { subject: String(subject) } : {})
489
- },
490
- content: msgNodes
491
- }
492
- ]
493
- })
494
- }
495
368
  /**
496
369
  * Get the account opt-out list (`optoutlist` IQ). Returns the raw result node.
497
370
  */
@@ -501,28 +374,6 @@ const makeChatsSocket = config => {
501
374
  attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'optoutlist', type: 'get' }
502
375
  })
503
376
  }
504
- /**
505
- * Sign a blinded credential for privacy-preserving anonymous stats.
506
- * Mirrors WASmaxPrivateStatsSignCredentialRPC (xmlns `privatestats`).
507
- * @param {Buffer} blindedCredential 32-byte blinded credential
508
- * @returns {Promise<Buffer|undefined>} signed credential bytes from server
509
- */
510
- const signPrivateCredential = async blindedCredential => {
511
- const result = await query({
512
- tag: 'iq',
513
- attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'privatestats', type: 'get' },
514
- content: [
515
- {
516
- tag: 'sign_credential',
517
- attrs: { version: '1' },
518
- content: [{ tag: 'blinded_credential', attrs: {}, content: blindedCredential }]
519
- }
520
- ]
521
- })
522
- const signedNode = (0, WABinary_1.getBinaryNodeChild)(result, 'sign_credential')
523
- const signedBytes = (0, WABinary_1.getBinaryNodeChild)(signedNode, 'signed_credential')
524
- return signedBytes?.content instanceof Uint8Array ? Buffer.from(signedBytes.content) : undefined
525
- }
526
377
  /**
527
378
  * Get push-notification settings (`urn:xmpp:whatsapp:push`).
528
379
  */
@@ -551,7 +402,7 @@ const makeChatsSocket = config => {
551
402
  for (const jid of jids) {
552
403
  usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid))
553
404
  }
554
- const result = await executeUSyncQuery(usyncQuery)
405
+ const result = await sock.executeUSyncQuery(usyncQuery)
555
406
  if (result) {
556
407
  return result.list
557
408
  }
@@ -561,7 +412,7 @@ const makeChatsSocket = config => {
561
412
  for (const jid of jids) {
562
413
  usyncQuery.withUser(new WAUSync_1.USyncUser().withId(jid))
563
414
  }
564
- const result = await executeUSyncQuery(usyncQuery)
415
+ const result = await sock.executeUSyncQuery(usyncQuery)
565
416
  if (result) {
566
417
  return result.list
567
418
  }
@@ -641,24 +492,17 @@ const makeChatsSocket = config => {
641
492
  const updateProfileName = async name => {
642
493
  await chatModify({ pushNameSetting: name }, '')
643
494
  }
644
- // dhash: previously-seen blocklist delta-hash, for incremental sync — server returns
645
- // the current dhash unconditionally, and a fresh list only when it doesn't match.
646
- // Confirmed from live capture: content is null for a full fetch, or a single
647
- // <item dhash="..."/> child for the incremental check — never a <blocklist/> child.
648
- const fetchBlocklist = async (dhash = null) => {
495
+ const fetchBlocklist = async () => {
649
496
  const result = await query({
650
497
  tag: 'iq',
651
498
  attrs: {
652
499
  xmlns: 'blocklist',
653
500
  to: WABinary_1.S_WHATSAPP_NET,
654
501
  type: 'get'
655
- },
656
- content: dhash ? [{ tag: 'item', attrs: { dhash } }] : null
502
+ }
657
503
  })
658
504
  const listNode = (0, WABinary_1.getBinaryNodeChild)(result, 'list')
659
- const jids = (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item').map(n => n.attrs.jid)
660
- ev.emit('blocklist.set', { blocklist: jids })
661
- return jids
505
+ return (0, WABinary_1.getBinaryNodeChildren)(listNode, 'item').map(n => n.attrs.jid)
662
506
  }
663
507
  const updateBlockStatus = async (jid, action) => {
664
508
  await query({
@@ -680,67 +524,26 @@ const makeChatsSocket = config => {
680
524
  })
681
525
  }
682
526
  const getBusinessProfile = async jid => {
683
- // Confirmed from live capture: business_profile uses v="116" (not "244"), and
684
- // verified_name is always its own separate w:biz iq — { jid } attr, no xmlns
685
- // child attr — never bundled into the business_profile request.
686
- const [results, verifiedNameResult] = await Promise.all([
687
- query({
688
- tag: 'iq',
689
- attrs: {
690
- to: 's.whatsapp.net',
691
- xmlns: 'w:biz',
692
- type: 'get'
693
- },
694
- content: [
695
- {
696
- tag: 'business_profile',
697
- attrs: { v: '116' },
698
- content: [
699
- {
700
- tag: 'profile',
701
- attrs: { jid }
702
- }
703
- ]
704
- }
705
- ]
706
- }),
707
- query({
708
- tag: 'iq',
709
- attrs: {
710
- to: 's.whatsapp.net',
711
- xmlns: 'w:biz',
712
- type: 'get'
713
- },
714
- content: [
715
- {
716
- tag: 'verified_name',
717
- attrs: { jid }
718
- }
719
- ]
720
- })
721
- ])
722
- // Parse verified_name certificate if present in the response
723
- const verifiedNameNode = (0, WABinary_1.getBinaryNodeChild)(verifiedNameResult, 'verified_name')
724
- let verifiedNameCert
725
- if (verifiedNameNode) {
726
- const certNode = (0, WABinary_1.getBinaryNodeChild)(verifiedNameNode, 'certificate')
727
- if (certNode) {
728
- const detailsNode = (0, WABinary_1.getBinaryNodeChild)(certNode, 'details')
729
- const localizedNames = (0, WABinary_1.getBinaryNodeChildren)(certNode, 'localized_name').map(n => ({
730
- lg: n.attrs?.lg,
731
- lc: n.attrs?.lc,
732
- verifiedName: n.attrs?.verified_name || n.content?.toString()
733
- }))
734
- verifiedNameCert = {
735
- certSerial: certNode.attrs?.serial ? +certNode.attrs.serial : undefined,
736
- issuer: certNode.attrs?.issuer,
737
- details: {
738
- verifiedName: detailsNode?.attrs?.verified_name || detailsNode?.content?.toString(),
739
- localizedNames
740
- }
527
+ const results = await query({
528
+ tag: 'iq',
529
+ attrs: {
530
+ to: 's.whatsapp.net',
531
+ xmlns: 'w:biz',
532
+ type: 'get'
533
+ },
534
+ content: [
535
+ {
536
+ tag: 'business_profile',
537
+ attrs: { v: '244' },
538
+ content: [
539
+ {
540
+ tag: 'profile',
541
+ attrs: { jid }
542
+ }
543
+ ]
741
544
  }
742
- }
743
- }
545
+ ]
546
+ })
744
547
  const profileNode = (0, WABinary_1.getBinaryNodeChild)(results, 'business_profile')
745
548
  const profiles = (0, WABinary_1.getBinaryNodeChild)(profileNode, 'profile')
746
549
  if (profiles) {
@@ -757,31 +560,9 @@ const makeChatsSocket = config => {
757
560
  ? (0, WABinary_1.getBinaryNodeChildren)(businessHours, 'business_hours_config')
758
561
  : undefined
759
562
  const websiteStr = website?.content?.toString()
760
-
761
- // Catalog status from profile attrs
762
- const catalogStatus = {
763
- exists: profiles.attrs?.catalog_exists === 'true' || profiles.attrs?.catalog_exists === true || false,
764
- sendAll: profiles.attrs?.catalog_send_all === 'true' || profiles.attrs?.catalog_send_all === true || false
765
- }
766
-
767
- // Cart flags from profile attrs
768
- const cartEnabled =
769
- profiles.attrs?.cart_enabled !== undefined
770
- ? profiles.attrs.cart_enabled === 'true' || profiles.attrs.cart_enabled === true
771
- : undefined
772
- const webCartEnabled =
773
- profiles.attrs?.web_cart_enabled !== undefined
774
- ? profiles.attrs.web_cart_enabled === 'true' || profiles.attrs.web_cart_enabled === true
775
- : undefined
776
- const webCartOnOff = profiles.attrs?.web_cart_on_off
777
-
778
- // Commerce experience from profile attrs
779
- const commerceExperience = profiles.attrs?.commerce_experience
780
-
781
563
  return {
782
564
  wid: profiles.attrs?.jid,
783
565
  address: address?.content?.toString(),
784
- businessAddress: address?.content?.toString(),
785
566
  description: description?.content?.toString() || '',
786
567
  website: websiteStr ? [websiteStr] : [],
787
568
  email: email?.content?.toString(),
@@ -789,24 +570,7 @@ const makeChatsSocket = config => {
789
570
  business_hours: {
790
571
  timezone: businessHours?.attrs?.timezone,
791
572
  business_config: businessHoursConfig?.map(({ attrs }) => attrs)
792
- },
793
- businessHours: businessHoursConfig
794
- ? {
795
- timezone: businessHours?.attrs?.timezone ?? null,
796
- config: businessHoursConfig.map(({ attrs }) => ({
797
- dayOfWeek: attrs?.day_of_week ?? null,
798
- openTime: attrs?.open_time ?? null,
799
- closeTime: attrs?.close_time ?? null,
800
- mode: attrs?.mode ?? null
801
- }))
802
- }
803
- : null,
804
- catalogStatus,
805
- cartEnabled,
806
- webCartEnabled,
807
- webCartOnOff,
808
- commerceExperience,
809
- verifiedNameCert
573
+ }
810
574
  }
811
575
  }
812
576
  }
@@ -841,13 +605,6 @@ const makeChatsSocket = config => {
841
605
  isInitialSync ? { accountSettings: authState.creds.accountSettings } : undefined,
842
606
  logger
843
607
  )
844
- // Persist NCT salt received via App-State-Sync (SyncActionValue field 80)
845
- const _nctSalt = mutation?.syncAction?.value?.nctSaltSyncAction?.salt
846
- if (_nctSalt?.length) {
847
- authState.keys
848
- .set({ 'nct-salt': { default: Buffer.from(_nctSalt) } })
849
- .catch(err => logger.debug({ err: err?.message }, 'nct: failed to persist salt'))
850
- }
851
608
  }
852
609
  }
853
610
  }
@@ -855,7 +612,7 @@ const makeChatsSocket = config => {
855
612
  const appStateSyncKeyCache = new Map()
856
613
  const getCachedAppStateSyncKey = async keyId => {
857
614
  if (appStateSyncKeyCache.has(keyId)) {
858
- return appStateSyncKeyCache.get(keyId)
615
+ return appStateSyncKeyCache.get(keyId) ?? undefined
859
616
  }
860
617
  const key = await getAppStateSyncKey(keyId)
861
618
  appStateSyncKeyCache.set(keyId, key ?? null)
@@ -880,7 +637,7 @@ const makeChatsSocket = config => {
880
637
  const result = await authState.keys.get('app-state-sync-version', [name])
881
638
  let state = result[name]
882
639
  if (state) {
883
- if (initialVersionMap[name] === undefined) {
640
+ if (typeof initialVersionMap[name] === 'undefined') {
884
641
  initialVersionMap[name] = state.version
885
642
  }
886
643
  } else {
@@ -1005,9 +762,7 @@ const makeChatsSocket = config => {
1005
762
  * type = "avatar" for the avatar variant (no query attr, confirmed from interop logs)
1006
763
  */
1007
764
  const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
1008
- // Confirmed from live capture: query="url" is always present, for both
1009
- // "preview" and "image" — not image-only as previously assumed.
1010
- const picAttrs = { type, query: 'url' }
765
+ const picAttrs = type === 'image' ? { type, query: 'url' } : { type }
1011
766
  const baseContent = [{ tag: 'picture', attrs: picAttrs }]
1012
767
  // WA Web only includes tctoken for user JIDs (not groups/newsletters)
1013
768
  // and never for own profile pic (Chat model for self has no tcToken).
@@ -1124,136 +879,25 @@ const makeChatsSocket = config => {
1124
879
  })
1125
880
  }
1126
881
  }
1127
- /**
1128
- * Store privacy tokens received from a usync response per-contact.
1129
- * Called externally when a USyncQuery result includes privacy_token data.
1130
- *
1131
- * @param {Array<{jid: string, privacyToken: Buffer, privacyModeTs: number|string}>} entries
1132
- */
1133
- const storePrivacyTokens = async entries => {
1134
- if (!entries?.length) return
1135
- const write = {}
1136
- for (const { jid, privacyToken, privacyModeTs } of entries) {
1137
- if (!jid || !privacyToken?.length) continue
1138
- write[jid] = {
1139
- token: Buffer.isBuffer(privacyToken) ? privacyToken : Buffer.from(privacyToken),
1140
- ts: String(privacyModeTs || '')
1141
- }
1142
- }
1143
- if (Object.keys(write).length) {
1144
- await authState.keys.set({ 'privacy-token': write })
1145
- }
1146
- }
1147
-
1148
- /**
1149
- * Wrapper around sock.executeUSyncQuery that automatically persists any per-contact
1150
- * privacy tokens (privacy_mode_ts + privacy_token) present in the usync result list,
1151
- * and emits contacts.update for any entries flagged isBlockedByContact.
1152
- *
1153
- * @param {import('../WAUSync/USyncQuery').USyncQuery} usyncQuery
1154
- */
1155
- const executeUSyncQuery = async usyncQuery => {
1156
- const result = await sock.executeUSyncQuery(usyncQuery)
1157
- if (!result) return result
1158
-
1159
- // B. Auto-persist privacy tokens found in any usync response.
1160
- const privacyEntries = []
1161
- const blockedContacts = []
1162
- for (const entry of [...(result.list || []), ...(result.sideList || [])]) {
1163
- if (entry.privacy?.token) {
1164
- privacyEntries.push({
1165
- jid: entry.id,
1166
- privacyToken: entry.privacy.token,
1167
- privacyModeTs: entry.privacy.modeTs
1168
- })
1169
- }
1170
- // G. Emit contacts.update for contacts that blocked us.
1171
- if (entry.isBlockedByContact) {
1172
- blockedContacts.push({ id: entry.id, isBlockedByContact: true })
1173
- }
1174
- }
1175
- if (privacyEntries.length) {
1176
- storePrivacyTokens(privacyEntries).catch(err => logger.warn({ err }, 'failed to store privacy tokens from usync'))
1177
- }
1178
- if (blockedContacts.length) {
1179
- ev.emit('contacts.update', blockedContacts)
1180
- }
1181
- return result
1182
- }
1183
-
1184
882
  /**
1185
883
  * @param toJid the jid to subscribe to
1186
- * @param options.presenceType optional presenceType attribute stored for the contact
1187
- * @param options.presenceName optional presenceName attribute for the subscribe stanza
1188
- * @param options.groupJid optional group JID — when set adds context="group" attribute
884
+ * @param tcToken token for subscription, use if present
1189
885
  */
1190
- const presenceSubscribe = async (toJid, { presenceType, presenceName, groupJid } = {}) => {
886
+ const presenceSubscribe = async toJid => {
1191
887
  // Only include tctoken for user JIDs — groups/newsletters don't use tctokens
1192
888
  const normalizedToJid = (0, WABinary_1.jidNormalizedUser)(toJid)
1193
889
  const isUserJid = (0, WABinary_1.isPnUser)(normalizedToJid) || (0, WABinary_1.isLidUser)(normalizedToJid)
1194
-
1195
- // Build presence attributes
1196
- const presenceAttrs = {
1197
- to: toJid,
1198
- id: generateMessageTag(),
1199
- type: 'subscribe'
1200
- }
1201
-
1202
- // Accumulate child nodes for the presence stanza
1203
- const presenceContent = []
1204
-
1205
- // A. TC Token as attribute: fetch stored token and attach as base64 tc_token attr
1206
- if (isUserJid) {
1207
- try {
1208
- const storageJid = await (0, tc_token_utils_1.resolveTcTokenJid)(normalizedToJid, getLIDForPN)
1209
- const tcTokenData = await authState.keys.get('tctoken', [storageJid])
1210
- const entry = tcTokenData?.[storageJid]
1211
- const tcTokenBuffer = entry?.token
1212
- if (tcTokenBuffer?.length && !(0, tc_token_utils_1.isTcTokenExpired)(entry?.timestamp)) {
1213
- presenceAttrs.tc_token = tcTokenBuffer.toString('base64')
1214
- }
1215
- } catch (e) {
1216
- // best-effort — proceed without tc_token if lookup fails
1217
- }
1218
-
1219
- // B. Privacy Token — include per-contact privacy token when available.
1220
- // This token is issued by the contact's device via usync and gates presence
1221
- // visibility when their online privacy is set to "contacts" or "contact_blacklist".
1222
- try {
1223
- const privTokenData = await authState.keys.get('privacy-token', [normalizedToJid])
1224
- const privEntry = privTokenData?.[normalizedToJid]
1225
- if (privEntry?.token?.length) {
1226
- const privTokenAttrs = {}
1227
- if (privEntry.ts) privTokenAttrs.t = privEntry.ts
1228
- presenceContent.push({
1229
- tag: 'privacy_token',
1230
- attrs: privTokenAttrs,
1231
- content: privEntry.token
1232
- })
1233
- }
1234
- } catch (e) {
1235
- // best-effort — proceed without privacy_token if lookup fails
1236
- }
1237
- }
1238
-
1239
- // C. presenceType and presenceName attributes
1240
- if (presenceType) {
1241
- presenceAttrs.presenceType = presenceType
1242
- }
1243
- if (presenceName) {
1244
- presenceAttrs.presenceName = presenceName
1245
- }
1246
-
1247
- // D. Group presence context
1248
- if (groupJid) {
1249
- presenceAttrs.context = 'group'
1250
- presenceAttrs.group_jid = (0, WABinary_1.jidNormalizedUser)(groupJid)
1251
- }
1252
-
890
+ const tcTokenContent = isUserJid
891
+ ? await (0, tc_token_utils_1.buildTcTokenFromJid)({ authState, jid: normalizedToJid, getLIDForPN })
892
+ : undefined
1253
893
  return sendNode({
1254
894
  tag: 'presence',
1255
- attrs: presenceAttrs,
1256
- content: presenceContent.length ? presenceContent : undefined
895
+ attrs: {
896
+ to: toJid,
897
+ id: generateMessageTag(),
898
+ type: 'subscribe'
899
+ },
900
+ content: tcTokenContent
1257
901
  })
1258
902
  }
1259
903
  const handlePresenceUpdate = ({ tag, attrs, content }) => {
@@ -1561,36 +1205,6 @@ const makeChatsSocket = config => {
1561
1205
  const updatePrivateProcessingSetting = enabled => {
1562
1206
  return chatModify({ privateProcessingSetting: enabled ? 'enabled' : 'disabled' }, '')
1563
1207
  }
1564
- /**
1565
- * Update Meta AI features control
1566
- * @param status 'enabled' | 'enabled_has_learning' | 'disabled'
1567
- * @param replyMode 'muted' | 'ai_agent' | 'suggestions'
1568
- */
1569
- const updateAIFeatures = (status = 'enabled', replyMode = 'suggestions') => {
1570
- const statusEnum =
1571
- require('../../WAProto/index.js').proto.SyncActionValue.MaibaAIFeaturesControlAction.MaibaAIFeatureStatus
1572
- const replyModeEnum =
1573
- require('../../WAProto/index.js').proto.SyncActionValue.MaibaAIFeaturesControlAction.MaibaAIReplyMode
1574
- const statusMap = {
1575
- enabled: statusEnum.ENABLED,
1576
- enabled_has_learning: statusEnum.ENABLED_HAS_LEARNING,
1577
- disabled: statusEnum.DISABLED
1578
- }
1579
- const replyModeMap = {
1580
- muted: replyModeEnum.MUTED,
1581
- ai_agent: replyModeEnum.AI_AGENT,
1582
- suggestions: replyModeEnum.SUGGESTIONS
1583
- }
1584
- return chatModify(
1585
- {
1586
- maibaAIFeatures: {
1587
- status: statusMap[status] ?? statusEnum.ENABLED,
1588
- replyMode: replyModeMap[replyMode] ?? replyModeEnum.SUGGESTIONS
1589
- }
1590
- },
1591
- ''
1592
- )
1593
- }
1594
1208
  /**
1595
1209
  * Update bio/about privacy (who can see your About text)
1596
1210
  * @param value 'all' | 'contacts' | 'contact_blacklist' | 'none'
@@ -1679,30 +1293,6 @@ const makeChatsSocket = config => {
1679
1293
  const silenceChat = (jid, silent = true, until = null) => {
1680
1294
  return chatModify({ silenceChat: { silent, until } }, jid)
1681
1295
  }
1682
- /**
1683
- * Clear all messages in a chat
1684
- */
1685
- const clearChat = (jid, lastMessages) => {
1686
- return chatModify({ clear: true, lastMessages }, jid)
1687
- }
1688
- /**
1689
- * Pin or unpin a chat to the top
1690
- */
1691
- const pinChat = (jid, pinned = true) => {
1692
- return chatModify({ pin: { pinned } }, jid)
1693
- }
1694
- /**
1695
- * Star or unstar messages
1696
- */
1697
- const starMessages = (jid, messageIds, starred = true) => {
1698
- return chatModify({ star: { messages: messageIds, star: starred } }, jid)
1699
- }
1700
- /**
1701
- * Set up a quick reply
1702
- */
1703
- const setQuickReply = (text, shortcut) => {
1704
- return chatModify({ quickReply: { text, shortcut } }, '')
1705
- }
1706
1296
  /**
1707
1297
  * Fetch bot profiles for a list of JIDs using USyncBotProfileProtocol
1708
1298
  */
@@ -1713,7 +1303,7 @@ const makeChatsSocket = config => {
1713
1303
  for (const jid of jids) {
1714
1304
  q.withUser(new USyncUser().withId(jid))
1715
1305
  }
1716
- const result = await executeUSyncQuery(q)
1306
+ const result = await sock.executeUSyncQuery(q)
1717
1307
  return result?.list || []
1718
1308
  }
1719
1309
  /**
@@ -1771,18 +1361,12 @@ const makeChatsSocket = config => {
1771
1361
  /**
1772
1362
  * Fetch AB-test (abt) props from server.
1773
1363
  * Mirrors ABPropsProtocolHelper — protocol 1 or 2, optional hash/refresh_id/group.
1774
- * Confirmed from live capture: group-scoped queries send only { group }, never combined
1775
- * with protocol/hash/refresh_id; the account-level default protocol is "1", not "2".
1776
1364
  */
1777
- const fetchABProps = async (protocol = '1', hash = '', refreshId = null, group = null) => {
1778
- const propAttrs =
1779
- group != null
1780
- ? { group: String(group) }
1781
- : {
1782
- protocol,
1783
- ...(hash ? { hash } : {}),
1784
- ...(refreshId != null ? { refresh_id: String(refreshId) } : {})
1785
- }
1365
+ const fetchABProps = async (protocol = '2', hash = '', refreshId = null, group = null) => {
1366
+ const propAttrs = { protocol }
1367
+ if (hash) propAttrs.hash = hash
1368
+ if (refreshId != null) propAttrs.refresh_id = String(refreshId)
1369
+ if (group != null) propAttrs.group = String(group)
1786
1370
  const result = await query({
1787
1371
  tag: 'iq',
1788
1372
  attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'abt', type: 'get' },
@@ -1795,19 +1379,16 @@ const makeChatsSocket = config => {
1795
1379
  /**
1796
1380
  * Remove a companion (linked) device from the account.
1797
1381
  * Mirrors CompanionDeviceRemovalJob — xmlns="md", child tag "remove-companion-device".
1798
- * jid: full device JID of the companion to remove (e.g. "1234567890:5@s.whatsapp.net").
1799
- * reason: "user_initiated" | "unknown_companion" | any WA-defined reason string.
1800
- * Confirmed from live capture — the previous { platform, id: keyIndex } attrs never
1801
- * identified a device at all; the server needs the companion's full jid.
1382
+ * reason: "user_initiated" | "server_initiated" | any WA-defined reason string.
1802
1383
  */
1803
- const removeCompanionDevice = async (jid, reason = 'user_initiated') => {
1384
+ const removeCompanionDevice = async (keyIndex, reason = 'user_initiated') => {
1804
1385
  await query({
1805
1386
  tag: 'iq',
1806
1387
  attrs: { to: WABinary_1.S_WHATSAPP_NET, xmlns: 'md', type: 'set' },
1807
1388
  content: [
1808
1389
  {
1809
1390
  tag: 'remove-companion-device',
1810
- attrs: { jid, reason }
1391
+ attrs: { platform: 'true', reason, id: String(keyIndex) }
1811
1392
  }
1812
1393
  ]
1813
1394
  })
@@ -1831,51 +1412,6 @@ const makeChatsSocket = config => {
1831
1412
  ]
1832
1413
  })
1833
1414
  }
1834
- /**
1835
- * Build and push the companion's signed key-index-list on (re)connect.
1836
- *
1837
- * The official client sends this within ~0.8s of every session start (xmlns="md").
1838
- * A companion that never re-asserts its ADV key index is treated as unverified and
1839
- * gets <conflict type="device_removed"/>.
1840
- *
1841
- * Payload construction mirrors CompanionDeviceAdvUtil.A02 (C19350ud) from the APK:
1842
- * details = proto ADVKeyIndexList { rawId, timestamp, currentIndex, validIndexes, accountType }
1843
- * accountSignature = Curve.sign(signedIdentityKey.private, [6,2] || details)
1844
- * accountSignatureKey = only attached for HOSTED accounts (skipped for consumer E2EE)
1845
- * The signature uses our own device identity key (the same key that produces the ADV
1846
- * deviceSignature during pairing) — a companion cannot re-sign with the account key.
1847
- */
1848
- const sendKeyIndexList = async () => {
1849
- const account = authState.creds.account
1850
- const signedIdentityKey = authState.creds.signedIdentityKey
1851
- if (!account?.details || !signedIdentityKey?.private) {
1852
- logger.debug('no ADV account / signed identity key, skipping key-index-list')
1853
- return
1854
- }
1855
- const deviceIdentity = index_js_1.proto.ADVDeviceIdentity.decode(account.details)
1856
- const keyIndex = deviceIdentity.keyIndex || 0
1857
- const accountType = deviceIdentity.accountType ?? index_js_1.proto.ADVEncryptionType.E2EE
1858
- const isHosted = accountType === index_js_1.proto.ADVEncryptionType.HOSTED
1859
- const ts = Math.floor(Date.now() / 1000)
1860
- const details = index_js_1.proto.ADVKeyIndexList.encode({
1861
- rawId: deviceIdentity.rawId || 0,
1862
- timestamp: ts,
1863
- currentIndex: keyIndex,
1864
- validIndexes: [keyIndex],
1865
- accountType
1866
- }).finish()
1867
- const sigPrefix = isHosted
1868
- ? Defaults_1.WA_ADV_HOSTED_KEY_INDEX_LIST_SIG_PREFIX
1869
- : Defaults_1.WA_ADV_KEY_INDEX_LIST_SIG_PREFIX
1870
- const accountSignature = Utils_1.Curve.sign(signedIdentityKey.private, Buffer.concat([sigPrefix, details]))
1871
- const signedKeyIndexList = { details, accountSignature }
1872
- if (isHosted && account.accountSignatureKey?.length) {
1873
- signedKeyIndexList.accountSignatureKey = account.accountSignatureKey
1874
- }
1875
- const content = index_js_1.proto.ADVSignedKeyIndexList.encode(signedKeyIndexList).finish()
1876
- await updateKeyIndexList(ts, Buffer.from(content))
1877
- logger.info({ ts, keyIndex }, 'sent key-index-list')
1878
- }
1879
1415
  /**
1880
1416
  * Request a media upload connection token from the server.
1881
1417
  * Mirrors MediaConnFetcher — xmlns="w:m", type="set", optional last_id.
@@ -1932,8 +1468,6 @@ const makeChatsSocket = config => {
1932
1468
  await query({
1933
1469
  tag: 'iq',
1934
1470
  attrs: {
1935
- // reuse the challenge id at the iq level too, matching AccountDefenceDeviceLogoutJob
1936
- id: String(id),
1937
1471
  to: WABinary_1.S_WHATSAPP_NET,
1938
1472
  xmlns: 'w:account_defence',
1939
1473
  type: 'set',
@@ -1952,71 +1486,9 @@ const makeChatsSocket = config => {
1952
1486
  * help ensure parity with WA Web
1953
1487
  * */
1954
1488
  const executeInitQueries = async () => {
1955
- // Gate initInterop on previously-persisted flag: undefined = first connection (always run);
1956
- // false = account has interop disabled, skip the TOS/opt-in flow on reconnect.
1957
- const shouldInitInterop = authState.creds.interopEnabled !== false
1958
- const [, , , abProps] = await Promise.all([
1959
- fetchProps(),
1960
- fetchBlocklist(),
1961
- fetchPrivacySettings(),
1962
- fetchABProps(),
1963
- ...(shouldInitInterop ? [initInterop()] : [])
1964
- ])
1965
- const INTEROP_FLAGS = ['stella_interop_enabled', 'stella_ios_enabled']
1966
- for (const flag of INTEROP_FLAGS) {
1967
- if (flag in abProps) {
1968
- const enabled = abProps[flag] === 'true' || abProps[flag] === '1'
1969
- ev.emit('interop.feature-update', { feature: flag, enabled })
1970
- }
1971
- }
1972
- // Persist AB prop feature flags into connection credentials so they survive reconnect.
1973
- const abCredsUpdate = {}
1974
- if ('stella_interop_enabled' in abProps) {
1975
- abCredsUpdate.interopEnabled =
1976
- abProps['stella_interop_enabled'] === 'true' || abProps['stella_interop_enabled'] === '1'
1977
- }
1978
- if ('stella_ios_enabled' in abProps) {
1979
- abCredsUpdate.interopIosEnabled =
1980
- abProps['stella_ios_enabled'] === 'true' || abProps['stella_ios_enabled'] === '1'
1981
- }
1982
- if ('md_privacy_v2' in abProps) {
1983
- abCredsUpdate.mdPrivacyV2 = abProps['md_privacy_v2'] === 'true' || abProps['md_privacy_v2'] === '1'
1984
- }
1985
- // Persist media feature-flag AB props so upload/download paths can gate on them.
1986
- const MEDIA_AB_PROPS = [
1987
- 'hd_image_dual_upload',
1988
- 'hd_video_dual_upload',
1989
- 'hevc_video_dual_upload',
1990
- 'partial_pjpeg_enabled',
1991
- 'multi_scan_pjpeg',
1992
- 'media_poll'
1993
- ]
1994
- const mediaAbProps = {}
1995
- for (const prop of MEDIA_AB_PROPS) {
1996
- if (prop in abProps) {
1997
- mediaAbProps[prop] = abProps[prop] === 'true' || abProps[prop] === '1'
1998
- }
1999
- }
2000
- if (Object.keys(mediaAbProps).length) {
2001
- abCredsUpdate.mediaAbProps = { ...(authState.creds.mediaAbProps || {}), ...mediaAbProps }
2002
- }
2003
- if (Object.keys(abCredsUpdate).length) {
2004
- ev.emit('creds.update', abCredsUpdate)
2005
- }
1489
+ await Promise.all([fetchProps(), fetchBlocklist(), fetchPrivacySettings(), initInterop()])
2006
1490
  }
2007
1491
  const upsertMessage = ev.createBufferedFunction(async (msg, type) => {
2008
- // Cache newsletter server_id for use when this message is later quoted
2009
- if (msg.newsletter && msg.newsletter_server_id && msg.key?.id) {
2010
- _nlServerIdCache.set(msg.key.id, msg.newsletter_server_id)
2011
- }
2012
- // If this message quotes a newsletter post, enrich it with the cached server_id
2013
- if (msg.message) {
2014
- const ci = Object.values(msg.message).find(v => v?.contextInfo)?.contextInfo
2015
- if (ci?.stanzaId && (0, WABinary_1.isJidNewsletter)(ci.remoteJid)) {
2016
- const sid = _nlServerIdCache.get(ci.stanzaId)
2017
- if (sid != null) msg.quotedNewsletterServerId = sid
2018
- }
2019
- }
2020
1492
  ev.emit('messages.upsert', { messages: [msg], type })
2021
1493
  if (!!msg.pushName) {
2022
1494
  let jid = msg.key.fromMe ? authState.creds.me.id : msg.key.participant || msg.key.remoteJid
@@ -2134,20 +1606,6 @@ const makeChatsSocket = config => {
2134
1606
  })
2135
1607
  ws.on('CB:presence', handlePresenceUpdate)
2136
1608
  ws.on('CB:chatstate', handlePresenceUpdate)
2137
- // Server device-legitimacy challenge (w:account_defence). Fires a few seconds after the
2138
- // companion sends its first message; if we don't approve within ~20s the device is removed
2139
- // with <conflict type="device_removed"/>. Wire the inbound challenge to confirmDeviceLogout.
2140
- ws.on('CB:iq,xmlns:w:account_defence', async node => {
2141
- const deviceLogout = (0, WABinary_1.getBinaryNodeChild)(node, 'device_logout')
2142
- if (!deviceLogout) return
2143
- const id = node.attrs.id
2144
- logger.info({ id }, 'received account_defence device_logout challenge, approving')
2145
- try {
2146
- await confirmDeviceLogout(id, true)
2147
- } catch (error) {
2148
- onUnexpectedError(error, 'account_defence device_logout approve')
2149
- }
2150
- })
2151
1609
  ws.on('CB:ib,,dirty', async node => {
2152
1610
  const { attrs } = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty')
2153
1611
  const type = attrs.type
@@ -2183,9 +1641,6 @@ const makeChatsSocket = config => {
2183
1641
  sendPresenceUpdate(markOnlineOnConnect ? 'available' : 'unavailable').catch(error =>
2184
1642
  onUnexpectedError(error, 'presence update requests')
2185
1643
  )
2186
- // Re-assert our ADV key index to the server on every (re)connect, or the companion
2187
- // is treated as unverified and removed. Best-effort: never block the open flow.
2188
- sendKeyIndexList().catch(error => onUnexpectedError(error, 'send key-index-list'))
2189
1644
  }
2190
1645
  if (!receivedPendingNotifications || syncState !== State_1.SyncState.Connecting) {
2191
1646
  return
@@ -2263,12 +1718,9 @@ const makeChatsSocket = config => {
2263
1718
  getChatBlockingStatus,
2264
1719
  updateChatBlockingStatus,
2265
1720
  getUserDisclosures,
2266
- acceptTosNotice,
2267
- reportSpam,
2268
1721
  getOptOutList,
2269
1722
  getPushConfig,
2270
1723
  setPushConfig,
2271
- signPrivateCredential,
2272
1724
  messageMutex,
2273
1725
  receiptMutex,
2274
1726
  appStatePatchMutex,
@@ -2317,7 +1769,6 @@ const makeChatsSocket = config => {
2317
1769
  renameAIThread,
2318
1770
  pinThreadMessage,
2319
1771
  updatePrivateProcessingSetting,
2320
- updateAIFeatures,
2321
1772
  updateBioPrivacy,
2322
1773
  blockBot,
2323
1774
  unblockBot,
@@ -2331,10 +1782,6 @@ const makeChatsSocket = config => {
2331
1782
  markChatAsUnread,
2332
1783
  setChatEphemeral,
2333
1784
  silenceChat,
2334
- clearChat,
2335
- pinChat,
2336
- starMessages,
2337
- setQuickReply,
2338
1785
  fetchBotProfiles,
2339
1786
  updateChatLock,
2340
1787
  updateChatWallpaper,
@@ -2363,12 +1810,7 @@ const makeChatsSocket = config => {
2363
1810
  fetchMediaConn,
2364
1811
  deleteBroadcastList,
2365
1812
  fetchQRCode,
2366
- confirmDeviceLogout,
2367
- updateKeyIndexList,
2368
- sendKeyIndexList,
2369
- storePrivacyTokens,
2370
- executeUSyncQuery,
2371
- newsletterServerIdCache: _nlServerIdCache
1813
+ confirmDeviceLogout
2372
1814
  }
2373
1815
  }
2374
1816
  exports.makeChatsSocket = makeChatsSocket