@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.
- package/README.md +1 -1
- package/WAProto/WAProto.proto +6 -347
- package/WAProto/fix-imports.js +85 -86
- package/WAProto/index.d.ts +25 -4913
- package/WAProto/index.js +93 -14069
- package/{src → lib}/Defaults/index.js +2 -13
- package/{src → lib}/Signal/Group/group_cipher.js +5 -5
- package/{src → lib}/Signal/Group/keyhelper.js +4 -4
- package/{src → lib}/Signal/Group/sender-chain-key.js +2 -2
- package/{src → lib}/Signal/Group/sender-key-distribution-message.js +1 -1
- package/{src → lib}/Signal/Group/sender-key-message.js +3 -3
- package/{src → lib}/Signal/Group/sender-key-record.js +1 -1
- package/{src → lib}/Signal/Group/sender-message-key.js +2 -2
- package/{src → lib}/Signal/libsignal.js +26 -6
- package/{src → lib}/Socket/chats.js +58 -616
- package/{src → lib}/Socket/communities.js +28 -59
- package/{src → lib}/Socket/graphql.js +103 -362
- package/{src → lib}/Socket/groups.js +29 -77
- package/{src → lib}/Socket/index.js +5 -3
- package/{src → lib}/Socket/interactive-handler.js +60 -117
- package/{src → lib}/Socket/interop.js +19 -36
- package/lib/Socket/luxu.js +387 -0
- package/{src → lib}/Socket/managed-account.js +27 -58
- package/{src → lib}/Socket/messages-recv.js +144 -715
- package/{src → lib}/Socket/messages-send.js +102 -412
- package/lib/Socket/mex.js +60 -0
- package/{src → lib}/Socket/newsletter.js +23 -266
- package/{src → lib}/Socket/privacy.js +38 -41
- package/{src → lib}/Socket/registration.js +106 -113
- package/{src → lib}/Socket/socket.js +101 -88
- package/{src → lib}/Socket/text-router.js +17 -1
- package/lib/Socket/username.js +243 -0
- package/lib/Store/keyed-db.js +108 -0
- package/{src → lib}/Store/make-in-memory-store.js +104 -6
- package/lib/Types/Mex.js +112 -0
- package/{src → lib}/Types/Newsletter.js +41 -72
- package/{src → lib}/Types/index.js +1 -0
- package/{src → lib}/Utils/chat-utils.js +9 -73
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +42 -0
- package/lib/Utils/crypto.js +155 -0
- package/lib/Utils/curve25519-js.js +275 -0
- package/{src → lib}/Utils/decode-wa-message.js +36 -284
- package/{src → lib}/Utils/event-buffer.js +27 -6
- package/{src → lib}/Utils/generics.js +37 -114
- package/{src → lib}/Utils/index.js +5 -1
- package/lib/Utils/lt-hash.js +39 -0
- package/{src → lib}/Utils/message-retry-manager.js +39 -0
- package/{src → lib}/Utils/messages-media.js +58 -105
- package/{src → lib}/Utils/messages.js +529 -115
- package/lib/Utils/meta-ai-msmsg.js +262 -0
- package/lib/Utils/native-bridge.js +82 -0
- package/{src → lib}/Utils/noise-handler.js +15 -13
- package/{src → lib}/Utils/process-message.js +55 -54
- package/{src → lib}/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/{src → lib}/Utils/signal.js +51 -19
- package/{src → lib}/Utils/stanza-ack.js +2 -36
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/{src → lib}/Utils/tc-token-utils.js +1 -1
- package/{src → lib}/Utils/validate-connection.js +11 -40
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/{src → lib}/WABinary/decode.js +4 -38
- package/lib/WABinary/encode.js +228 -0
- package/{src → lib}/WABinary/generic-utils.js +93 -1
- package/{src → lib}/WABinary/jid-utils.js +3 -16
- package/{src → lib}/WAM/encode.js +1 -1
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
- package/{src → lib}/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/{src → lib}/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/{src → lib}/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/{src → lib}/WAUSync/Protocols/USyncFeatureProtocol.js +2 -17
- package/{src → lib}/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/{src → lib}/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/{src → lib}/WAUSync/Protocols/index.js +0 -1
- package/{src → lib}/WAUSync/USyncQuery.js +6 -77
- package/{src → lib}/WAUSync/USyncUser.js +8 -16
- package/{src → lib}/WAUSync/index.js +0 -1
- package/{src → lib}/antiban.js +266 -355
- package/{src → lib}/index.js +2 -2
- package/package.json +19 -16
- package/WAProto/fix-import.js +0 -38
- package/src/Socket/mex.js +0 -47
- package/src/Socket/username.js +0 -234
- package/src/Utils/crypto.js +0 -125
- package/src/Utils/lt-hash.js +0 -10
- package/src/Utils/message-inspect.js +0 -400
- package/src/Utils/meta-ai-msmsg.js +0 -133
- package/src/WABinary/encode.js +0 -58
- package/src/WAUSync/Protocols/USyncBusinessProtocol.js +0 -100
- package/src/WAUSync/USyncBackoff.js +0 -31
- /package/{src → lib}/Defaults/phonenumber-mcc.json +0 -0
- /package/{src → lib}/Signal/Group/ciphertext-message.js +0 -0
- /package/{src → lib}/Signal/Group/group-session-builder.js +0 -0
- /package/{src → lib}/Signal/Group/index.js +0 -0
- /package/{src → lib}/Signal/Group/sender-key-name.js +0 -0
- /package/{src → lib}/Signal/Group/sender-key-state.js +0 -0
- /package/{src → lib}/Signal/lid-mapping.js +0 -0
- /package/{src → lib}/Socket/Client/index.js +0 -0
- /package/{src → lib}/Socket/Client/types.js +0 -0
- /package/{src → lib}/Socket/Client/websocket.js +0 -0
- /package/{src → lib}/Socket/aigroups.js +0 -0
- /package/{src → lib}/Socket/business.js +0 -0
- /package/{src → lib}/Store/index.js +0 -0
- /package/{src → lib}/Store/make-cache-manager-store.js +0 -0
- /package/{src → lib}/Store/make-ordered-dictionary.js +0 -0
- /package/{src → lib}/Store/object-repository.js +0 -0
- /package/{src → lib}/Types/Auth.js +0 -0
- /package/{src → lib}/Types/Bussines.js +0 -0
- /package/{src → lib}/Types/Call.js +0 -0
- /package/{src → lib}/Types/Chat.js +0 -0
- /package/{src → lib}/Types/Contact.js +0 -0
- /package/{src → lib}/Types/Events.js +0 -0
- /package/{src → lib}/Types/GroupMetadata.js +0 -0
- /package/{src → lib}/Types/Label.js +0 -0
- /package/{src → lib}/Types/LabelAssociation.js +0 -0
- /package/{src → lib}/Types/Message.js +0 -0
- /package/{src → lib}/Types/Product.js +0 -0
- /package/{src → lib}/Types/Signal.js +0 -0
- /package/{src → lib}/Types/Socket.js +0 -0
- /package/{src → lib}/Types/State.js +0 -0
- /package/{src → lib}/Types/USync.js +0 -0
- /package/{src → lib}/Utils/auth-utils.js +0 -0
- /package/{src → lib}/Utils/browser-utils.js +0 -0
- /package/{src → lib}/Utils/business.js +0 -0
- /package/{src → lib}/Utils/consumer-application.js +0 -0
- /package/{src → lib}/Utils/group-history.js +0 -0
- /package/{src → lib}/Utils/history.js +0 -0
- /package/{src → lib}/Utils/identity-change-handler.js +0 -0
- /package/{src → lib}/Utils/jid-display-normalization.js +0 -0
- /package/{src → lib}/Utils/link-preview.js +0 -0
- /package/{src → lib}/Utils/logger.js +0 -0
- /package/{src → lib}/Utils/make-mutex.js +0 -0
- /package/{src → lib}/Utils/message-composer.js +0 -0
- /package/{src → lib}/Utils/offline-node-processor.js +0 -0
- /package/{src → lib}/Utils/pre-key-manager.js +0 -0
- /package/{src → lib}/Utils/sync-action-utils.js +0 -0
- /package/{src → lib}/Utils/use-multi-file-auth-state.js +0 -0
- /package/{src → lib}/Utils/voip-rekey.js +0 -0
- /package/{src → lib}/WABinary/constants.js +0 -0
- /package/{src → lib}/WABinary/index.js +0 -0
- /package/{src → lib}/WABinary/types.js +0 -0
- /package/{src → lib}/WAM/BinaryInfo.js +0 -0
- /package/{src → lib}/WAM/constants.js +0 -0
- /package/{src → lib}/WAM/index.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncBotProfileProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncLIDProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncPictureProtocol.js +0 -0
- /package/{src → lib}/WAUSync/Protocols/USyncSidelistProtocol.js +0 -0
- /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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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('
|
|
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
|