@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
@@ -12,7 +12,6 @@ const Utils_1 = require('../Utils')
12
12
  const browser_utils_1 = require('../Utils/browser-utils')
13
13
  const WABinary_1 = require('../WABinary')
14
14
  const BinaryInfo_js_1 = require('../WAM/BinaryInfo.js')
15
- const WAM_1 = require('../WAM')
16
15
  const WAUSync_1 = require('../WAUSync/')
17
16
  const Client_1 = require('./Client')
18
17
  const mex_1 = require('./mex')
@@ -72,7 +71,6 @@ const makeSocket = config => {
72
71
  routingInfo: authState?.creds?.routingInfo
73
72
  })
74
73
  const ws = new Client_1.WebSocketClient(url, config)
75
- const connectionStartTs = Date.now()
76
74
  ws.connect()
77
75
  const sendPromise = (0, util_1.promisify)(ws.send)
78
76
  /** send a raw buffer */
@@ -192,16 +190,14 @@ const makeSocket = config => {
192
190
  if (usyncQuery.protocols.length === 0) {
193
191
  throw new boom_1.Boom('USyncQuery must have at least one protocol')
194
192
  }
195
- const validUsers = usyncQuery.users.filter(user => (typeof user.validate === 'function' ? user.validate() : true))
196
- if (!validUsers.length) {
197
- logger.warn('a usync query must have at least one valid user')
198
- }
193
+ // todo: validate users, throw WARNING on no valid users
194
+ // variable below has only validated users
195
+ const validUsers = usyncQuery.users
199
196
  const userNodes = validUsers.map(user => {
200
197
  return {
201
198
  tag: 'user',
202
199
  attrs: {
203
- jid: !user.phone ? user.id : undefined,
204
- pn_jid: user.pnJid
200
+ jid: !user.phone ? user.id : undefined
205
201
  },
206
202
  content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
207
203
  }
@@ -237,17 +233,8 @@ const makeSocket = config => {
237
233
  }
238
234
  ]
239
235
  }
240
- await (0, WAUSync_1.waitForBackoff)(usyncQuery)
241
236
  const result = await query(iq)
242
- const parsed = usyncQuery.parseUSyncQueryResult(result)
243
- if (parsed?.errors) {
244
- for (const [protocolName, err] of Object.entries(parsed.errors)) {
245
- if (err?.errorBackoff) {
246
- ;(0, WAUSync_1.setProtocolBackoffMs)(protocolName, err.errorBackoff * 1000)
247
- }
248
- }
249
- }
250
- return parsed
237
+ return usyncQuery.parseUSyncQueryResult(result)
251
238
  }
252
239
  const onWhatsApp = async (...phoneNumber) => {
253
240
  let usyncQuery = new WAUSync_1.USyncQuery()
@@ -292,6 +279,80 @@ const makeSocket = config => {
292
279
  }
293
280
  return []
294
281
  }
