@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/Utils/generics.js
CHANGED
|
@@ -41,9 +41,11 @@ exports.bindWaitForEvent = bindWaitForEvent
|
|
|
41
41
|
exports.trimUndefined = trimUndefined
|
|
42
42
|
exports.bytesToCrockford = bytesToCrockford
|
|
43
43
|
exports.encodeNewsletterMessage = encodeNewsletterMessage
|
|
44
|
+
exports.generateIOSMessageID = void 0
|
|
45
|
+
exports.generateAndroMessageID = void 0
|
|
44
46
|
const boom_1 = require('@hapi/boom')
|
|
45
47
|
const crypto_1 = require('crypto')
|
|
46
|
-
const
|
|
48
|
+
const Crypto_1 = require('./crypto')
|
|
47
49
|
const DEFAULTS_1 = require('../Defaults')
|
|
48
50
|
const index_js_1 = require('../../WAProto/index.js')
|
|
49
51
|
const baileysVersion = DEFAULTS_1.VERSION
|
|
@@ -51,14 +53,14 @@ const Types_1 = require('../Types')
|
|
|
51
53
|
const WABinary_1 = require('../WABinary')
|
|
52
54
|
const crypto_2 = require('./crypto')
|
|
53
55
|
exports.BufferJSON = {
|
|
54
|
-
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
57
|
replacer: (k, value) => {
|
|
56
58
|
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
57
59
|
return { type: 'Buffer', data: Buffer.from(value?.data || value).toString('base64') }
|
|
58
60
|
}
|
|
59
61
|
return value
|
|
60
62
|
},
|
|
61
|
-
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
62
64
|
reviver: (_, value) => {
|
|
63
65
|
if (typeof value === 'object' && value !== null && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
64
66
|
return Buffer.from(value.data, 'base64')
|
|
@@ -79,7 +81,7 @@ const getKeyAuthor = (key, meId = 'me') =>
|
|
|
79
81
|
(key?.fromMe ? meId : key?.participantAlt || key?.remoteJidAlt || key?.participant || key?.remoteJid) || ''
|
|
80
82
|
exports.getKeyAuthor = getKeyAuthor
|
|
81
83
|
const isStringNullOrEmpty = value =>
|
|
82
|
-
|
|
84
|
+
// eslint-disable-next-line eqeqeq
|
|
83
85
|
value == null || value === ''
|
|
84
86
|
exports.isStringNullOrEmpty = isStringNullOrEmpty
|
|
85
87
|
const writeRandomPadMax16 = msg => {
|
|
@@ -100,7 +102,7 @@ const unpadRandomMax16 = e => {
|
|
|
100
102
|
return new Uint8Array(t.buffer, t.byteOffset, t.length - r)
|
|
101
103
|
}
|
|
102
104
|
exports.unpadRandomMax16 = unpadRandomMax16
|
|
103
|
-
|
|
105
|
+
// code is inspired by whatsmeow
|
|
104
106
|
const generateParticipantHashV2 = participants => {
|
|
105
107
|
participants.sort()
|
|
106
108
|
const sha256Hash = (0, crypto_2.sha256)(Buffer.from(participants.join(''))).toString('base64')
|
|
@@ -125,7 +127,7 @@ const encodeBigEndian = (e, t = 4) => {
|
|
|
125
127
|
exports.encodeBigEndian = encodeBigEndian
|
|
126
128
|
const toNumber = t => (typeof t === 'object' && t ? ('toNumber' in t ? t.toNumber() : t.low) : t || 0)
|
|
127
129
|
exports.toNumber = toNumber
|
|
128
|
-
|
|
130
|
+
/** unix timestamp of a date in seconds */
|
|
129
131
|
const unixTimestampSeconds = (date = new Date()) => Math.floor(date.getTime() / 1000)
|
|
130
132
|
exports.unixTimestampSeconds = unixTimestampSeconds
|
|
131
133
|
const debouncedTimeout = (intervalMs = 1000, task) => {
|
|
@@ -175,7 +177,7 @@ async function promiseTimeout(ms, promise) {
|
|
|
175
177
|
return new Promise(promise)
|
|
176
178
|
}
|
|
177
179
|
const stack = new Error().stack
|
|
178
|
-
|
|
180
|
+
// Create a promise that rejects in <ms> milliseconds
|
|
179
181
|
const { delay, cancel } = (0, exports.delayCancellable)(ms)
|
|
180
182
|
const p = new Promise((resolve, reject) => {
|
|
181
183
|
delay
|
|
@@ -194,26 +196,38 @@ async function promiseTimeout(ms, promise) {
|
|
|
194
196
|
}).finally(cancel)
|
|
195
197
|
return p
|
|
196
198
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const generateMessageIDV2 = userId => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
199
|
+
// inspired from whatsmeow code
|
|
200
|
+
// https://github.com/tulir/whatsmeow/blob/64bc969fbe78d31ae0dd443b8d4c80a5d026d07a/send.go#L42
|
|
201
|
+
const generateMessageIDV2 = (userId) => {
|
|
202
|
+
const data = Buffer.alloc(8 + 20 + 16);
|
|
203
|
+
data.writeBigUInt64BE(BigInt(Math.floor(Date.now() / 1000)));
|
|
204
|
+
if (userId) {
|
|
205
|
+
const id = (0, WABinary_1.jidDecode)(userId);
|
|
206
|
+
if (id?.user) {
|
|
207
|
+
data.write(id.user, 8);
|
|
208
|
+
data.write('@c.us', 8 + id.user.length);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const random = (0, crypto_1.randomBytes)(16);
|
|
212
|
+
random.copy(data, 28);
|
|
213
|
+
const hash = (0, crypto_1.createHash)('sha256').update(data).digest();
|
|
214
|
+
return 'XZCYYX-' + hash.toString('hex').toUpperCase().substring(0, 18);
|
|
215
|
+
};
|
|
214
216
|
exports.generateMessageIDV2 = generateMessageIDV2
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
+
const generateIOSMessageID = () => {
|
|
218
|
+
const prefix = '3A';
|
|
219
|
+
const random = (0, crypto_1.randomBytes)(9.5); // 19 hex chars = 9.5 bytes
|
|
220
|
+
return (prefix + random.toString('hex')).toUpperCase().substring(0, 21);
|
|
221
|
+
};
|
|
222
|
+
exports.generateIOSMessageID = generateIOSMessageID
|
|
223
|
+
const generateAndroMessageID = () => {
|
|
224
|
+
const prefix = '3A';
|
|
225
|
+
const random = (0, crypto_1.randomBytes)(16); // 32 hex chars = 16 bytes
|
|
226
|
+
return (random.toString('hex')).toUpperCase().substring(0, 21);
|
|
227
|
+
};
|
|
228
|
+
exports.generateAndroMessageID = generateAndroMessageID
|
|
229
|
+
// generate a random ID to attach to a message
|
|
230
|
+
const generateMessageID = () => 'XZCYYX-' + (0, crypto_1.randomBytes)(18).toString('hex').toUpperCase()
|
|
217
231
|
exports.generateMessageID = generateMessageID
|
|
218
232
|
function bindWaitForEvent(ev, event) {
|
|
219
233
|
return async (check, timeoutMs) => {
|
|
@@ -243,33 +257,30 @@ function bindWaitForEvent(ev, event) {
|
|
|
243
257
|
}
|
|
244
258
|
const bindWaitForConnectionUpdate = ev => bindWaitForEvent(ev, 'connection.update')
|
|
245
259
|
exports.bindWaitForConnectionUpdate = bindWaitForConnectionUpdate
|
|
246
|
-
|
|
260
|
+
/**
|
|
261
|
+
* utility that fetches latest baileys version from the master branch.
|
|
262
|
+
* Use to ensure your WA connection is always on the latest version
|
|
263
|
+
*/
|
|
247
264
|
const fetchLatestBaileysVersion = async (options = {}) => {
|
|
248
265
|
const URL = 'https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/Defaults/index.ts'
|
|
249
266
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const controller = new AbortController()
|
|
253
|
-
const timer = setTimeout(() => controller.abort(), options.timeout ?? 5000)
|
|
254
267
|
const response = await fetch(URL, {
|
|
255
268
|
dispatcher: options.dispatcher,
|
|
256
269
|
method: 'GET',
|
|
257
|
-
headers: options.headers
|
|
258
|
-
signal: controller.signal
|
|
270
|
+
headers: options.headers
|
|
259
271
|
})
|
|
260
|
-
clearTimeout(timer)
|
|
261
272
|
if (!response.ok) {
|
|
262
273
|
throw new boom_1.Boom(`Failed to fetch latest Baileys version: ${response.statusText}`, {
|
|
263
274
|
statusCode: response.status
|
|
264
275
|
})
|
|
265
276
|
}
|
|
266
277
|
const text = await response.text()
|
|
267
|
-
|
|
278
|
+
// Extract version from line 7 (const version = [...])
|
|
268
279
|
const lines = text.split('\n')
|
|
269
|
-
const versionLine = lines[6]
|
|
280
|
+
const versionLine = lines[6] // Line 7 (0-indexed)
|
|
270
281
|
const versionMatch = versionLine.match(/const version = \[(\d+),\s*(\d+),\s*(\d+)\]/)
|
|
271
282
|
if (versionMatch) {
|
|
272
|
-
const version = [parseInt(versionMatch[1]
|
|
283
|
+
const version = [parseInt(versionMatch[1]), parseInt(versionMatch[2]), parseInt(versionMatch[3])]
|
|
273
284
|
return {
|
|
274
285
|
version,
|
|
275
286
|
isLatest: true
|
|
@@ -286,25 +297,24 @@ const fetchLatestBaileysVersion = async (options = {}) => {
|
|
|
286
297
|
}
|
|
287
298
|
}
|
|
288
299
|
exports.fetchLatestBaileysVersion = fetchLatestBaileysVersion
|
|
289
|
-
|
|
300
|
+
/**
|
|
301
|
+
* A utility that fetches the latest web version of whatsapp.
|
|
302
|
+
* Use to ensure your WA connection is always on the latest version
|
|
303
|
+
*/
|
|
290
304
|
const fetchLatestWaWebVersion = async (options = {}) => {
|
|
291
305
|
try {
|
|
292
|
-
|
|
306
|
+
// Absolute minimal headers required to bypass anti-bot detection
|
|
293
307
|
const defaultHeaders = {
|
|
294
308
|
'sec-fetch-site': 'none',
|
|
295
309
|
'user-agent':
|
|
296
310
|
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
297
311
|
}
|
|
298
312
|
const headers = { ...defaultHeaders, ...options.headers }
|
|
299
|
-
const controller = new AbortController()
|
|
300
|
-
const timer = setTimeout(() => controller.abort(), options.timeout ?? 5000)
|
|
301
313
|
const response = await fetch('https://web.whatsapp.com/sw.js', {
|
|
302
314
|
...options,
|
|
303
315
|
method: 'GET',
|
|
304
|
-
headers
|
|
305
|
-
signal: controller.signal
|
|
316
|
+
headers
|
|
306
317
|
})
|
|
307
|
-
clearTimeout(timer)
|
|
308
318
|
if (!response.ok) {
|
|
309
319
|
throw new boom_1.Boom(`Failed to fetch sw.js: ${response.statusText}`, { statusCode: response.status })
|
|
310
320
|
}
|
|
@@ -334,7 +344,7 @@ const fetchLatestWaWebVersion = async (options = {}) => {
|
|
|
334
344
|
}
|
|
335
345
|
}
|
|
336
346
|
exports.fetchLatestWaWebVersion = fetchLatestWaWebVersion
|
|
337
|
-
|
|
347
|
+
/** unique message tag prefix for MD clients */
|
|
338
348
|
const generateMdTagPrefix = () => {
|
|
339
349
|
const bytes = (0, crypto_1.randomBytes)(4)
|
|
340
350
|
return `${bytes.readUInt16BE()}.${bytes.readUInt16BE(2)}-`
|
|
@@ -343,11 +353,13 @@ exports.generateMdTagPrefix = generateMdTagPrefix
|
|
|
343
353
|
const STATUS_MAP = {
|
|
344
354
|
sender: index_js_1.proto.WebMessageInfo.Status.SERVER_ACK,
|
|
345
355
|
played: index_js_1.proto.WebMessageInfo.Status.PLAYED,
|
|
346
|
-
'played-self': index_js_1.proto.WebMessageInfo.Status.PLAYED,
|
|
347
356
|
read: index_js_1.proto.WebMessageInfo.Status.READ,
|
|
348
357
|
'read-self': index_js_1.proto.WebMessageInfo.Status.READ
|
|
349
358
|
}
|
|
350
|
-
|
|
359
|
+
/**
|
|
360
|
+
* Given a type of receipt, returns what the new status of the message should be
|
|
361
|
+
* @param type type from receipt
|
|
362
|
+
*/
|
|
351
363
|
const getStatusFromReceiptType = type => {
|
|
352
364
|
const status = STATUS_MAP[type]
|
|
353
365
|
if (typeof type === 'undefined') {
|
|
@@ -359,7 +371,10 @@ exports.getStatusFromReceiptType = getStatusFromReceiptType
|
|
|
359
371
|
const CODE_MAP = {
|
|
360
372
|
conflict: Types_1.DisconnectReason.connectionReplaced
|
|
361
373
|
}
|
|
362
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Stream errors generally provide a reason, map that to a baileys DisconnectReason
|
|
376
|
+
* @param reason the string reason given, eg. "conflict"
|
|
377
|
+
*/
|
|
363
378
|
const getErrorCodeFromStreamError = node => {
|
|
364
379
|
const [reasonNode] = (0, WABinary_1.getAllBinaryNodeChildren)(node)
|
|
365
380
|
let reason = reasonNode?.tag || 'unknown'
|
|
@@ -381,31 +396,11 @@ const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
|
381
396
|
status = 'offer'
|
|
382
397
|
break
|
|
383
398
|
case 'terminate':
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
break
|
|
390
|
-
case 'RejectDoNotDisturb':
|
|
391
|
-
status = 'reject_do_not_disturb'
|
|
392
|
-
break
|
|
393
|
-
case 'MicPermissionDenied':
|
|
394
|
-
status = 'mic_permission_denied'
|
|
395
|
-
break
|
|
396
|
-
case 'CameraPermissionDenied':
|
|
397
|
-
status = 'camera_permission_denied'
|
|
398
|
-
break
|
|
399
|
-
case 'RemoteBusy':
|
|
400
|
-
status = 'remote_busy'
|
|
401
|
-
break
|
|
402
|
-
case 'RemoteOffline':
|
|
403
|
-
status = 'remote_offline'
|
|
404
|
-
break
|
|
405
|
-
default:
|
|
406
|
-
|
|
407
|
-
status = 'terminate'
|
|
408
|
-
break
|
|
399
|
+
if (attrs.reason === 'timeout') {
|
|
400
|
+
status = 'timeout'
|
|
401
|
+
} else {
|
|
402
|
+
//fired when accepted/rejected/timeout/caller hangs up
|
|
403
|
+
status = 'terminate'
|
|
409
404
|
}
|
|
410
405
|
break
|
|
411
406
|
case 'reject':
|
|
@@ -414,76 +409,6 @@ const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
|
414
409
|
case 'accept':
|
|
415
410
|
status = 'accept'
|
|
416
411
|
break
|
|
417
|
-
case 'preaccept':
|
|
418
|
-
status = 'preaccept'
|
|
419
|
-
break
|
|
420
|
-
case 'accept_ack':
|
|
421
|
-
status = 'accept_ack'
|
|
422
|
-
break
|
|
423
|
-
case 'enc-rekey':
|
|
424
|
-
case 'enc_rekey':
|
|
425
|
-
status = 'enc_rekey'
|
|
426
|
-
break
|
|
427
|
-
case 'peer_state':
|
|
428
|
-
status = 'peer_state'
|
|
429
|
-
break
|
|
430
|
-
case 'group_info':
|
|
431
|
-
status = 'group_info'
|
|
432
|
-
break
|
|
433
|
-
case 'video_state':
|
|
434
|
-
status = 'video_state'
|
|
435
|
-
break
|
|
436
|
-
case 'video_state_ack':
|
|
437
|
-
status = 'video_state_ack'
|
|
438
|
-
break
|
|
439
|
-
case 'flow_control':
|
|
440
|
-
status = 'flow_control'
|
|
441
|
-
break
|
|
442
|
-
case 'relaylatency':
|
|
443
|
-
status = 'relaylatency'
|
|
444
|
-
break
|
|
445
|
-
case 'mute_v2':
|
|
446
|
-
status = 'mute'
|
|
447
|
-
break
|
|
448
|
-
case 'waiting_room_request':
|
|
449
|
-
status = 'waiting_room_request'
|
|
450
|
-
break
|
|
451
|
-
case 'signal':
|
|
452
|
-
|
|
453
|
-
switch (attrs.type) {
|
|
454
|
-
case '13':
|
|
455
|
-
status = 'preaccept'
|
|
456
|
-
break
|
|
457
|
-
case '15':
|
|
458
|
-
status = 'video_state'
|
|
459
|
-
break
|
|
460
|
-
case '17':
|
|
461
|
-
status = 'group_info'
|
|
462
|
-
break
|
|
463
|
-
case '20':
|
|
464
|
-
status = 'video_state_ack'
|
|
465
|
-
break
|
|
466
|
-
case '21':
|
|
467
|
-
status = 'flow_control'
|
|
468
|
-
break
|
|
469
|
-
case '23':
|
|
470
|
-
status = 'accept_ack'
|
|
471
|
-
break
|
|
472
|
-
case '1002':
|
|
473
|
-
case '1007':
|
|
474
|
-
status = 'peer_state'
|
|
475
|
-
break
|
|
476
|
-
case '1008':
|
|
477
|
-
status = 'enc_rekey'
|
|
478
|
-
break
|
|
479
|
-
case '12':
|
|
480
|
-
status = 'mute'
|
|
481
|
-
break
|
|
482
|
-
default:
|
|
483
|
-
status = 'ringing'
|
|
484
|
-
break
|
|
485
|
-
}
|
|
486
|
-
break
|
|
487
412
|
default:
|
|
488
413
|
status = 'ringing'
|
|
489
414
|
break
|
|
@@ -500,22 +425,25 @@ const getCodeFromWSError = error => {
|
|
|
500
425
|
statusCode = code
|
|
501
426
|
}
|
|
502
427
|
} else if (
|
|
503
|
-
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
504
429
|
error?.code?.startsWith('E') ||
|
|
505
430
|
error?.message?.includes('timed out')
|
|
506
431
|
) {
|
|
507
|
-
|
|
432
|
+
// handle ETIMEOUT, ENOTFOUND etc
|
|
508
433
|
statusCode = 408
|
|
509
434
|
}
|
|
510
435
|
return statusCode
|
|
511
436
|
}
|
|
512
437
|
exports.getCodeFromWSError = getCodeFromWSError
|
|
513
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Is the given platform WA business
|
|
440
|
+
* @param platform AuthenticationCreds.platform
|
|
441
|
+
*/
|
|
514
442
|
const isWABusinessPlatform = platform => {
|
|
515
443
|
return platform === 'smbi' || platform === 'smba'
|
|
516
444
|
}
|
|
517
445
|
exports.isWABusinessPlatform = isWABusinessPlatform
|
|
518
|
-
|
|
446
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
519
447
|
function trimUndefined(obj) {
|
|
520
448
|
for (const key in obj) {
|
|
521
449
|
if (typeof obj[key] === 'undefined') {
|
|
@@ -546,40 +474,51 @@ function encodeNewsletterMessage(message) {
|
|
|
546
474
|
return index_js_1.proto.Message.encode(message).finish()
|
|
547
475
|
}
|
|
548
476
|
|
|
549
|
-
|
|
477
|
+
/**
|
|
478
|
+
* Add human-like jitter to a base delay
|
|
479
|
+
* @param baseMs base delay in milliseconds
|
|
480
|
+
* @param varianceFraction fraction of baseMs to use as variance (0–1), default 0.3
|
|
481
|
+
*/
|
|
550
482
|
const jitterDelay = (baseMs, varianceFraction = 0.3) => {
|
|
551
483
|
const variance = baseMs * varianceFraction
|
|
552
484
|
return baseMs + (Math.random() * 2 - 1) * variance
|
|
553
485
|
}
|
|
554
486
|
exports.jitterDelay = jitterDelay
|
|
555
487
|
|
|
556
|
-
|
|
488
|
+
/**
|
|
489
|
+
* Compute exponential backoff delay for a given attempt number (0-based)
|
|
490
|
+
* @param attempt attempt index (0 = first retry)
|
|
491
|
+
* @param baseMs starting delay in ms (default 500)
|
|
492
|
+
* @param maxMs maximum delay cap in ms (default 30000)
|
|
493
|
+
*/
|
|
557
494
|
const exponentialBackoff = (attempt, baseMs = 500, maxMs = 30000) => {
|
|
558
495
|
return Math.min(baseMs * Math.pow(2, attempt), maxMs)
|
|
559
496
|
}
|
|
560
497
|
exports.exponentialBackoff = exponentialBackoff
|
|
561
498
|
|
|
562
|
-
|
|
499
|
+
/** Convert buffer/Uint8Array to hex string */
|
|
563
500
|
const bytesToHex = buf => Buffer.from(buf).toString('hex')
|
|
564
501
|
exports.bytesToHex = bytesToHex
|
|
565
502
|
|
|
566
|
-
|
|
503
|
+
/** Convert hex string to Buffer */
|
|
567
504
|
const hexToBytes = hex => Buffer.from(hex, 'hex')
|
|
568
505
|
exports.hexToBytes = hexToBytes
|
|
569
506
|
|
|
570
|
-
|
|
507
|
+
/** Convert buffer to base64url string */
|
|
571
508
|
const bytesToBase64Url = buf => Buffer.from(buf).toString('base64url')
|
|
572
509
|
exports.bytesToBase64Url = bytesToBase64Url
|
|
573
510
|
|
|
574
|
-
|
|
511
|
+
/** Compute SHA-256 hex digest of data */
|
|
575
512
|
const sha256Hex = data => (0, crypto_1.createHash)('sha256').update(data).digest('hex')
|
|
576
513
|
exports.sha256Hex = sha256Hex
|
|
577
514
|
|
|
578
|
-
|
|
515
|
+
/** Compute HMAC-SHA-256 of data with key, returns Buffer */
|
|
579
516
|
const hmacSha256 = (data, key) => (0, crypto_1.createHmac)('sha256', key).update(data).digest()
|
|
580
517
|
exports.hmacSha256 = hmacSha256
|
|
581
518
|
|
|
582
|
-
|
|
519
|
+
/**
|
|
520
|
+
* Normalize and deduplicate an array of JIDs
|
|
521
|
+
*/
|
|
583
522
|
const normalizeJidBatch = (jids, normalizer) => {
|
|
584
523
|
const seen = new Set()
|
|
585
524
|
const result = []
|
|
@@ -595,11 +534,18 @@ const normalizeJidBatch = (jids, normalizer) => {
|
|
|
595
534
|
}
|
|
596
535
|
exports.normalizeJidBatch = normalizeJidBatch
|
|
597
536
|
|
|
598
|
-
|
|
537
|
+
/**
|
|
538
|
+
* Sleep for a given number of milliseconds, returns a cancellable promise
|
|
539
|
+
*/
|
|
599
540
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
600
541
|
exports.sleep = sleep
|
|
601
542
|
|
|
602
|
-
|
|
543
|
+
/**
|
|
544
|
+
* Run an async function with retry using exponential backoff
|
|
545
|
+
* @param fn async function to retry
|
|
546
|
+
* @param maxAttempts maximum number of attempts
|
|
547
|
+
* @param baseDelayMs base delay between retries
|
|
548
|
+
*/
|
|
603
549
|
const withRetry = async (fn, maxAttempts = 3, baseDelayMs = 500) => {
|
|
604
550
|
let lastErr
|
|
605
551
|
for (let i = 0; i < maxAttempts; i++) {
|
|
@@ -4,7 +4,13 @@ exports.processGroupHistory = exports.decodeGroupHistory = void 0
|
|
|
4
4
|
const zlib_1 = require('zlib')
|
|
5
5
|
const index_js_1 = require('../../WAProto/index.js')
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Decode a GroupHistory protobuf blob (community/group history container).
|
|
9
|
+
* @param buffer raw bytes, optionally zlib-compressed
|
|
10
|
+
* @param options.inflate try zlib.inflateSync first, falling back to raw bytes (default true)
|
|
11
|
+
* @param options.withMessageBytes decode the GroupHistoryWithMessageBytes variant,
|
|
12
|
+
* expanding each entry's messageBytes into a WebMessageInfo (default false)
|
|
13
|
+
*/
|
|
8
14
|
const decodeGroupHistory = (buffer, options = {}) => {
|
|
9
15
|
const { inflate = true, withMessageBytes = false } = options
|
|
10
16
|
if (!Buffer.isBuffer(buffer) && !(buffer instanceof Uint8Array)) {
|
|
@@ -15,7 +21,7 @@ const decodeGroupHistory = (buffer, options = {}) => {
|
|
|
15
21
|
try {
|
|
16
22
|
data = zlib_1.inflateSync(data)
|
|
17
23
|
} catch {
|
|
18
|
-
|
|
24
|
+
// not zlib-compressed — use raw bytes
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
if (withMessageBytes) {
|
|
@@ -38,7 +44,10 @@ const decodeGroupHistory = (buffer, options = {}) => {
|
|
|
38
44
|
}
|
|
39
45
|
exports.decodeGroupHistory = decodeGroupHistory
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Normalize a decoded GroupHistory into stable message buckets.
|
|
49
|
+
* @param groupHistory a decoded proto.GroupHistory (or the withMessageBytes shape)
|
|
50
|
+
*/
|
|
42
51
|
const processGroupHistory = groupHistory => {
|
|
43
52
|
const gh = groupHistory || {}
|
|
44
53
|
return {
|
package/lib/Utils/history.js
CHANGED
|
@@ -17,8 +17,8 @@ const inflatePromise = (0, util_1.promisify)(zlib_1.inflate)
|
|
|
17
17
|
const extractPnFromMessages = messages => {
|
|
18
18
|
for (const msgItem of messages) {
|
|
19
19
|
const message = msgItem.message
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
// Only extract from outgoing messages (fromMe: true) in 1:1 chats
|
|
21
|
+
// because userReceipt.userJid is the recipient's JID
|
|
22
22
|
if (!message?.key?.fromMe || !message.userReceipt?.length) {
|
|
23
23
|
continue
|
|
24
24
|
}
|
|
@@ -36,7 +36,7 @@ const downloadHistory = async (msg, options) => {
|
|
|
36
36
|
bufferArray.push(chunk)
|
|
37
37
|
}
|
|
38
38
|
let buffer = Buffer.concat(bufferArray)
|
|
39
|
-
|
|
39
|
+
// decompress buffer
|
|
40
40
|
buffer = await inflatePromise(buffer)
|
|
41
41
|
const syncData = index_js_1.proto.HistorySync.decode(buffer)
|
|
42
42
|
return syncData
|
|
@@ -53,13 +53,13 @@ const processHistoryMessage = (item, logger) => {
|
|
|
53
53
|
let globalSettings = undefined
|
|
54
54
|
let statusV3Messages = undefined
|
|
55
55
|
logger?.trace({ progress: item.progress }, 'processing history of type ' + item.syncType?.toString())
|
|
56
|
-
|
|
56
|
+
// Extract LID-PN mappings for all sync types
|
|
57
57
|
for (const m of item.phoneNumberToLidMappings || []) {
|
|
58
58
|
if (m.lidJid && m.pnJid) {
|
|
59
59
|
lidPnMappings.push({ lid: m.lidJid, pn: m.pnJid })
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
// Extract inline contacts (LID-PN mappings with names)
|
|
63
63
|
for (const c of item.inlineContacts || []) {
|
|
64
64
|
if (c.lidJid && c.pnJid) {
|
|
65
65
|
lidPnMappings.push({ lid: c.lidJid, pn: c.pnJid })
|
|
@@ -77,7 +77,7 @@ const processHistoryMessage = (item, logger) => {
|
|
|
77
77
|
})
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
// Extract call log records with richer metadata
|
|
81
81
|
for (const rec of item.callLogRecords || []) {
|
|
82
82
|
callLogRecords.push({
|
|
83
83
|
...rec,
|
|
@@ -89,7 +89,7 @@ const processHistoryMessage = (item, logger) => {
|
|
|
89
89
|
timestamp: rec.startTime ? (0, generics_1.toNumber)(rec.startTime) : undefined
|
|
90
90
|
})
|
|
91
91
|
}
|
|
92
|
-
|
|
92
|
+
// Extract recent stickers with metadata, deduped by key
|
|
93
93
|
const stickerKeys = new Set()
|
|
94
94
|
for (const s of item.recentStickers || []) {
|
|
95
95
|
const stickerKey = s.fileSha256 ? Buffer.from(s.fileSha256).toString('base64') : undefined
|
|
@@ -103,12 +103,12 @@ const processHistoryMessage = (item, logger) => {
|
|
|
103
103
|
case index_js_1.proto.HistorySync.HistorySyncType.RECENT:
|
|
104
104
|
case index_js_1.proto.HistorySync.HistorySyncType.FULL:
|
|
105
105
|
case index_js_1.proto.HistorySync.HistorySyncType.ON_DEMAND:
|
|
106
|
-
|
|
106
|
+
// Extract global client settings with full field parsing
|
|
107
107
|
if (item.globalSettings) {
|
|
108
108
|
const gs = item.globalSettings
|
|
109
109
|
globalSettings = {
|
|
110
110
|
...gs,
|
|
111
|
-
|
|
111
|
+
// Parsed convenience fields
|
|
112
112
|
autoDownloadSettings: {
|
|
113
113
|
photos: gs.autoDownloadPhotos,
|
|
114
114
|
audio: gs.autoDownloadAudio,
|
|
@@ -139,7 +139,7 @@ const processHistoryMessage = (item, logger) => {
|
|
|
139
139
|
: undefined
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
// Extract status V3 messages with expiry tracking
|
|
143
143
|
if (item.statusV3Messages?.length) {
|
|
144
144
|
const now = Math.floor(Date.now() / 1000)
|
|
145
145
|
statusV3Messages = item.statusV3Messages.filter(s => {
|
|
@@ -165,7 +165,7 @@ const processHistoryMessage = (item, logger) => {
|
|
|
165
165
|
} else if (isPn && chat.lidJid) {
|
|
166
166
|
lidPnMappings.push({ lid: chat.lidJid, pn: chatId })
|
|
167
167
|
} else if (isLid && !chat.pnJid) {
|
|
168
|
-
|
|
168
|
+
// Fallback: extract PN from userReceipt in messages when pnJid is missing
|
|
169
169
|
const pnFromReceipt = extractPnFromMessages(chat.messages || [])
|
|
170
170
|
if (pnFromReceipt) {
|
|
171
171
|
lidPnMappings.push({ lid: chatId, pn: pnFromReceipt })
|
|
@@ -177,7 +177,7 @@ const processHistoryMessage = (item, logger) => {
|
|
|
177
177
|
const message = item.message
|
|
178
178
|
messages.push(message)
|
|
179
179
|
if (!chat.messages?.length) {
|
|
180
|
-
|
|
180
|
+
// keep only the most recent message in the chat array
|
|
181
181
|
chat.messages = [{ message }]
|
|
182
182
|
}
|
|
183
183
|
if (!message.key.fromMe && !chat.lastMessageRecvTimestamp) {
|
|
@@ -194,13 +194,13 @@ const processHistoryMessage = (item, logger) => {
|
|
|
194
194
|
})
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
-
|
|
197
|
+
// Extract past participants metadata
|
|
198
198
|
if (chat.id && (0, WABinary_1.isJidGroup)(chat.id)) {
|
|
199
|
-
|
|
199
|
+
// Chat-level past participants info is available via PastParticipants messages
|
|
200
200
|
}
|
|
201
201
|
chats.push({ ...chat })
|
|
202
202
|
}
|
|
203
|
-
|
|
203
|
+
// Extract past participants from dedicated field
|
|
204
204
|
for (const pp of item.pastParticipants || []) {
|
|
205
205
|
pastParticipants.push(pp)
|
|
206
206
|
}
|
package/lib/Utils/index.js
CHANGED
|
@@ -28,7 +28,6 @@ var __exportStar =
|
|
|
28
28
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
29
29
|
__exportStar(require('./generics'), exports)
|
|
30
30
|
__exportStar(require('./decode-wa-message'), exports)
|
|
31
|
-
__exportStar(require('./message-inspect'), exports)
|
|
32
31
|
__exportStar(require('./messages'), exports)
|
|
33
32
|
__exportStar(require('./messages-media'), exports)
|
|
34
33
|
__exportStar(require('./validate-connection'), exports)
|
|
@@ -51,3 +50,8 @@ __exportStar(require('./message-composer'), exports)
|
|
|
51
50
|
__exportStar(require('./group-history'), exports)
|
|
52
51
|
__exportStar(require('./consumer-application'), exports)
|
|
53
52
|
__exportStar(require('./voip-rekey'), exports)
|
|
53
|
+
__exportStar(require('./companion-reg-client-utils'), exports)
|
|
54
|
+
__exportStar(require('./view-once-cache'), exports)
|
|
55
|
+
__exportStar(require('./sticker'), exports)
|
|
56
|
+
__exportStar(require('./command-loader'), exports)
|
|
57
|
+
__exportStar(require('./session-pool'), exports)
|
|
@@ -55,13 +55,18 @@ exports.getUrlInfo = void 0
|
|
|
55
55
|
const messages_1 = require('./messages')
|
|
56
56
|
const messages_media_1 = require('./messages-media')
|
|
57
57
|
const THUMBNAIL_WIDTH_PX = 192
|
|
58
|
-
|
|
58
|
+
/** Fetches an image and generates a thumbnail for it */
|
|
59
59
|
const getCompressedJpegThumbnail = async (url, { thumbnailWidth, fetchOpts }) => {
|
|
60
60
|
const stream = await (0, messages_media_1.getHttpStream)(url, fetchOpts)
|
|
61
61
|
const result = await (0, messages_media_1.extractImageThumb)(stream, thumbnailWidth)
|
|
62
62
|
return result
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Given a piece of text, checks for any URL present, generates link preview for the same and returns it
|
|
66
|
+
* Return undefined if the fetch failed or no URL was found
|
|
67
|
+
* @param text first matched URL in text
|
|
68
|
+
* @returns the URL info required to generate link preview
|
|
69
|
+
*/
|
|
65
70
|
const getUrlInfo = async (
|
|
66
71
|
text,
|
|
67
72
|
opts = {
|
|
@@ -70,7 +75,7 @@ const getUrlInfo = async (
|
|
|
70
75
|
}
|
|
71
76
|
) => {
|
|
72
77
|
try {
|
|
73
|
-
|
|
78
|
+
// retries
|
|
74
79
|
const retries = 0
|
|
75
80
|
const maxRetry = 5
|
|
76
81
|
const { getLinkPreview } = await Promise.resolve().then(() => __importStar(require('link-preview-js')))
|
package/lib/Utils/lt-hash.js
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
3
|
exports.LT_HASH_ANTI_TAMPERING = void 0
|
|
4
|
-
const
|
|
4
|
+
const crypto_1 = require('./crypto')
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* LT Hash ("lthash16") is a summation-based homomorphic hash algorithm that
|
|
8
|
+
* maintains the integrity of a piece of data over a series of mutations. You
|
|
9
|
+
* can add/remove mutations and it'll return a hash equal to if the same
|
|
10
|
+
* series of mutations was made sequentially.
|
|
11
|
+
*
|
|
12
|
+
* WhatsApp expands each element to 128 bytes via HKDF (info = "WhatsApp Patch
|
|
13
|
+
* Integrity"), treats that as 64 little-endian uint16 lanes, and combines
|
|
14
|
+
* elements by adding/subtracting lane-wise mod 2^16 -- this is a pure HKDF +
|
|
15
|
+
* integer-arithmetic algorithm, no elliptic-curve math involved. Pure-JS,
|
|
16
|
+
* built on the already-validated `hkdf` implementation.
|
|
17
|
+
*/
|
|
18
|
+
const LTHASH_INFO = 'WhatsApp Patch Integrity'
|
|
19
|
+
const LTHASH_SIZE = 128 // bytes = 64 uint16 lanes
|
|
20
|
+
|
|
21
|
+
const expand = element => (0, crypto_1.hkdf)(element, LTHASH_SIZE, { info: LTHASH_INFO })
|
|
22
|
+
|
|
23
|
+
const applyDelta = (result, element, sign) => {
|
|
24
|
+
const expanded = expand(element)
|
|
25
|
+
for (let i = 0; i < LTHASH_SIZE; i += 2) {
|
|
26
|
+
const lane = result.readUInt16LE(i)
|
|
27
|
+
const delta = expanded.readUInt16LE(i)
|
|
28
|
+
result.writeUInt16LE((lane + sign * delta) & 0xffff, i)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
exports.LT_HASH_ANTI_TAMPERING = {
|
|
33
|
+
subtractThenAdd: (base, subtract = [], add = []) => {
|
|
34
|
+
const result = Buffer.from(base)
|
|
35
|
+
for (const element of subtract) applyDelta(result, element, -1)
|
|
36
|
+
for (const element of add) applyDelta(result, element, 1)
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
39
|
+
}
|