@badzz88/baileys 8.5.4 → 8.5.5
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 +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- 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 +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/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/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
package/lib/Socket/socket.js
CHANGED
|
@@ -12,11 +12,15 @@ 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')
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Connects to WA servers and performs:
|
|
20
|
+
* - simple queries (no retry mechanism, wait for connection establishment)
|
|
21
|
+
* - listen to messages and emit events
|
|
22
|
+
* - query phone connection
|
|
23
|
+
*/
|
|
20
24
|
const makeSocket = config => {
|
|
21
25
|
const {
|
|
22
26
|
waWebSocketUrl,
|
|
@@ -57,9 +61,9 @@ const makeSocket = config => {
|
|
|
57
61
|
if (url.protocol === 'wss' && authState?.creds?.routingInfo) {
|
|
58
62
|
url.searchParams.append('ED', authState.creds.routingInfo.toString('base64url'))
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
/** ephemeral key pair used to encrypt/decrypt communication. Unique for each connection */
|
|
61
65
|
const ephemeralKeyPair = Utils_1.Curve.generateKeyPair()
|
|
62
|
-
|
|
66
|
+
/** WA noise protocol wrapper */
|
|
63
67
|
const noise = (0, Utils_1.makeNoiseHandler)({
|
|
64
68
|
keyPair: ephemeralKeyPair,
|
|
65
69
|
NOISE_HEADER: Defaults_1.NOISE_WA_HEADER,
|
|
@@ -67,10 +71,9 @@ const makeSocket = config => {
|
|
|
67
71
|
routingInfo: authState?.creds?.routingInfo
|
|
68
72
|
})
|
|
69
73
|
const ws = new Client_1.WebSocketClient(url, config)
|
|
70
|
-
const connectionStartTs = Date.now()
|
|
71
74
|
ws.connect()
|
|
72
75
|
const sendPromise = (0, util_1.promisify)(ws.send)
|
|
73
|
-
|
|
76
|
+
/** send a raw buffer */
|
|
74
77
|
const sendRawMessage = async data => {
|
|
75
78
|
if (!ws.isOpen) {
|
|
76
79
|
throw new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed })
|
|
@@ -85,7 +88,7 @@ const makeSocket = config => {
|
|
|
85
88
|
}
|
|
86
89
|
})
|
|
87
90
|
}
|
|
88
|
-
|
|
91
|
+
/** send a binary node */
|
|
89
92
|
const sendNode = frame => {
|
|
90
93
|
if (logger.level === 'trace') {
|
|
91
94
|
logger.trace({ xml: (0, WABinary_1.binaryNodeToString)(frame), msg: 'xml send' })
|
|
@@ -93,7 +96,11 @@ const makeSocket = config => {
|
|
|
93
96
|
const buff = (0, WABinary_1.encodeBinaryNode)(frame)
|
|
94
97
|
return sendRawMessage(buff)
|
|
95
98
|
}
|
|
96
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Wait for a message with a certain tag to be received
|
|
101
|
+
* @param msgId the message tag to await
|
|
102
|
+
* @param timeoutMs timeout after which the promise will reject
|
|
103
|
+
*/
|
|
97
104
|
const waitForMessage = async (msgId, timeoutMs = defaultQueryTimeoutMs) => {
|
|
98
105
|
let onRecv
|
|
99
106
|
let onErr
|
|
@@ -117,7 +124,7 @@ const makeSocket = config => {
|
|
|
117
124
|
})
|
|
118
125
|
return result
|
|
119
126
|
} catch (error) {
|
|
120
|
-
|
|
127
|
+
// Catch timeout and return undefined instead of throwing
|
|
121
128
|
if (error instanceof boom_1.Boom && error.output?.statusCode === Types_1.DisconnectReason.timedOut) {
|
|
122
129
|
logger?.warn?.({ msgId }, 'timed out waiting for message')
|
|
123
130
|
return undefined
|
|
@@ -131,7 +138,7 @@ const makeSocket = config => {
|
|
|
131
138
|
}
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
|
-
|
|
141
|
+
/** send a query, and wait for its response. auto-generates message ID if not provided */
|
|
135
142
|
const query = async (node, timeoutMs) => {
|
|
136
143
|
if (!node.attrs.id) {
|
|
137
144
|
node.attrs.id = generateMessageTag()
|
|
@@ -148,7 +155,7 @@ const makeSocket = config => {
|
|
|
148
155
|
}
|
|
149
156
|
return result
|
|
150
157
|
}
|
|
151
|
-
|
|
158
|
+
// Validate current key-bundle on server; on failure, trigger pre-key upload and rethrow
|
|
152
159
|
const digestKeyBundle = async () => {
|
|
153
160
|
const res = await query({
|
|
154
161
|
tag: 'iq',
|
|
@@ -161,7 +168,7 @@ const makeSocket = config => {
|
|
|
161
168
|
throw new Error('encrypt/get digest returned no digest node')
|
|
162
169
|
}
|
|
163
170
|
}
|
|
164
|
-
|
|
171
|
+
// Rotate our signed pre-key on server; on failure, run digest as fallback and rethrow
|
|
165
172
|
const rotateSignedPreKey = async () => {
|
|
166
173
|
const newId = (creds.signedPreKey.keyId || 0) + 1
|
|
167
174
|
const skey = await (0, Utils_1.signedKeyPair)(creds.signedIdentityKey, newId)
|
|
@@ -176,23 +183,21 @@ const makeSocket = config => {
|
|
|
176
183
|
}
|
|
177
184
|
]
|
|
178
185
|
})
|
|
179
|
-
|
|
186
|
+
// Persist new signed pre-key in creds
|
|
180
187
|
ev.emit('creds.update', { signedPreKey: skey })
|
|
181
188
|
}
|
|
182
189
|
const executeUSyncQuery = async usyncQuery => {
|
|
183
190
|
if (usyncQuery.protocols.length === 0) {
|
|
184
191
|
throw new boom_1.Boom('USyncQuery must have at least one protocol')
|
|
185
192
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
193
|
+
// todo: validate users, throw WARNING on no valid users
|
|
194
|
+
// variable below has only validated users
|
|
195
|
+
const validUsers = usyncQuery.users
|
|
190
196
|
const userNodes = validUsers.map(user => {
|
|
191
197
|
return {
|
|
192
198
|
tag: 'user',
|
|
193
199
|
attrs: {
|
|
194
|
-
jid: !user.phone ? user.id : undefined
|
|
195
|
-
pn_jid: user.pnJid
|
|
200
|
+
jid: !user.phone ? user.id : undefined
|
|
196
201
|
},
|
|
197
202
|
content: usyncQuery.protocols.map(a => a.getUserElement(user)).filter(a => a !== null)
|
|
198
203
|
}
|
|
@@ -228,17 +233,8 @@ const makeSocket = config => {
|
|
|
228
233
|
}
|
|
229
234
|
]
|
|
230
235
|
}
|
|
231
|
-
await (0, WAUSync_1.waitForBackoff)(usyncQuery)
|
|
232
236
|
const result = await query(iq)
|
|
233
|
-
|
|
234
|
-
if (parsed?.errors) {
|
|
235
|
-
for (const [protocolName, err] of Object.entries(parsed.errors)) {
|
|
236
|
-
if (err?.errorBackoff) {
|
|
237
|
-
;(0, WAUSync_1.setProtocolBackoffMs)(protocolName, err.errorBackoff * 1000)
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return parsed
|
|
237
|
+
return usyncQuery.parseUSyncQueryResult(result)
|
|
242
238
|
}
|
|
243
239
|
const onWhatsApp = async (...phoneNumber) => {
|
|
244
240
|
let usyncQuery = new WAUSync_1.USyncQuery()
|
|
@@ -257,7 +253,7 @@ const makeSocket = config => {
|
|
|
257
253
|
}
|
|
258
254
|
}
|
|
259
255
|
if (usyncQuery.users.length === 0) {
|
|
260
|
-
return []
|
|
256
|
+
return [] // return early without forcing an empty query
|
|
261
257
|
}
|
|
262
258
|
const results = await executeUSyncQuery(usyncQuery)
|
|
263
259
|
if (results) {
|
|
@@ -275,7 +271,7 @@ const makeSocket = config => {
|
|
|
275
271
|
}
|
|
276
272
|
}
|
|
277
273
|
if (usyncQuery.users.length === 0) {
|
|
278
|
-
return []
|
|
274
|
+
return [] // return early without forcing an empty query
|
|
279
275
|
}
|
|
280
276
|
const results = await executeUSyncQuery(usyncQuery)
|
|
281
277
|
if (results) {
|
|
@@ -283,9 +279,83 @@ const makeSocket = config => {
|
|
|
283
279
|
}
|
|
284
280
|
return []
|
|
285
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
|
+
}
|
|
286
356
|
const ev = (0, Utils_1.makeEventBuffer)(logger)
|
|
287
357
|
const { creds } = authState
|
|
288
|
-
|
|
358
|
+
// add transaction capability
|
|
289
359
|
const keys = (0, Utils_1.addTransactionCapability)(authState.keys, logger, transactionOpts)
|
|
290
360
|
const signalRepository = makeSignalRepository({ creds, keys }, logger, pnFromLIDUSync)
|
|
291
361
|
let lastDateRecv
|
|
@@ -293,11 +363,12 @@ const makeSocket = config => {
|
|
|
293
363
|
let keepAliveReq
|
|
294
364
|
let qrTimer
|
|
295
365
|
let closed = false
|
|
296
|
-
|
|
366
|
+
const socketEndHandlers = []
|
|
367
|
+
/** log & process any unexpected errors */
|
|
297
368
|
const onUnexpectedError = (err, msg) => {
|
|
298
369
|
logger.error({ err }, `unexpected error in '${msg}'`)
|
|
299
370
|
}
|
|
300
|
-
|
|
371
|
+
/** await the next incoming message */
|
|
301
372
|
const awaitNextMessage = async sendMsg => {
|
|
302
373
|
if (!ws.isOpen) {
|
|
303
374
|
throw new boom_1.Boom('Connection Closed', {
|
|
@@ -322,7 +393,7 @@ const makeSocket = config => {
|
|
|
322
393
|
}
|
|
323
394
|
return result
|
|
324
395
|
}
|
|
325
|
-
|
|
396
|
+
/** connection handshake */
|
|
326
397
|
const validateConnection = async () => {
|
|
327
398
|
let helloMsg = {
|
|
328
399
|
clientHello: { ephemeral: ephemeralKeyPair.public }
|
|
@@ -333,7 +404,7 @@ const makeSocket = config => {
|
|
|
333
404
|
const result = await awaitNextMessage(init)
|
|
334
405
|
const handshake = index_js_1.proto.HandshakeMessage.decode(result)
|
|
335
406
|
logger.trace({ handshake }, 'handshake recv from WA')
|
|
336
|
-
const
|
|
407
|
+
const keyEnc = noise.processHandshake(handshake, creds.noiseKey)
|
|
337
408
|
let node
|
|
338
409
|
if (!creds.me) {
|
|
339
410
|
node = (0, Utils_1.generateRegistrationNode)(creds, config)
|
|
@@ -352,9 +423,6 @@ const makeSocket = config => {
|
|
|
352
423
|
}).finish()
|
|
353
424
|
)
|
|
354
425
|
await noise.finishInit()
|
|
355
|
-
if (serverStaticPub && !creds.serverStaticPub) {
|
|
356
|
-
ev.emit('creds.update', { serverStaticPub })
|
|
357
|
-
}
|
|
358
426
|
startKeepAliveRequest()
|
|
359
427
|
}
|
|
360
428
|
const getAvailablePreKeysOnServer = async () => {
|
|
@@ -371,12 +439,12 @@ const makeSocket = config => {
|
|
|
371
439
|
const countChild = (0, WABinary_1.getBinaryNodeChild)(result, 'count')
|
|
372
440
|
return +countChild.attrs.value
|
|
373
441
|
}
|
|
374
|
-
|
|
442
|
+
// Pre-key upload state management
|
|
375
443
|
let uploadPreKeysPromise = null
|
|
376
444
|
let lastUploadTime = 0
|
|
377
|
-
|
|
445
|
+
/** generates and uploads a set of pre-keys to the server */
|
|
378
446
|
const uploadPreKeys = async (count = Defaults_1.MIN_PREKEY_COUNT, retryCount = 0) => {
|
|
379
|
-
|
|
447
|
+
// Check minimum interval (except for retries)
|
|
380
448
|
if (retryCount === 0) {
|
|
381
449
|
const timeSinceLastUpload = Date.now() - lastUploadTime
|
|
382
450
|
if (timeSinceLastUpload < Defaults_1.MIN_UPLOAD_INTERVAL) {
|
|
@@ -384,29 +452,29 @@ const makeSocket = config => {
|
|
|
384
452
|
return
|
|
385
453
|
}
|
|
386
454
|
}
|
|
387
|
-
|
|
455
|
+
// Prevent multiple concurrent uploads
|
|
388
456
|
if (uploadPreKeysPromise) {
|
|
389
457
|
logger.debug('Pre-key upload already in progress, waiting for completion')
|
|
390
458
|
await uploadPreKeysPromise
|
|
391
459
|
}
|
|
392
460
|
const uploadLogic = async () => {
|
|
393
461
|
logger.info({ count, retryCount }, 'uploading pre-keys')
|
|
394
|
-
|
|
462
|
+
// Generate and save pre-keys atomically (prevents ID collisions on retry)
|
|
395
463
|
const node = await keys.transaction(async () => {
|
|
396
464
|
logger.debug({ requestedCount: count }, 'generating pre-keys with requested count')
|
|
397
465
|
const { update, node } = await (0, Utils_1.getNextPreKeysNode)({ creds, keys }, count)
|
|
398
|
-
|
|
466
|
+
// Update credentials immediately to prevent duplicate IDs on retry
|
|
399
467
|
ev.emit('creds.update', update)
|
|
400
|
-
return node
|
|
468
|
+
return node // Only return node since update is already used
|
|
401
469
|
}, creds?.me?.id || 'upload-pre-keys')
|
|
402
|
-
|
|
470
|
+
// Upload to server (outside transaction, can fail without affecting local keys)
|
|
403
471
|
try {
|
|
404
472
|
await query(node)
|
|
405
473
|
logger.info({ count }, 'uploaded pre-keys successfully')
|
|
406
474
|
lastUploadTime = Date.now()
|
|
407
475
|
} catch (uploadError) {
|
|
408
476
|
logger.error({ uploadError: uploadError.toString(), count }, 'Failed to upload pre-keys to server')
|
|
409
|
-
|
|
477
|
+
// Exponential backoff retry (max 3 retries)
|
|
410
478
|
if (retryCount < 3) {
|
|
411
479
|
const backoffDelay = Math.min(1000 * Math.pow(2, retryCount), 10000)
|
|
412
480
|
logger.info(`Retrying pre-key upload in ${backoffDelay}ms`)
|
|
@@ -416,7 +484,7 @@ const makeSocket = config => {
|
|
|
416
484
|
throw uploadError
|
|
417
485
|
}
|
|
418
486
|
}
|
|
419
|
-
|
|
487
|
+
// Add timeout protection
|
|
420
488
|
uploadPreKeysPromise = Promise.race([
|
|
421
489
|
uploadLogic(),
|
|
422
490
|
new Promise((_, reject) =>
|
|
@@ -464,47 +532,40 @@ const makeSocket = config => {
|
|
|
464
532
|
}
|
|
465
533
|
} catch (error) {
|
|
466
534
|
logger.error({ error }, 'Failed to check/upload pre-keys during initialization')
|
|
467
|
-
|
|
535
|
+
// Don't throw - allow connection to continue even if pre-key check fails
|
|
468
536
|
}
|
|
469
537
|
}
|
|
470
538
|
const onMessageReceived = async data => {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
logger.trace({ xml: (0, WABinary_1.binaryNodeToString)(frame), msg: 'recv xml' })
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
anyTriggered = ws.emit(`${Defaults_1.DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
|
|
485
|
-
|
|
486
|
-
const l0 = frame.tag
|
|
487
|
-
const l1 = frame.attrs || {}
|
|
488
|
-
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
|
489
|
-
for (const key of Object.keys(l1)) {
|
|
490
|
-
anyTriggered =
|
|
491
|
-
ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered
|
|
492
|
-
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered
|
|
493
|
-
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered
|
|
494
|
-
}
|
|
495
|
-
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
|
|
496
|
-
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
|
|
497
|
-
if (!anyTriggered && logger.level === 'debug') {
|
|
498
|
-
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv')
|
|
499
|
-
}
|
|
539
|
+
await noise.decodeFrame(data, frame => {
|
|
540
|
+
// reset ping timeout
|
|
541
|
+
lastDateRecv = new Date()
|
|
542
|
+
let anyTriggered = false
|
|
543
|
+
anyTriggered = ws.emit('frame', frame)
|
|
544
|
+
// if it's a binary node
|
|
545
|
+
if (!(frame instanceof Uint8Array)) {
|
|
546
|
+
const msgId = frame.attrs.id
|
|
547
|
+
if (logger.level === 'trace') {
|
|
548
|
+
logger.trace({ xml: (0, WABinary_1.binaryNodeToString)(frame), msg: 'recv xml' })
|
|
500
549
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
550
|
+
/* Check if this is a response to a message we sent */
|
|
551
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_TAG_PREFIX}${msgId}`, frame) || anyTriggered
|
|
552
|
+
/* Check if this is a response to a message we are expecting */
|
|
553
|
+
const l0 = frame.tag
|
|
554
|
+
const l1 = frame.attrs || {}
|
|
555
|
+
const l2 = Array.isArray(frame.content) ? frame.content[0]?.tag : ''
|
|
556
|
+
for (const key of Object.keys(l1)) {
|
|
557
|
+
anyTriggered =
|
|
558
|
+
ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]},${l2}`, frame) || anyTriggered
|
|
559
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}:${l1[key]}`, frame) || anyTriggered
|
|
560
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},${key}`, frame) || anyTriggered
|
|
561
|
+
}
|
|
562
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0},,${l2}`, frame) || anyTriggered
|
|
563
|
+
anyTriggered = ws.emit(`${Defaults_1.DEF_CALLBACK_PREFIX}${l0}`, frame) || anyTriggered
|
|
564
|
+
if (!anyTriggered && logger.level === 'debug') {
|
|
565
|
+
logger.debug({ unhandled: true, msgId, fromMe: false, frame }, 'communication recv')
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
})
|
|
508
569
|
}
|
|
509
570
|
const end = async error => {
|
|
510
571
|
if (closed) {
|
|
@@ -514,7 +575,6 @@ const makeSocket = config => {
|
|
|
514
575
|
closed = true
|
|
515
576
|
logger.info({ trace: error?.stack }, error ? 'connection errored' : 'connection closed')
|
|
516
577
|
clearInterval(keepAliveReq)
|
|
517
|
-
clearInterval(wamFlushInterval)
|
|
518
578
|
clearTimeout(qrTimer)
|
|
519
579
|
ws.removeAllListeners('close')
|
|
520
580
|
ws.removeAllListeners('open')
|
|
@@ -524,6 +584,13 @@ const makeSocket = config => {
|
|
|
524
584
|
await ws.close()
|
|
525
585
|
} catch {}
|
|
526
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
|
+
}
|
|
527
594
|
ev.emit('connection.update', {
|
|
528
595
|
connection: 'close',
|
|
529
596
|
lastDisconnect: {
|
|
@@ -560,13 +627,14 @@ const makeSocket = config => {
|
|
|
560
627
|
lastDateRecv = new Date()
|
|
561
628
|
}
|
|
562
629
|
const diff = Date.now() - lastDateRecv.getTime()
|
|
563
|
-
|
|
630
|
+
/*
|
|
631
|
+
check if it's been a suspicious amount of time since the server responded with our last seen
|
|
632
|
+
it could be that the network is down
|
|
633
|
+
*/
|
|
564
634
|
if (diff > keepAliveIntervalMs + 5000) {
|
|
565
635
|
void end(new boom_1.Boom('Connection was lost', { statusCode: Types_1.DisconnectReason.connectionLost }))
|
|
566
636
|
} else if (ws.isOpen) {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
637
|
+
// if its all good, send a keep alive request
|
|
570
638
|
query({
|
|
571
639
|
tag: 'iq',
|
|
572
640
|
attrs: {
|
|
@@ -574,7 +642,8 @@ const makeSocket = config => {
|
|
|
574
642
|
to: WABinary_1.S_WHATSAPP_NET,
|
|
575
643
|
type: 'get',
|
|
576
644
|
xmlns: 'w:p'
|
|
577
|
-
}
|
|
645
|
+
},
|
|
646
|
+
content: [{ tag: 'ping', attrs: {} }]
|
|
578
647
|
}).catch(err => {
|
|
579
648
|
logger.error({ trace: err.stack }, 'error in sending keep alive')
|
|
580
649
|
})
|
|
@@ -582,7 +651,7 @@ const makeSocket = config => {
|
|
|
582
651
|
logger.warn('keep alive called when WS not open')
|
|
583
652
|
}
|
|
584
653
|
}, keepAliveIntervalMs))
|
|
585
|
-
|
|
654
|
+
/** i have no idea why this exists. pls enlighten me */
|
|
586
655
|
const sendPassiveIq = tag =>
|
|
587
656
|
query({
|
|
588
657
|
tag: 'iq',
|
|
@@ -593,7 +662,7 @@ const makeSocket = config => {
|
|
|
593
662
|
},
|
|
594
663
|
content: [{ tag, attrs: {} }]
|
|
595
664
|
})
|
|
596
|
-
|
|
665
|
+
/** logout & invalidate connection */
|
|
597
666
|
const logout = async msg => {
|
|
598
667
|
const jid = authState.creds.me?.id
|
|
599
668
|
if (jid) {
|
|
@@ -620,7 +689,7 @@ const makeSocket = config => {
|
|
|
620
689
|
}
|
|
621
690
|
const requestPairingCode = async (phoneNumber, customPairingCode) => {
|
|
622
691
|
const pairingCode = customPairingCode ?? (0, Utils_1.bytesToCrockford)((0, crypto_1.randomBytes)(5))
|
|
623
|
-
if (customPairingCode && customPairingCode
|
|
692
|
+
if (customPairingCode && customPairingCode?.length !== 8) {
|
|
624
693
|
throw new Error('Custom pairing code must be exactly 8 chars')
|
|
625
694
|
}
|
|
626
695
|
authState.creds.pairingCode = pairingCode
|
|
@@ -690,8 +759,7 @@ const makeSocket = config => {
|
|
|
690
759
|
attrs: {
|
|
691
760
|
to: WABinary_1.S_WHATSAPP_NET,
|
|
692
761
|
id: generateMessageTag(),
|
|
693
|
-
xmlns: 'w:stats'
|
|
694
|
-
type: 'set'
|
|
762
|
+
xmlns: 'w:stats'
|
|
695
763
|
},
|
|
696
764
|
content: [
|
|
697
765
|
{
|
|
@@ -702,30 +770,8 @@ const makeSocket = config => {
|
|
|
702
770
|
]
|
|
703
771
|
})
|
|
704
772
|
}
|
|
705
|
-
const flushWAMEvents = async () => {
|
|
706
|
-
if (!publicWAMBuffer.events.length) return
|
|
707
|
-
try {
|
|
708
|
-
const encoded = (0, WAM_1.encodeWAM)(publicWAMBuffer)
|
|
709
|
-
publicWAMBuffer.events = []
|
|
710
|
-
publicWAMBuffer.sequence = (publicWAMBuffer.sequence + 1) & 0xffff
|
|
711
|
-
await sendWAMBuffer(encoded)
|
|
712
|
-
} catch (e) {
|
|
713
|
-
logger.debug({ err: e }, 'WAM flush failed')
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
const wsOpenTs = { value: connectionStartTs }
|
|
717
773
|
ws.on('message', onMessageReceived)
|
|
718
774
|
ws.on('open', async () => {
|
|
719
|
-
wsOpenTs.value = Date.now()
|
|
720
|
-
publicWAMBuffer.events.push({
|
|
721
|
-
WebcSocketConnect: {
|
|
722
|
-
props: {
|
|
723
|
-
webcSocketConnectDuration: wsOpenTs.value - connectionStartTs,
|
|
724
|
-
webcSocketConnectReason: 0
|
|
725
|
-
},
|
|
726
|
-
globals: {}
|
|
727
|
-
}
|
|
728
|
-
})
|
|
729
775
|
try {
|
|
730
776
|
await validateConnection()
|
|
731
777
|
} catch (err) {
|
|
@@ -733,15 +779,12 @@ const makeSocket = config => {
|
|
|
733
779
|
void end(err)
|
|
734
780
|
}
|
|
735
781
|
})
|
|
736
|
-
const wamFlushInterval = setInterval(() => {
|
|
737
|
-
void flushWAMEvents()
|
|
738
|
-
}, 30_000)
|
|
739
782
|
ws.on('error', mapWebSocketError(end))
|
|
740
783
|
ws.on(
|
|
741
784
|
'close',
|
|
742
785
|
() => void end(new boom_1.Boom('Connection Terminated', { statusCode: Types_1.DisconnectReason.connectionClosed }))
|
|
743
786
|
)
|
|
744
|
-
|
|
787
|
+
// the server terminated the connection
|
|
745
788
|
ws.on(
|
|
746
789
|
'CB:xmlstreamend',
|
|
747
790
|
() =>
|
|
@@ -749,7 +792,7 @@ const makeSocket = config => {
|
|
|
749
792
|
new boom_1.Boom('Connection Terminated by Server', { statusCode: Types_1.DisconnectReason.connectionClosed })
|
|
750
793
|
)
|
|
751
794
|
)
|
|
752
|
-
|
|
795
|
+
// QR gen
|
|
753
796
|
ws.on('CB:iq,type:set,pair-device', async stanza => {
|
|
754
797
|
const iq = {
|
|
755
798
|
tag: 'iq',
|
|
@@ -765,7 +808,7 @@ const makeSocket = config => {
|
|
|
765
808
|
const noiseKeyB64 = Buffer.from(creds.noiseKey.public).toString('base64')
|
|
766
809
|
const identityKeyB64 = Buffer.from(creds.signedIdentityKey.public).toString('base64')
|
|
767
810
|
const advB64 = creds.advSecretKey
|
|
768
|
-
let qrMs = qrTimeout || 60000
|
|
811
|
+
let qrMs = qrTimeout || 60000 // time to let a QR live
|
|
769
812
|
const genPairQR = () => {
|
|
770
813
|
if (!ws.isOpen) {
|
|
771
814
|
return
|
|
@@ -775,16 +818,16 @@ const makeSocket = config => {
|
|
|
775
818
|
void end(new boom_1.Boom('QR refs attempts ended', { statusCode: Types_1.DisconnectReason.timedOut }))
|
|
776
819
|
return
|
|
777
820
|
}
|
|
778
|
-
const ref = refNode.content.toString('
|
|
821
|
+
const ref = refNode.content.toString('utf-8')
|
|
779
822
|
const qr = [ref, noiseKeyB64, identityKeyB64, advB64].join(',')
|
|
780
823
|
ev.emit('connection.update', { qr })
|
|
781
824
|
qrTimer = setTimeout(genPairQR, qrMs)
|
|
782
|
-
qrMs = qrTimeout || 20000
|
|
825
|
+
qrMs = qrTimeout || 20000 // shorter subsequent qrs
|
|
783
826
|
}
|
|
784
827
|
genPairQR()
|
|
785
828
|
})
|
|
786
|
-
|
|
787
|
-
|
|
829
|
+
// device paired for the first time
|
|
830
|
+
// if device pairs successfully, the server asks to restart the connection
|
|
788
831
|
ws.on('CB:iq,,pair-success', async stanza => {
|
|
789
832
|
logger.debug('pair success recv')
|
|
790
833
|
try {
|
|
@@ -803,16 +846,16 @@ const makeSocket = config => {
|
|
|
803
846
|
void end(error)
|
|
804
847
|
}
|
|
805
848
|
})
|
|
806
|
-
|
|
849
|
+
// login complete
|
|
807
850
|
ws.on('CB:success', async node => {
|
|
808
851
|
try {
|
|
809
852
|
updateServerTimeOffset(node)
|
|
810
853
|
await uploadPreKeysToServerIfRequired()
|
|
811
|
-
|
|
854
|
+
// passive→active handshake only for companion (passive) sessions
|
|
812
855
|
if (!config.masqueradeAsPrimary) {
|
|
813
856
|
await sendPassiveIq('active')
|
|
814
857
|
}
|
|
815
|
-
|
|
858
|
+
// After successful login, validate our key-bundle against server
|
|
816
859
|
try {
|
|
817
860
|
await digestKeyBundle()
|
|
818
861
|
} catch (e) {
|
|
@@ -822,15 +865,7 @@ const makeSocket = config => {
|
|
|
822
865
|
logger.warn({ err }, 'failed to send initial passive iq')
|
|
823
866
|
}
|
|
824
867
|
logger.info('opened connection to WA')
|
|
825
|
-
clearTimeout(qrTimer)
|
|
826
|
-
|
|
827
|
-
publicWAMBuffer.events.push({
|
|
828
|
-
Login: {
|
|
829
|
-
props: { loginResult: 1, loginT: Date.now() - connectionStartTs, connectionOrigin: 1, passive: false },
|
|
830
|
-
globals: {}
|
|
831
|
-
}
|
|
832
|
-
})
|
|
833
|
-
void flushWAMEvents()
|
|
868
|
+
clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
|
|
834
869
|
ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
|
|
835
870
|
if (config.syncFullHistory && authState.creds.initialFullSyncDone !== true) {
|
|
836
871
|
logger.debug('initial full history sync completed, persisting one-time flag')
|
|
@@ -843,16 +878,16 @@ const makeSocket = config => {
|
|
|
843
878
|
process.nextTick(async () => {
|
|
844
879
|
try {
|
|
845
880
|
const myPN = authState.creds.me.id
|
|
846
|
-
|
|
881
|
+
// Store our own LID-PN mapping
|
|
847
882
|
await signalRepository.lidMapping.storeLIDPNMappings([{ lid: myLID, pn: myPN }])
|
|
848
|
-
|
|
883
|
+
// Create device list for our own user (needed for bulk migration)
|
|
849
884
|
const { user, device } = (0, WABinary_1.jidDecode)(myPN)
|
|
850
885
|
await authState.keys.set({
|
|
851
886
|
'device-list': {
|
|
852
887
|
[user]: [device?.toString() || '0']
|
|
853
888
|
}
|
|
854
889
|
})
|
|
855
|
-
|
|
890
|
+
// migrate our own session
|
|
856
891
|
await signalRepository.migrateSession(myPN, myLID)
|
|
857
892
|
logger.info({ myPN, myLID }, 'Own LID session created successfully')
|
|
858
893
|
} catch (error) {
|
|
@@ -867,7 +902,7 @@ const makeSocket = config => {
|
|
|
867
902
|
const { reason, statusCode } = (0, Utils_1.getErrorCodeFromStreamError)(node)
|
|
868
903
|
void end(new boom_1.Boom(`Stream Errored (${reason})`, { statusCode, data: reasonNode || node }))
|
|
869
904
|
})
|
|
870
|
-
|
|
905
|
+
// stream fail, possible logout
|
|
871
906
|
ws.on('CB:failure', node => {
|
|
872
907
|
const reason = +(node.attrs.reason || 500)
|
|
873
908
|
void end(new boom_1.Boom('Connection Failure', { statusCode: reason, data: node.attrs }))
|
|
@@ -877,15 +912,6 @@ const makeSocket = config => {
|
|
|
877
912
|
new boom_1.Boom('Multi-device beta not joined', { statusCode: Types_1.DisconnectReason.multideviceMismatch })
|
|
878
913
|
)
|
|
879
914
|
})
|
|
880
|
-
ws.on('CB:ib,,dirty', node => {
|
|
881
|
-
const dirtyNode = (0, WABinary_1.getBinaryNodeChild)(node, 'dirty')
|
|
882
|
-
const dirtyType = dirtyNode?.attrs?.type
|
|
883
|
-
const dirtyTs = dirtyNode?.attrs?.timestamp ? +dirtyNode.attrs.timestamp : undefined
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
logger.debug({ dirtyType, dirtyTs }, 'received dirty flag')
|
|
887
|
-
ev.emit('account.dirty', { type: dirtyType, timestamp: dirtyTs })
|
|
888
|
-
})
|
|
889
915
|
ws.on('CB:ib,,offline_preview', async node => {
|
|
890
916
|
logger.info('offline preview received', JSON.stringify(node))
|
|
891
917
|
await sendNode({
|
|
@@ -894,19 +920,6 @@ const makeSocket = config => {
|
|
|
894
920
|
content: [{ tag: 'offline_batch', attrs: { count: '100' } }]
|
|
895
921
|
})
|
|
896
922
|
})
|
|
897
|
-
ws.on('CB:ib,,thread_metadata', node => {
|
|
898
|
-
const tmNode = (0, WABinary_1.getBinaryNodeChild)(node, 'thread_metadata')
|
|
899
|
-
const items = (0, WABinary_1.getBinaryNodeChildren)(tmNode, 'item')
|
|
900
|
-
if (items.length) {
|
|
901
|
-
ev.emit(
|
|
902
|
-
'thread-metadata.update',
|
|
903
|
-
items.map(item => ({
|
|
904
|
-
jid: item.attrs.from,
|
|
905
|
-
t: item.attrs.t ? +item.attrs.t : undefined
|
|
906
|
-
}))
|
|
907
|
-
)
|
|
908
|
-
}
|
|
909
|
-
})
|
|
910
923
|
ws.on('CB:ib,,edge_routing', node => {
|
|
911
924
|
const edgeRoutingNode = (0, WABinary_1.getBinaryNodeChild)(node, 'edge_routing')
|
|
912
925
|
const routingInfo = (0, WABinary_1.getBinaryNodeChild)(edgeRoutingNode, 'routing_info')
|
|
@@ -918,14 +931,14 @@ const makeSocket = config => {
|
|
|
918
931
|
let didStartBuffer = false
|
|
919
932
|
process.nextTick(() => {
|
|
920
933
|
if (creds.me?.id) {
|
|
921
|
-
|
|
922
|
-
|
|
934
|
+
// start buffering important events
|
|
935
|
+
// if we're logged in
|
|
923
936
|
ev.buffer()
|
|
924
937
|
didStartBuffer = true
|
|
925
938
|
}
|
|
926
939
|
ev.emit('connection.update', { connection: 'connecting', receivedPendingNotifications: false, qr: undefined })
|
|
927
940
|
})
|
|
928
|
-
|
|
941
|
+
// called when all offline notifs are handled
|
|
929
942
|
ws.on('CB:ib,,offline', node => {
|
|
930
943
|
const child = (0, WABinary_1.getBinaryNodeChild)(node, 'offline')
|
|
931
944
|
const offlineNotifs = +(child?.attrs.count || 0)
|
|
@@ -936,14 +949,11 @@ const makeSocket = config => {
|
|
|
936
949
|
}
|
|
937
950
|
ev.emit('connection.update', { receivedPendingNotifications: true })
|
|
938
951
|
})
|
|
939
|
-
|
|
952
|
+
// update credentials when required
|
|
940
953
|
ev.on('creds.update', update => {
|
|
941
954
|
const name = update.me?.name
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
if (typeof name === 'string' && name.length > 0 && creds.me?.name !== name) {
|
|
955
|
+
// if name has just been received
|
|
956
|
+
if (creds.me?.name !== name) {
|
|
947
957
|
logger.debug({ name }, 'updated pushName')
|
|
948
958
|
sendNode({
|
|
949
959
|
tag: 'presence',
|
|
@@ -1023,6 +1033,9 @@ const makeSocket = config => {
|
|
|
1023
1033
|
generateMessageTag
|
|
1024
1034
|
)
|
|
1025
1035
|
}
|
|
1036
|
+
const registerSocketEndHandler = handler => {
|
|
1037
|
+
socketEndHandlers.push(handler)
|
|
1038
|
+
}
|
|
1026
1039
|
return {
|
|
1027
1040
|
type: 'md',
|
|
1028
1041
|
ws,
|
|
@@ -1040,6 +1053,10 @@ const makeSocket = config => {
|
|
|
1040
1053
|
sendNode,
|
|
1041
1054
|
logout,
|
|
1042
1055
|
end,
|
|
1056
|
+
registerSocketEndHandler,
|
|
1057
|
+
toPn,
|
|
1058
|
+
toLID,
|
|
1059
|
+
fetchUsername,
|
|
1043
1060
|
onUnexpectedError,
|
|
1044
1061
|
uploadPreKeys,
|
|
1045
1062
|
uploadPreKeysToServerIfRequired,
|
|
@@ -1049,19 +1066,20 @@ const makeSocket = config => {
|
|
|
1049
1066
|
updateServerTimeOffset,
|
|
1050
1067
|
sendUnifiedSession,
|
|
1051
1068
|
wamBuffer: publicWAMBuffer,
|
|
1052
|
-
|
|
1069
|
+
/** Waits for the connection to WA to reach a state */
|
|
1053
1070
|
waitForConnectionUpdate: (0, Utils_1.bindWaitForConnectionUpdate)(ev),
|
|
1054
1071
|
sendWAMBuffer,
|
|
1055
1072
|
executeUSyncQuery,
|
|
1056
1073
|
onWhatsApp,
|
|
1057
1074
|
fetchAccountReachoutTimelock,
|
|
1058
|
-
fetchNewChatMessageCap
|
|
1059
|
-
logger,
|
|
1060
|
-
masqueradeAsPrimary: config.masqueradeAsPrimary
|
|
1075
|
+
fetchNewChatMessageCap
|
|
1061
1076
|
}
|
|
1062
1077
|
}
|
|
1063
1078
|
exports.makeSocket = makeSocket
|
|
1064
|
-
|
|
1079
|
+
/**
|
|
1080
|
+
* map the websocket error to the right type
|
|
1081
|
+
* so it can be retried by the caller
|
|
1082
|
+
* */
|
|
1065
1083
|
function mapWebSocketError(handler) {
|
|
1066
1084
|
return error => {
|
|
1067
1085
|
handler(
|