282
+ /**
283
+ * @param {string | string[]} lid - @lid JID or array of @lid JID
284
+ * @returns {Promise<{ lid: string, pn: string | null }[]>}
285
+ */
286
+ const toPn = async lid => {
287
+ const lids = Array.isArray(lid) ? lid : [lid]
288
+ const usyncQuery = new WAUSync_1.USyncQuery().withContactProtocol().withLIDProtocol()
289
+ for (const l of lids) {
290
+ const decoded = (0, WABinary_1.jidDecode)(l)
291
+ if (!decoded) continue
292
+ usyncQuery.withUser(
293
+ new WAUSync_1.USyncUser().withId((0, WABinary_1.jidEncode)(decoded.user, 's.whatsapp.net')).withLid(l)
294
+ )
295
+ }
296
+ const result = await executeUSyncQuery(usyncQuery)
297
+ return lids.map(l => {
298
+ const decoded = (0, WABinary_1.jidDecode)(l)
299
+ if (!decoded) return { lid: l, pn: null }
300
+ const match = result?.list?.find(entry => {
301
+ const entryDecoded = (0, WABinary_1.jidDecode)(entry.id)
302
+ return entryDecoded?.user === decoded.user
303
+ })
304
+ if (!match?.contact) return { lid: l, pn: null }
305
+ const pn = (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(match.id)?.user, 's.whatsapp.net')
306
+ return { lid: l, pn: pn ?? null }
307
+ })
308
+ }
309
+ /**
310
+ * @param {string | string[]} pn - @s.whatsapp.net JID or array of @s.whatsapp.net JID
311
+ * @returns {Promise<{ pn: string, lid: string | null }[]>}
312
+ */
313
+ const toLID = async pn => {
314
+ const pns = Array.isArray(pn) ? pn : [pn]
315
+ const usyncQuery = new WAUSync_1.USyncQuery().withContactProtocol().withLIDProtocol()
316
+ for (const p of pns) {
317
+ const decoded = (0, WABinary_1.jidDecode)(p)
318
+ if (!decoded) continue
319
+ usyncQuery.withUser(new WAUSync_1.USyncUser().withId((0, WABinary_1.jidEncode)(decoded.user, 's.whatsapp.net')))
320
+ }
321
+ const result = await executeUSyncQuery(usyncQuery)
322
+ return pns.map(p => {
323
+ const decoded = (0, WABinary_1.jidDecode)(p)
324
+ if (!decoded) return { pn: p, lid: null }
325
+ const match = result?.list?.find(entry => {
326
+ return (0, WABinary_1.jidDecode)(entry.id)?.user === decoded.user
327
+ })
328
+ if (!match?.contact) return { pn: p, lid: null }
329
+ const lidVal = match?.lid ?? null
330
+ const lid = lidVal ? (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(lidVal)?.user ?? lidVal, 'lid') : null
331
+ return { pn: p, lid }
332
+ })
333
+ }
334
+ /**
335
+ * @param {string | string[]} username - username to fetch
336
+ * @param {string} [pin] - pin of username if the user has one set
337
+ * @returns {Promise<{ username: string, pn: string | null, lid: string | null }[]>}
338
+ */
339
+ const fetchUsername = async (username, pin) => {
340
+ const usernames = Array.isArray(username) ? username : [username]
341
+ const usyncQuery = new WAUSync_1.USyncQuery().withContactProtocol().withLIDProtocol()
342
+ for (const u of usernames) {
343
+ const user = new WAUSync_1.USyncUser().withUsername(u)
344
+ if (pin) user.withUsernameKey(pin)
345
+ usyncQuery.withUser(user)
346
+ }
347
+ const result = await executeUSyncQuery(usyncQuery)
348
+ return usernames.map(u => {
349
+ const match = result?.list?.find(entry => entry?.username === u)
350
+ if (!match?.contact) return { username: u, pn: null, lid: null }
351
+ const lid = match.lid
352
+ const pn = (0, WABinary_1.jidEncode)((0, WABinary_1.jidDecode)(match.id)?.user, 's.whatsapp.net')
353
+ return { username: u, pn: pn ?? null, lid: lid ?? null }
354
+ })
355
+ }
295
356
  const ev = (0, Utils_1.makeEventBuffer)(logger)
296
357
  const { creds } = authState
297
358
  // add transaction capability
