@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
|
@@ -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
|
|
@@ -196,24 +198,36 @@ async function promiseTimeout(ms, promise) {
|
|
|
196
198
|
}
|
|
197
199
|
// inspired from whatsmeow code
|
|
198
200
|
// https://github.com/tulir/whatsmeow/blob/64bc969fbe78d31ae0dd443b8d4c80a5d026d07a/send.go#L42
|
|
199
|
-
const generateMessageIDV2 = userId => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
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
|
|
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
|
|
215
229
|
// generate a random ID to attach to a message
|
|
216
|
-
const generateMessageID = () => '
|
|
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) => {
|
|
@@ -266,7 +280,7 @@ const fetchLatestBaileysVersion = async (options = {}) => {
|
|
|
266
280
|
const versionLine = lines[6] // Line 7 (0-indexed)
|
|
267
281
|
const versionMatch = versionLine.match(/const version = \[(\d+),\s*(\d+),\s*(\d+)\]/)
|
|
268
282
|
if (versionMatch) {
|
|
269
|
-
const version = [parseInt(versionMatch[1]
|
|
283
|
+
const version = [parseInt(versionMatch[1]), parseInt(versionMatch[2]), parseInt(versionMatch[3])]
|
|
270
284
|
return {
|
|
271
285
|
version,
|
|
272
286
|
isLatest: true
|
|
@@ -339,7 +353,6 @@ exports.generateMdTagPrefix = generateMdTagPrefix
|
|
|
339
353
|
const STATUS_MAP = {
|
|
340
354
|
sender: index_js_1.proto.WebMessageInfo.Status.SERVER_ACK,
|
|
341
355
|
played: index_js_1.proto.WebMessageInfo.Status.PLAYED,
|
|
342
|
-
'played-self': index_js_1.proto.WebMessageInfo.Status.PLAYED,
|
|
343
356
|
read: index_js_1.proto.WebMessageInfo.Status.READ,
|
|
344
357
|
'read-self': index_js_1.proto.WebMessageInfo.Status.READ
|
|
345
358
|
}
|
|
@@ -383,31 +396,11 @@ const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
|
383
396
|
status = 'offer'
|
|
384
397
|
break
|
|
385
398
|
case 'terminate':
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
break
|
|
392
|
-
case 'RejectDoNotDisturb':
|
|
393
|
-
status = 'reject_do_not_disturb'
|
|
394
|
-
break
|
|
395
|
-
case 'MicPermissionDenied':
|
|
396
|
-
status = 'mic_permission_denied'
|
|
397
|
-
break
|
|
398
|
-
case 'CameraPermissionDenied':
|
|
399
|
-
status = 'camera_permission_denied'
|
|
400
|
-
break
|
|
401
|
-
case 'RemoteBusy':
|
|
402
|
-
status = 'remote_busy'
|
|
403
|
-
break
|
|
404
|
-
case 'RemoteOffline':
|
|
405
|
-
status = 'remote_offline'
|
|
406
|
-
break
|
|
407
|
-
default:
|
|
408
|
-
// fired when accepted/rejected/caller hangs up
|
|
409
|
-
status = 'terminate'
|
|
410
|
-
break
|
|
399
|
+
if (attrs.reason === 'timeout') {
|
|
400
|
+
status = 'timeout'
|
|
401
|
+
} else {
|
|
402
|
+
//fired when accepted/rejected/timeout/caller hangs up
|
|
403
|
+
status = 'terminate'
|
|
411
404
|
}
|
|
412
405
|
break
|
|
413
406
|
case 'reject':
|
|
@@ -416,76 +409,6 @@ const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
|
416
409
|
case 'accept':
|
|
417
410
|
status = 'accept'
|
|
418
411
|
break
|
|
419
|
-
case 'preaccept':
|
|
420
|
-
status = 'preaccept'
|
|
421
|
-
break
|
|
422
|
-
case 'accept_ack':
|
|
423
|
-
status = 'accept_ack'
|
|
424
|
-
break
|
|
425
|
-
case 'enc-rekey':
|
|
426
|
-
case 'enc_rekey':
|
|
427
|
-
status = 'enc_rekey'
|
|
428
|
-
break
|
|
429
|
-
case 'peer_state':
|
|
430
|
-
status = 'peer_state'
|
|
431
|
-
break
|
|
432
|
-
case 'group_info':
|
|
433
|
-
status = 'group_info'
|
|
434
|
-
break
|
|
435
|
-
case 'video_state':
|
|
436
|
-
status = 'video_state'
|
|
437
|
-
break
|
|
438
|
-
case 'video_state_ack':
|
|
439
|
-
status = 'video_state_ack'
|
|
440
|
-
break
|
|
441
|
-
case 'flow_control':
|
|
442
|
-
status = 'flow_control'
|
|
443
|
-
break
|
|
444
|
-
case 'relaylatency':
|
|
445
|
-
status = 'relaylatency'
|
|
446
|
-
break
|
|
447
|
-
case 'mute_v2':
|
|
448
|
-
status = 'mute'
|
|
449
|
-
break
|
|
450
|
-
case 'waiting_room_request':
|
|
451
|
-
status = 'waiting_room_request'
|
|
452
|
-
break
|
|
453
|
-
case 'signal':
|
|
454
|
-
// Numeric type mapping for <signal type="N"/> child nodes
|
|
455
|
-
switch (attrs.type) {
|
|
456
|
-
case '13':
|
|
457
|
-
status = 'preaccept'
|
|
458
|
-
break
|
|
459
|
-
case '15':
|
|
460
|
-
status = 'video_state'
|
|
461
|
-
break
|
|
462
|
-
case '17':
|
|
463
|
-
status = 'group_info'
|
|
464
|
-
break
|
|
465
|
-
case '20':
|
|
466
|
-
status = 'video_state_ack'
|
|
467
|
-
break
|
|
468
|
-
case '21':
|
|
469
|
-
status = 'flow_control'
|
|
470
|
-
break
|
|
471
|
-
case '23':
|
|
472
|
-
status = 'accept_ack'
|
|
473
|
-
break
|
|
474
|
-
case '1002':
|
|
475
|
-
case '1007':
|
|
476
|
-
status = 'peer_state'
|
|
477
|
-
break
|
|
478
|
-
case '1008':
|
|
479
|
-
status = 'enc_rekey'
|
|
480
|
-
break
|
|
481
|
-
case '12':
|
|
482
|
-
status = 'mute'
|
|
483
|
-
break
|
|
484
|
-
default:
|
|
485
|
-
status = 'ringing'
|
|
486
|
-
break
|
|
487
|
-
}
|
|
488
|
-
break
|
|
489
412
|
default:
|
|
490
413
|
status = 'ringing'
|
|
491
414
|
break
|
|
@@ -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)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.LT_HASH_ANTI_TAMPERING = void 0
|
|
4
|
+
const crypto_1 = require('./crypto')
|
|
5
|
+
|
|
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
|
+
}
|
|
@@ -55,6 +55,11 @@ class MessageRetryManager {
|
|
|
55
55
|
ttlAutopurge: true,
|
|
56
56
|
updateAgeOnGet: true
|
|
57
57
|
}) // 15 minutes TTL
|
|
58
|
+
this.baseKeys = new lru_cache_1.LRUCache({
|
|
59
|
+
max: 1024,
|
|
60
|
+
ttl: 15 * 60 * 1000,
|
|
61
|
+
ttlAutopurge: true
|
|
62
|
+
})
|
|
58
63
|
this.pendingPhoneRequests = {}
|
|
59
64
|
this.maxMsgRetryCount = 5
|
|
60
65
|
this.statistics = {
|
|
@@ -216,6 +221,40 @@ class MessageRetryManager {
|
|
|
216
221
|
this.logger.debug(`Cancelled pending phone request for message ${messageId}`)
|
|
217
222
|
}
|
|
218
223
|
}
|
|
224
|
+
clear() {
|
|
225
|
+
this.recentMessagesMap.clear()
|
|
226
|
+
this.messageKeyIndex.clear()
|
|
227
|
+
this.sessionRecreateHistory.clear()
|
|
228
|
+
this.retryCounters.clear()
|
|
229
|
+
this.baseKeys.clear()
|
|
230
|
+
for (const messageId of Object.keys(this.pendingPhoneRequests)) {
|
|
231
|
+
this.cancelPendingPhoneRequest(messageId)
|
|
232
|
+
}
|
|
233
|
+
this.statistics = {
|
|
234
|
+
totalRetries: 0,
|
|
235
|
+
successfulRetries: 0,
|
|
236
|
+
failedRetries: 0,
|
|
237
|
+
mediaRetries: 0,
|
|
238
|
+
sessionRecreations: 0,
|
|
239
|
+
phoneRequests: 0
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
saveBaseKey(addr, msgId, baseKey) {
|
|
243
|
+
this.baseKeys.set(`${addr}:${msgId}`, baseKey)
|
|
244
|
+
}
|
|
245
|
+
hasSameBaseKey(addr, msgId, baseKey) {
|
|
246
|
+
const stored = this.baseKeys.get(`${addr}:${msgId}`)
|
|
247
|
+
if (!stored || stored.length !== baseKey.length) {
|
|
248
|
+
return false
|
|
249
|
+
}
|
|
250
|
+
for (let i = 0; i < stored.length; i++) {
|
|
251
|
+
if (stored[i] !== baseKey[i]) return false
|
|
252
|
+
}
|
|
253
|
+
return true
|
|
254
|
+
}
|
|
255
|
+
deleteBaseKey(addr, msgId) {
|
|
256
|
+
this.baseKeys.delete(`${addr}:${msgId}`)
|
|
257
|
+
}
|
|
219
258
|
keyToString(key) {
|
|
220
259
|
return `${key.to}${MESSAGE_KEY_SEPARATOR}${key.id}`
|
|
221
260
|
}
|
|
@@ -60,7 +60,6 @@ exports.getStatusCodeForMediaRetry =
|
|
|
60
60
|
exports.downloadEncryptedContent =
|
|
61
61
|
exports.downloadContentFromMessage =
|
|
62
62
|
exports.getUrlFromDirectPath =
|
|
63
|
-
exports.getMediaProp =
|
|
64
63
|
exports.encryptedStream =
|
|
65
64
|
exports.getHttpStream =
|
|
66
65
|
exports.getStream =
|
|
@@ -493,85 +492,16 @@ const toSmallestChunkSize = num => {
|
|
|
493
492
|
}
|
|
494
493
|
const getUrlFromDirectPath = directPath => `https://${DEF_HOST}${directPath}`
|
|
495
494
|
exports.getUrlFromDirectPath = getUrlFromDirectPath
|
|
496
|
-
/**
|
|
497
|
-
* Returns the boolean value of a named AB prop from the mediaAbProps dict stored in creds.
|
|
498
|
-
* Returns false when the prop is absent or the dict is not available.
|
|
499
|
-
*/
|
|
500
|
-
const getMediaProp = (mediaAbProps, propName) => {
|
|
501
|
-
if (!mediaAbProps || typeof mediaAbProps !== 'object') return false
|
|
502
|
-
return !!mediaAbProps[propName]
|
|
503
|
-
}
|
|
504
|
-
exports.getMediaProp = getMediaProp
|
|
505
495
|
const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
506
|
-
const
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
// When both are the same host the first attempt is enough.
|
|
510
|
-
let downloadUrl
|
|
511
|
-
if (directUrl) {
|
|
512
|
-
downloadUrl = directUrl
|
|
513
|
-
} else if (fallbackUrl) {
|
|
514
|
-
downloadUrl = fallbackUrl
|
|
515
|
-
} else {
|
|
496
|
+
const isValidMediaUrl = url?.startsWith('https://mmg.whatsapp.net/')
|
|
497
|
+
const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath)
|
|
498
|
+
if (!downloadUrl) {
|
|
516
499
|
throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 })
|
|
517
500
|
}
|
|
518
501
|
const keys = await getMediaKeys(mediaKey, type)
|
|
519
|
-
|
|
520
|
-
const mediaAbProps = opts.mediaAbProps
|
|
521
|
-
const pjpegHeaders = {}
|
|
522
|
-
if (type === 'image' && getMediaProp(mediaAbProps, 'partial_pjpeg_enabled')) {
|
|
523
|
-
// Signal to the CDN that we accept partial / progressive content.
|
|
524
|
-
pjpegHeaders['X-WhatsApp-PJPEG'] = '1'
|
|
525
|
-
}
|
|
526
|
-
if (type === 'image' && getMediaProp(mediaAbProps, 'multi_scan_pjpeg')) {
|
|
527
|
-
pjpegHeaders['X-WhatsApp-Multi-Scan-PJPEG'] = '1'
|
|
528
|
-
}
|
|
529
|
-
const mergedOpts =
|
|
530
|
-
Object.keys(pjpegHeaders).length
|
|
531
|
-
? { ...opts, options: { ...(opts.options || {}), headers: { ...(opts.options?.headers || {}), ...pjpegHeaders } } }
|
|
532
|
-
: opts
|
|
533
|
-
try {
|
|
534
|
-
return await (0, exports.downloadEncryptedContent)(downloadUrl, keys, mergedOpts)
|
|
535
|
-
} catch (err) {
|
|
536
|
-
// Feature F fallback: if directPath URL failed and we have an alternative mediaUrl, retry.
|
|
537
|
-
if (directUrl && fallbackUrl && fallbackUrl !== directUrl) {
|
|
538
|
-
return (0, exports.downloadEncryptedContent)(fallbackUrl, keys, mergedOpts)
|
|
539
|
-
}
|
|
540
|
-
throw err
|
|
541
|
-
}
|
|
502
|
+
return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts)
|
|
542
503
|
}
|
|
543
504
|
exports.downloadContentFromMessage = downloadContentFromMessage
|
|
544
|
-
/**
|
|
545
|
-
* Fetch a specific decrypted byte range of an encrypted media message.
|
|
546
|
-
* Returns a Transform stream (same contract as downloadContentFromMessage).
|
|
547
|
-
* Relies on the Range-request support already present in downloadEncryptedContent.
|
|
548
|
-
*/
|
|
549
|
-
const downloadMediaChunk = (message, type, startByte, endByte, opts = {}) =>
|
|
550
|
-
downloadContentFromMessage(message, type, { ...opts, startByte, endByte })
|
|
551
|
-
exports.downloadMediaChunk = downloadMediaChunk
|
|
552
|
-
/**
|
|
553
|
-
* Async generator that yields successive decrypted chunk streams for large media.
|
|
554
|
-
* Uses HTTP Range requests so the CDN only sends the requested slice each time.
|
|
555
|
-
* Useful for progressive playback: the caller can begin rendering before the full
|
|
556
|
-
* file is downloaded.
|
|
557
|
-
*
|
|
558
|
-
* @param {object} message - The message content object with mediaKey, directPath/url, fileLength
|
|
559
|
-
* @param {string} type - WA media type ('image', 'video', 'audio', 'document', ...)
|
|
560
|
-
* @param {object} opts - Passed through to downloadContentFromMessage; add chunkSize (bytes) to override
|
|
561
|
-
*/
|
|
562
|
-
async function* streamMediaChunks(message, type, opts = {}) {
|
|
563
|
-
const chunkSize = opts.chunkSize || 2 * 1024 * 1024
|
|
564
|
-
const fileLength = message.fileLength
|
|
565
|
-
if (!fileLength || fileLength <= chunkSize) {
|
|
566
|
-
yield downloadContentFromMessage(message, type, opts)
|
|
567
|
-
return
|
|
568
|
-
}
|
|
569
|
-
for (let offset = 0; offset < fileLength; offset += chunkSize) {
|
|
570
|
-
const endByte = Math.min(offset + chunkSize - 1, fileLength - 1)
|
|
571
|
-
yield downloadContentFromMessage(message, type, { ...opts, startByte: offset, endByte })
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
exports.streamMediaChunks = streamMediaChunks
|
|
575
505
|
/**
|
|
576
506
|
* Decrypts and downloads an AES256-CBC encrypted file given the keys.
|
|
577
507
|
* Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
|
|
@@ -725,9 +655,13 @@ const uploadWithNodeHttp = async ({ url, filePath, headers, timeoutMs, agent },
|
|
|
725
655
|
res.on('data', chunk => (body += chunk))
|
|
726
656
|
res.on('end', () => {
|
|
727
657
|
try {
|
|
728
|
-
|
|
658
|
+
const parsed = JSON.parse(body)
|
|
659
|
+
if (parsed && typeof parsed === 'object') {
|
|
660
|
+
Object.defineProperty(parsed, '__statusCode', { value: res.statusCode, enumerable: false })
|
|
661
|
+
}
|
|
662
|
+
resolve(parsed)
|
|
729
663
|
} catch {
|
|
730
|
-
resolve(
|
|
664
|
+
resolve({ __statusCode: res.statusCode })
|
|
731
665
|
}
|
|
732
666
|
})
|
|
733
667
|
}
|
|
@@ -759,9 +693,13 @@ const uploadWithFetch = async ({ url, filePath, headers, timeoutMs, agent }) =>
|
|
|
759
693
|
signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined
|
|
760
694
|
})
|
|
761
695
|
try {
|
|
762
|
-
|
|
696
|
+
const parsed = await response.json()
|
|
697
|
+
if (parsed && typeof parsed === 'object') {
|
|
698
|
+
Object.defineProperty(parsed, '__statusCode', { value: response.status, enumerable: false })
|
|
699
|
+
}
|
|
700
|
+
return parsed
|
|
763
701
|
} catch {
|
|
764
|
-
return
|
|
702
|
+
return { __statusCode: response.status }
|
|
765
703
|
}
|
|
766
704
|
}
|
|
767
705
|
/**
|
|
@@ -813,37 +751,52 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
813
751
|
const auth = encodeURIComponent(uploadInfo.auth)
|
|
814
752
|
const url = `https://${hostname}${Defaults_1.MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
|
|
815
753
|
let result
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
754
|
+
const maxRateLimitRetries = 3
|
|
755
|
+
for (let attempt = 0; attempt <= maxRateLimitRetries; attempt++) {
|
|
756
|
+
try {
|
|
757
|
+
result = await uploadMedia(
|
|
758
|
+
{
|
|
759
|
+
url,
|
|
760
|
+
filePath,
|
|
761
|
+
headers,
|
|
762
|
+
timeoutMs,
|
|
763
|
+
agent: fetchAgent
|
|
764
|
+
},
|
|
765
|
+
logger
|
|
766
|
+
)
|
|
767
|
+
const isRateLimited = result?.__statusCode === 429 || result?.__statusCode === 503
|
|
768
|
+
if (isRateLimited && attempt < maxRateLimitRetries) {
|
|
769
|
+
const backoffMs = Math.min(500 * 2 ** attempt, 8000) + Math.floor(Math.random() * 250)
|
|
770
|
+
logger.warn(
|
|
771
|
+
{ hostname, attempt, backoffMs, statusCode: result.__statusCode },
|
|
772
|
+
'media upload rate-limited, backing off before retry'
|
|
773
|
+
)
|
|
774
|
+
await new Promise(resolve => setTimeout(resolve, backoffMs))
|
|
775
|
+
continue
|
|
776
|
+
}
|
|
777
|
+
if (result?.url || result?.direct_path) {
|
|
778
|
+
urls = {
|
|
779
|
+
mediaUrl: result.url,
|
|
780
|
+
directPath: result.direct_path,
|
|
781
|
+
meta_hmac: result.meta_hmac,
|
|
782
|
+
fbid: result.fbid,
|
|
783
|
+
ts: result.ts
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
uploadInfo = await refreshMediaConn(true)
|
|
787
|
+
throw new Error(`upload failed, reason: ${JSON.stringify(result)}`)
|
|
834
788
|
}
|
|
835
789
|
break
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
|
|
790
|
+
} catch (error) {
|
|
791
|
+
const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname
|
|
792
|
+
logger.warn(
|
|
793
|
+
{ trace: error?.stack, uploadResult: result },
|
|
794
|
+
`Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`
|
|
795
|
+
)
|
|
796
|
+
break
|
|
839
797
|
}
|
|
840
|
-
} catch (error) {
|
|
841
|
-
const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname
|
|
842
|
-
logger.warn(
|
|
843
|
-
{ trace: error?.stack, uploadResult: result },
|
|
844
|
-
`Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`
|
|
845
|
-
)
|
|
846
798
|
}
|
|
799
|
+
if (urls) break
|
|
847
800
|
}
|
|
848
801
|
if (!urls) {
|
|
849
802
|
throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 })
|