@@ -302,6 +363,7 @@ const makeSocket = config => {
302
363
  let keepAliveReq
303
364
  let qrTimer
304
365
  let closed = false
366
+ const socketEndHandlers = []
305
367
  /** log & process any unexpected errors */
306
368
  const onUnexpectedError = (err, msg) => {
307
369
  logger.error({ err }, `unexpected error in '${msg}'`)
@@ -342,7 +404,7 @@ const makeSocket = config => {
342
404
  const result = await awaitNextMessage(init)
343
405
  const handshake = index_js_1.proto.HandshakeMessage.decode(result)
344
406
  logger.trace({ handshake }, 'handshake recv from WA')
345
- const { keyEnc, serverStaticPub } = noise.processHandshake(handshake, creds.noiseKey)
407
+ const keyEnc = noise.processHandshake(handshake, creds.noiseKey)
346
408
  let node
347
409
  if (!creds.me) {
348
410
  node = (0, Utils_1.generateRegistrationNode)(creds, config)
@@ -361,9 +423,6 @@ const makeSocket = config => {
361
423
  }).finish()
362
424
  )
363
425
  await noise.finishInit()
364
- if (serverStaticPub && !creds.serverStaticPub) {
365
- ev.emit('creds.update', { serverStaticPub })
366
- }
367
426
  startKeepAliveRequest()
368
427
  }
369
428
  const getAvailablePreKeysOnServer = async () => {
@@ -516,7 +575,6 @@ const makeSocket = config => {
516
575
  closed = true
517
576
  logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed')
518
577
  clearInterval(keepAliveReq)
519
- clearInterval(wamFlushInterval)
520
578
  clearTimeout(qrTimer)
521
579
  ws.removeAllListeners('close')
522
580
  ws.removeAllListeners('open')
@@ -526,6 +584,13 @@ const makeSocket = config => {
526
584
  await ws.close()
527
585
  } catch {}
528
586
  }
587
+ for (const handler of socketEndHandlers) {
588
+ try {
589
+ await handler(error)
590
+ } catch (err) {
591
+ logger.error({ err }, 'error in socket end handler')
592
+ }
593
+ }
529
594
  ev.emit('connection.update', {
530
595
  connection: 'close',
531
596
  lastDisconnect: {
@@ -570,8 +635,6 @@ const makeSocket = config => {
570
635
  void end(new boom_1.Boom('Connection was lost', { statusCode: Types_1.DisconnectReason.connectionLost }))
571
636
  } else if (ws.isOpen) {
572
637
  // if its all good, send a keep alive request
573
- // Confirmed from live capture: the w:p keepalive iq has no child element
574
- // at all — a bare <ping/> child doesn't appear in real traffic.
575
638
  query({
576
639
  tag: 'iq',
577
640
  attrs: {
@@ -579,7 +642,8 @@ const makeSocket = config => {
579
642
  to: WABinary_1.S_WHATSAPP_NET,
580
643
  type: 'get',
581
644
  xmlns: 'w:p'
582
- }
645
+ },
646
+ content: [{ tag: 'ping', attrs: {} }]
583
647
  }).catch(err => {
584
648
  logger.error({ trace: err.stack }, 'error in sending keep alive')
585
649
  })
@@ -625,7 +689,7 @@ const makeSocket = config => {
625
689
  }
626
690
  const requestPairingCode = async (phoneNumber, customPairingCode) => {
627
691
  const pairingCode = customPairingCode ?? (0, Utils_1.bytesToCrockford)((0, crypto_1.randomBytes)(5))
628
- if (customPairingCode && customPairingCode.length !== 8) {
692
+ if (customPairingCode && customPairingCode?.length !== 8) {
629
693
  throw new Error('Custom pairing code must be exactly 8 chars')
630
694
  }
631
695
  authState.creds.pairingCode = pairingCode
@@ -695,8 +759,7 @@ const makeSocket = config => {
695
759
  attrs: {
696
760
  to: WABinary_1.S_WHATSAPP_NET,
697
761
  id: generateMessageTag(),
698
- xmlns: 'w:stats',
699
- type: 'set'
762
+ xmlns: 'w:stats'
700
763
  },
701
764
  content: [
702
765
  {
@@ -707,30 +770,8 @@ const makeSocket = config => {
707
770
  ]
708
771
  })
709
772
  }
710
- const flushWAMEvents = async () => {
711
- if (!publicWAMBuffer.events.length) return
712
- try {
713
- const encoded = (0, WAM_1.encodeWAM)(publicWAMBuffer)
714
- publicWAMBuffer.events = []
715
- publicWAMBuffer.sequence = (publicWAMBuffer.sequence + 1) & 0xffff
716
- await sendWAMBuffer(encoded)
717
- } catch (e) {
718
- logger.debug({ err: e }, 'WAM flush failed')
719
- }
720
- }
721
- const wsOpenTs = { value: connectionStartTs }
722
773
  ws.on('message', onMessageReceived)
723
774
  ws.on('open', async () => {
724
- wsOpenTs.value = Date.now()
725
- publicWAMBuffer.events.push({
726
- WebcSocketConnect: {
727
- props: {
728
- webcSocketConnectDuration: wsOpenTs.value - connectionStartTs,
729
- webcSocketConnectReason: 0
730
- },
731
- globals: {}
732
- }
733
- })
734
775
  try {
735
776
  await validateConnection()
736
777
  } catch (err) {
@@ -738,9 +779,6 @@ const makeSocket = config => {
738
779
  void end(err)
739
780
  }
740
781
  })
741
- const wamFlushInterval = setInterval(() => {
742
- void flushWAMEvents()
743
- }, 30_000)
744
782
  ws.on('error', mapWebSocketError(end))
745
783
  ws.on(
746
784
  'close',
@@ -780,7 +818,7 @@ const makeSocket = config => {
780
818
  void end(new boom_1.Boom('QR refs attempts ended', { statusCode: Types_1.DisconnectReason.timedOut }))
781
819
  return
782
820
  }
783
- const ref = refNode.content.toString('utf8')
821
+ const ref = refNode.content.toString('utf-8')
784
822
  const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',')
785
823
  ev.emit('connection.update', { qr })
786
824
  qrTimer = setTimeout(genPairQR, qrMs)
@@ -828,14 +866,6 @@ const makeSocket = config => {
828
866
  }
829
867
  logger.info('opened connection to WA')
830
868
  clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
831
- // Fire Login WAM event
832
- publicWAMBuffer.events.push({
833
- Login: {
834
- props: { loginResult: 1, loginT: Date.now() - connectionStartTs, connectionOrigin: 1, passive: false },
835
- globals: {}
836
- }
837
- })
838
- void flushWAMEvents()
839
869
  ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
840
870
  if (config.syncFullHistory && authState.creds.initialFullSyncDone !== true) {
841
871
  logger.debug('initial full history sync completed, persisting one-time flag')
@@ -882,15 +912,6 @@ const makeSocket = config => {
882
912
  new boom_1.Boom('Multi-device beta not joined', { statusCode: Types_1.DisconnectReason.multideviceMismatch })
883
913
  )
884
914
  })
885
- ws.on('CB:ib,,dirty', node => {
886
- const dirtyNode = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty')
887
- const dirtyType = dirtyNode?.attrs?.type
888
- const dirtyTs = dirtyNode?.attrs?.timestamp ? +dirtyNode.attrs.timestamp : undefined
889
- // dirty signals that server-side account data changed; a notification with the same
890
- // type will follow immediately — no response stanza required
891
- logger.debug({ dirtyType, dirtyTs }, 'received dirty flag')
892
- ev.emit('account.dirty', { type: dirtyType, timestamp: dirtyTs })
893
- })
894
915
  ws.on('CB:ib,,offline_preview', async node => {
895
916
  logger.info('offline preview received', JSON.stringify(node))
896
917
  await sendNode({
@@ -899,19 +920,6 @@ const makeSocket = config => {
899
920
  content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
900
921
  })
901
922
  })
902
- ws.on('CB:ib,,thread_metadata', node => {
903
- const tmNode = (0, WABinary_1.getBinaryNodeChild)(node, 'thread_metadata')
904
- const items = (0, WABinary_1.getBinaryNodeChildren)(tmNode, 'item')
905
- if (items.length) {
906
- ev.emit(
907
- 'thread-metadata.update',
908
- items.map(item => ({
909
- jid: item.attrs.from,
910
- t: item.attrs.t ? +item.attrs.t : undefined
911
- }))
912
- )
913
- }
914
- })
915
923
  ws.on('CB:ib,,edge_routing', node => {
916
924
  const edgeRoutingNode = (0, WABinary_1.getBinaryNodeChild)(node, 'edge_routing')
917
925
  const routingInfo = (0, WABinary_1.getBinaryNodeChild)(edgeRoutingNode, 'routing_info')
@@ -1025,6 +1033,9 @@ const makeSocket = config => {
1025
1033
  generateMessageTag
1026
1034
  )
1027
1035
  }
1036
+ const registerSocketEndHandler = handler => {
1037
+ socketEndHandlers.push(handler)
1038
+ }
1028
1039
  return {
1029
1040
  type: 'md',
1030
1041
  ws,
@@ -1042,6 +1053,10 @@ const makeSocket = config => {
1042
1053
  sendNode,
1043
1054
  logout,
1044
1055
  end,
1056
+ registerSocketEndHandler,
1057
+ toPn,
1058
+ toLID,
1059
+ fetchUsername,
1045
1060
  onUnexpectedError,
1046
1061
  uploadPreKeys,
1047
1062
  uploadPreKeysToServerIfRequired,
@@ -1057,9 +1072,7 @@ const makeSocket = config => {
1057
1072
  executeUSyncQuery,
1058
1073
  onWhatsApp,
1059
1074
  fetchAccountReachoutTimelock,
1060
- fetchNewChatMessageCap,
1061
- logger,
1062
- masqueradeAsPrimary: config.masqueradeAsPrimary
1075
+ fetchNewChatMessageCap
1063
1076
  }
1064
1077
  }
1065
1078
  exports.makeSocket = makeSocket
@@ -16,7 +16,23 @@ const extractText = message => {
16
16
 
17
17
  const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
18
18
 
19
-
19
+ /**
20
+ * Attaches Telegraf text routing to a socket:
21
+ * `sock.onText(pattern, handler)`, `sock.hears(pattern, handler)` (alias), and
22
+ * `sock.command(name, handler)`, instead of manually parsing every
23
+ * `messages.upsert` event by hand.
24
+ *
25
+ * - `pattern` as a RegExp: handler gets called as `handler(msg, match)` where
26
+ * `match` is the result of `pattern.exec(text)` (or null if only testing).
27
+ * - `pattern` as a string: matched literally (exact match) against the message
28
+ * text; `handler(msg, [text])` is called on match.
29
+ * - `sock.command('start', handler)`: matches `/start`, `/start@BotName`, and
30
+ * `/start extra args`, calling `handler(msg, match)` where `match[1]` is the
31
+ * text after the command (or undefined).
32
+ *
33
+ * All three return an unsubscribe function. Only one `messages.upsert`
34
+ * listener is registered regardless of how many routes are added.
35
+ */
20
36
  const attachTextRouter = sock => {
21
37
  const routes = []
22
38
 
@@ -0,0 +1,243 @@
1
+ 'use strict'
2
+ Object.defineProperty(exports, '__esModule', { value: true })
3
+ exports.makeUsernameSocket = void 0
4
+
5
+ const { executeWMexQuery } = require('./mex.js')
6
+ const { USyncQuery, USyncUser } = require('../WAUSync/index.js')
7
+ const { makeNewsletterSocket } = require('./newsletter.js')
8
+ const crypto = require('crypto')
9
+ /**
10
+ * w:mex query IDs for username operations.
11
+ *
12
+ * Source: whatsapp-android-mex_client_persist_ids.json (WA 2.26.26.4 APK assets)
13
+ * Cross-checked: live Frida capture on real device (wa-logger-2.26.26.4.js, 2026-06-30)
14
+ */
15
+ const USERNAME_QUERY_IDS = {
16
+ CHECK: '26124072630599518', // UsernameCheck
17
+ CHECK_MULTI: '27134626522840286', // UsernameCheckMulti
18
+ SET: '27108705368767936', // UsernameSet
19
+ GET: '32618050064506055', // UsernameGet
20
+ GET_RECOMMENDATIONS: '26077456248616957', // UsernameGetRecommendationsQuery
21
+ PIN_SET: '25529696019976770' // UsernamePinSet
22
+ }
23
+
24
+ /**
25
+ * Source enum values from EnumC141106Vn (UsernameCheck result)
26
+ * and EnumC141056Vi (rejection reasons) confirmed via C164057Wg.java
27
+ */
28
+ const USERNAME_CHECK_RESULT = {
29
+ SUCCESS: 'SUCCESS',
30
+ INVALID: 'INVALID'
31
+ }
32
+
33
+ const USERNAME_SOURCE = {
34
+ FB: 'FB',
35
+ IG: 'IG',
36
+ USER_INPUT: 'USER_INPUT',
37
+ SUGGESTION: 'SUGGESTION'
38
+ }
39
+
40
+ const makeUsernameSocket = config => {
41
+ const sock = makeNewsletterSocket(config)
42
+ const { query, generateMessageTag, executeUSyncQuery } = sock
43
+
44
+ const mexQuery = (variables, queryId, dataPath) =>
45
+ executeWMexQuery(variables, queryId, dataPath, query, generateMessageTag)
46
+
47
+ /**
48
+ * Check whether a username is available.
49
+ *
50
+ * @param {string} username - The @username to check (without @)
51
+ * @param {boolean} includeSuggestions - Request alternative suggestions when taken
52
+ * @returns {object}
53
+ * On success: { available: true, username }
54
+ * On taken: { available: false, username, suggestions: string[], rejectionReasons: string[], suggestionsEligible: boolean }
55
+ * On rate-limit: throws Boom with statusCode 429
56
+ *
57
+ * Confirmed fields from C164057Wg.java:
58
+ * data.xwa2_username_check.result → 'SUCCESS' | 'INVALID'
59
+ * data.xwa2_username_check.suggestions
60
+ * data.xwa2_username_check.rejection_reasons
61
+ * data.xwa2_username_check.suggestions_eligible
62
+ */
63
+ const checkUsername = async (username, includeSuggestions = true, sessionId) => {
64
+ // session_id ties this check to the subsequent set step — caller can pass one in
65
+ // so both use the same UUID (confirmed from live WA 2.26.26.4 capture).
66
+ const session_id = sessionId || crypto.randomUUID()
67
+ const data = await mexQuery(
68
+ {
69
+ username,
70
+ include_suggestions: includeSuggestions,
71
+ session_id,
72
+ source: USERNAME_SOURCE.USER_INPUT
73
+ },
74
+ USERNAME_QUERY_IDS.CHECK,
75
+ 'xwa2_username_check'
76
+ )
77
+ if (data?.includes(USERNAME_CHECK_RESULT.SUCCESS) || data?.result === USERNAME_CHECK_RESULT.SUCCESS) {
78
+ return { available: true, username, session_id }
79
+ }
80
+ return {
81
+ available: false,
82
+ username,
83
+ session_id,
84
+ suggestions: data?.suggestions?? [],
85
+ rejectionReasons: data?.rejection_reasons?? [],
86
+ suggestionsEligible: data?.suggestions_eligible?? true
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Set your own username.
92
+ *
93
+ * @param {string} username - The username to set (without @)
94
+ * @param {object} options
95
+ * @param {string} [options.source] - 'USER_INPUT' | 'FB' | 'IG' | 'SUGGESTION'
96
+ * @param {string} [options.sessionId] - Optional session tracking ID
97
+ * @param {string} [options.pin] - Optional PIN for protected usernames
98
+ *
99
+ * Confirmed variables from C1568872p.java A00():
100
+ * username, reserved (bool), session_id, source, pin
101
+ */
102
+ const setUsername = async (username, options = {}) => {
103
+ const { source = USERNAME_SOURCE.USER_INPUT, sessionId, pin } = options
104
+ // session_id links the check and set steps — generate one if caller doesn't supply it.
105
+ // reserved:true confirmed from live WA 2.26.26.4 capture (was wrongly false before).
106
+ const session_id = sessionId || crypto.randomUUID()
107
+ const variables = {
108
+ username,
109
+ reserved: true,
110
+ source,
111
+ session_id,
112
+ ...(pin? { pin } : {})
113
+ }
114
+ return mexQuery(variables, USERNAME_QUERY_IDS.SET, 'xwa2_username_set')
115
+ }
116
+
117
+ const checkAndSetUsername = async (username) => {
118
+ const r = await checkUsername(username)
119
+ if (r.available) {
120
+ return await setUsername(username)
121
+ }
122
+ return r
123
+ }
124
+
125
+ /**
126
+ * Delete (unset) your own username.
127
+ *
128
+ * Confirmed from C1568872p.java:
129
+ * str4 = str == null? "delete" : "set"
130
+ * → sending username=null triggers the delete path on the server.
131
+ */
132
+ const deleteUsername = async () => {
133
+ return mexQuery({ username: null }, USERNAME_QUERY_IDS.SET, 'xwa2_username_delete')
134
+ }
135
+
136
+ /**
137
+ * Get your own current username.
138
+ *
139
+ * Confirmed from C1568872p.java A02():
140
+ * AbstractC41851rT.A0L(AbstractC130045pa.A0T(), C1363664w.class, "UsernameGet", false)
141
+ */
142
+ const getMyUsername = async () => {
143
+ const data = await mexQuery({}, USERNAME_QUERY_IDS.GET, 'xwa2_username_get')
144
+ return data?.username?? null
145
+ }
146
+
147
+ /**
148
+ * Set or delete the PIN that protects your username.
149
+ *
150
+ * @param {string|null} pin - New PIN, or null to delete the PIN
151
+ *
152
+ * Confirmed from MexUsernamePinProtocolApi.java:
153
+ * operation "UsernamePinSet", variable "pin"
154
+ * pin=null triggers the "delete" path on the server.
155
+ */
156
+ const setUsernamePin = async pin => {
157
+ // pin=null → delete PIN (server interprets empty/null variables as delete)
158
+ const variables = pin!= null? { pin } : {}
159
+ return mexQuery(variables, USERNAME_QUERY_IDS.PIN_SET, 'xwa2_username_pin_set')
160
+ }
161
+
162
+ /**
163
+ * Look up a contact by their @username via USync.
164
+ *
165
+ * Confirmed via USyncContactProtocol.getUserElement():
166
+ * { tag: 'contact', attrs: { username, pin? } }
167
+ *
168
+ * @param {string} username - The username to look up (without @)
169
+ * @param {string} [pin] - Optional PIN if the username is PIN-protected
170
+ * @returns {{ jid, lid?, contact: boolean }|null}
171
+ */
172
+ const findUserByUsername = async (username, pin) => {
173
+ const usyncQuery = new USyncQuery().withContactProtocol()
174
+ const user = new USyncUser().withUsername(username)
175
+ if (pin) user.withUsernameKey(pin)
176
+ usyncQuery.withUser(user)
177
+ const result = await executeUSyncQuery(usyncQuery)
178
+ if (!result?.list?.length) return null
179
+ const entry = result.list[0]
180
+ return {
181
+ jid: entry.id,
182
+ contact: entry.contact?? false
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Fetch the username of one or more contacts by their JID.
188
+ * Uses USync with the username protocol.
189
+ *
190
+ * @param {...string} jids - One or more JIDs
191
+ * @returns {Array<{ id, username: string|null }>}
192
+ */
193
+ const fetchContactUsernames = async (...jids) => {
194
+ const usyncQuery = new USyncQuery().withUsernameProtocol()
195
+ for (const jid of jids) {
196
+ usyncQuery.withUser(new USyncUser().withId(jid))
197
+ }
198
+ const result = await executeUSyncQuery(usyncQuery)
199
+ return result?.list?? []
200
+ }
201
+
202
+ /**
203
+ * Check multiple usernames for availability at once.
204
+ * @param {string[]} usernames - Array of usernames (without @)
205
+ */
206
+ const checkUsernameMulti = async usernames => {
207
+ const data = await mexQuery(
208
+ { usernames },
209
+ USERNAME_QUERY_IDS.CHECK_MULTI,
210
+ 'xwa2_username_check_multi'
211
+ )
212
+ return data
213
+ }
214
+
215
+ /**
216
+ * Fetch username recommendations for the current user.
217
+ * @param {string} [source] - Source hint: 'FB' | 'IG' | 'USER_INPUT'
218
+ */
219
+ const getUsernameRecommendations = async (source = null) => {
220
+ const variables = {}
221
+ if (source) variables.source = source
222
+ return mexQuery(variables, USERNAME_QUERY_IDS.GET_RECOMMENDATIONS, 'xwa2_username_get_recommendations')
223
+ }
224
+
225
+ return {
226
+ ...sock,
227
+ checkUsername,
228
+ checkUsernameMulti,
229
+ setUsername,
230
+ deleteUsername,
231
+ getMyUsername,
232
+ getUsernameRecommendations,
233
+ setUsernamePin,
234
+ findUserByUsername,
235
+ fetchContactUsernames,
236
+ checkAndSetUsername,
237
+ USERNAME_QUERY_IDS,
238
+ USERNAME_CHECK_RESULT,
239
+ USERNAME_SOURCE
240
+ }
241
+ }
242
+
243
+ exports.makeUsernameSocket = makeUsernameSocket