@hansaka02/baileys 7.3.4 → 7.3.6
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 +203 -247
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/connection.js +1 -1
- package/lib/Defaults/constants.js +13 -1
- package/lib/Defaults/history.js +3 -1
- package/lib/Signal/Group/sender-chain-key.js +1 -14
- package/lib/Signal/Group/sender-key-distribution-message.js +2 -2
- package/lib/Signal/Group/sender-key-record.js +2 -11
- package/lib/Signal/Group/sender-key-state.js +11 -57
- package/lib/Signal/libsignal.js +200 -116
- package/lib/Signal/lid-mapping.js +121 -68
- package/lib/Socket/Client/websocket.js +9 -2
- package/lib/Socket/business.js +5 -1
- package/lib/Socket/chats.js +180 -89
- package/lib/Socket/community.js +169 -41
- package/lib/Socket/groups.js +25 -21
- package/lib/Socket/messages-recv.js +458 -333
- package/lib/Socket/messages-send.js +517 -572
- package/lib/Socket/mex.js +61 -0
- package/lib/Socket/newsletter.js +159 -252
- package/lib/Socket/socket.js +283 -100
- package/lib/Types/Newsletter.js +32 -25
- package/lib/Utils/auth-utils.js +189 -354
- package/lib/Utils/browser-utils.js +43 -0
- package/lib/Utils/chat-utils.js +166 -41
- package/lib/Utils/decode-wa-message.js +77 -35
- package/lib/Utils/event-buffer.js +80 -24
- package/lib/Utils/generics.js +28 -128
- package/lib/Utils/history.js +10 -8
- package/lib/Utils/index.js +1 -1
- package/lib/Utils/link-preview.js +17 -32
- package/lib/Utils/lt-hash.js +28 -22
- package/lib/Utils/make-mutex.js +26 -28
- package/lib/Utils/message-retry-manager.js +51 -3
- package/lib/Utils/messages-media.js +343 -151
- package/lib/Utils/messages.js +806 -792
- package/lib/Utils/noise-handler.js +33 -2
- package/lib/Utils/pre-key-manager.js +126 -0
- package/lib/Utils/process-message.js +115 -55
- package/lib/Utils/signal.js +45 -18
- package/lib/Utils/validate-connection.js +52 -29
- package/lib/WABinary/constants.js +1268 -1268
- package/lib/WABinary/decode.js +58 -4
- package/lib/WABinary/encode.js +54 -7
- package/lib/WABinary/jid-utils.js +58 -11
- package/lib/WAM/constants.js +19064 -11563
- package/lib/WAM/encode.js +57 -8
- package/lib/WAUSync/USyncQuery.js +35 -19
- package/package.json +9 -8
- package/lib/Socket/usync.js +0 -83
package/lib/Utils/generics.js
CHANGED
|
@@ -12,116 +12,10 @@ const {
|
|
|
12
12
|
getAllBinaryNodeChildren
|
|
13
13
|
} = require("../WABinary")
|
|
14
14
|
const { sha256 } = require("./crypto")
|
|
15
|
-
const { platform } = require("os")
|
|
16
15
|
const { proto } = require("../../WAProto")
|
|
17
16
|
const { version } = require("../Defaults/baileys-version.json")
|
|
18
17
|
const { DisconnectReason } = require("../Types")
|
|
19
18
|
|
|
20
|
-
const COMPANION_PLATFORM_MAP = {
|
|
21
|
-
'Chrome': '49',
|
|
22
|
-
'Edge': '50',
|
|
23
|
-
'Firefox': '51',
|
|
24
|
-
'Opera': '53',
|
|
25
|
-
'Safari': '54',
|
|
26
|
-
'Brave': '1.79.112',
|
|
27
|
-
'Vivaldi': '6.2.3105.58',
|
|
28
|
-
'Tor': '12.5.3',
|
|
29
|
-
'Yandex': '23.7.1',
|
|
30
|
-
'Falkon': '22.08.3',
|
|
31
|
-
'Epiphany': '44.2'
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const PLATFORM_MAP = {
|
|
35
|
-
'aix': 'AIX',
|
|
36
|
-
'darwin': 'Mac OS',
|
|
37
|
-
'win32': 'Windows',
|
|
38
|
-
'android': 'Android',
|
|
39
|
-
'freebsd': 'FreeBSD',
|
|
40
|
-
'openbsd': 'OpenBSD',
|
|
41
|
-
'sunos': 'Solaris',
|
|
42
|
-
'linux': 'Linux',
|
|
43
|
-
'ubuntu': 'Ubuntu',
|
|
44
|
-
'ios': 'iOS',
|
|
45
|
-
'baileys': 'Baileys',
|
|
46
|
-
'chromeos': 'Chrome OS',
|
|
47
|
-
'tizen': 'Tizen',
|
|
48
|
-
'watchos': 'watchOS',
|
|
49
|
-
'wearos': 'Wear OS',
|
|
50
|
-
'harmonyos': 'HarmonyOS',
|
|
51
|
-
'kaios': 'KaiOS',
|
|
52
|
-
'smarttv': 'Smart TV',
|
|
53
|
-
'raspberrypi': 'Raspberry Pi OS',
|
|
54
|
-
'symbian': 'Symbian',
|
|
55
|
-
'blackberry': 'Blackberry OS',
|
|
56
|
-
'windowsphone': 'Windows Phone'
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const PLATFORM_VERSIONS = {
|
|
60
|
-
'ubuntu': '22.04.4',
|
|
61
|
-
'darwin': '18.5',
|
|
62
|
-
'win32': '10.0.22631',
|
|
63
|
-
'android': '14.0.0',
|
|
64
|
-
'freebsd': '13.2',
|
|
65
|
-
'openbsd': '7.3',
|
|
66
|
-
'sunos': '11',
|
|
67
|
-
'linux': '6.5',
|
|
68
|
-
'ios': '18.2',
|
|
69
|
-
'baileys': '6.5.0',
|
|
70
|
-
'chromeos': '117.0.5938.132',
|
|
71
|
-
'tizen': '6.5',
|
|
72
|
-
'watchos': '10.1',
|
|
73
|
-
'wearos': '4.1',
|
|
74
|
-
'harmonyos': '4.0.0',
|
|
75
|
-
'kaios': '3.1',
|
|
76
|
-
'smarttv': '23.3.1',
|
|
77
|
-
'raspberrypi': '11 (Bullseye)',
|
|
78
|
-
'symbian': '3',
|
|
79
|
-
'blackberry': '10.3.3',
|
|
80
|
-
'windowsphone': '8.1'
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const Browsers = {
|
|
84
|
-
ubuntu: (browser) => {
|
|
85
|
-
return [PLATFORM_MAP['ubuntu'], browser, PLATFORM_VERSIONS['ubuntu']]
|
|
86
|
-
},
|
|
87
|
-
macOS: (browser) => {
|
|
88
|
-
return [PLATFORM_MAP['darwin'], browser, PLATFORM_VERSIONS['darwin']]
|
|
89
|
-
},
|
|
90
|
-
windows: (browser) => {
|
|
91
|
-
return [PLATFORM_MAP['win32'], browser, PLATFORM_VERSIONS['win32']]
|
|
92
|
-
},
|
|
93
|
-
linux: (browser) => {
|
|
94
|
-
return [PLATFORM_MAP['linux'], browser, PLATFORM_VERSIONS['linux']]
|
|
95
|
-
},
|
|
96
|
-
solaris: (browser) => {
|
|
97
|
-
return [PLATFORM_MAP['sunos'], browser, PLATFORM_VERSIONS['sunos']]
|
|
98
|
-
},
|
|
99
|
-
baileys: (browser) => {
|
|
100
|
-
return [PLATFORM_MAP['baileys'], browser, PLATFORM_VERSIONS['baileys']]
|
|
101
|
-
},
|
|
102
|
-
android: (browser) => {
|
|
103
|
-
return [PLATFORM_MAP['android'], browser, PLATFORM_VERSIONS['android']]
|
|
104
|
-
},
|
|
105
|
-
iOS: (browser) => {
|
|
106
|
-
return [PLATFORM_MAP['ios'], browser, PLATFORM_VERSIONS['ios']]
|
|
107
|
-
},
|
|
108
|
-
kaiOS: (browser) => {
|
|
109
|
-
return [PLATFORM_MAP['kaios'], browser, PLATFORM_VERSIONS['kaios']]
|
|
110
|
-
},
|
|
111
|
-
chromeOS: (browser) => {
|
|
112
|
-
return [PLATFORM_MAP['chromeos'], browser, PLATFORM_VERSIONS['chromeos']]
|
|
113
|
-
},
|
|
114
|
-
appropriate: (browser) => {
|
|
115
|
-
const platform = platform()
|
|
116
|
-
const platformName = PLATFORM_MAP[platform] || 'Unknown OS'
|
|
117
|
-
return [platformName, browser, PLATFORM_VERSIONS[platform] || 'latest']
|
|
118
|
-
},
|
|
119
|
-
custom: (platform, browser, version) => {
|
|
120
|
-
const platformName = PLATFORM_MAP[platform.toLowerCase()] || platform
|
|
121
|
-
return [platformName, browser, version || PLATFORM_VERSIONS[platform] || 'latest']
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
19
|
const Itsuki = async () => {
|
|
126
20
|
try {
|
|
127
21
|
const response = await fetch('https://raw.githubusercontent.com/Itsukichann/database/refs/heads/main/itsuki.json', {
|
|
@@ -140,31 +34,34 @@ const Itsuki = async () => {
|
|
|
140
34
|
}
|
|
141
35
|
|
|
142
36
|
const BufferJSON = {
|
|
143
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
144
37
|
replacer: (k, value) => {
|
|
145
38
|
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
146
39
|
return { type: 'Buffer', data: Buffer.from(value?.data || value).toString('base64') }
|
|
147
40
|
}
|
|
148
41
|
return value
|
|
149
42
|
},
|
|
150
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
151
43
|
reviver: (_, value) => {
|
|
152
|
-
if (typeof value === 'object' &&
|
|
153
|
-
|
|
154
|
-
return typeof val === 'string' ? Buffer.from(val, 'base64') : Buffer.from(val || [])
|
|
44
|
+
if (typeof value === 'object' && value !== null && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
45
|
+
return Buffer.from(value.data, 'base64')
|
|
155
46
|
}
|
|
47
|
+
|
|
48
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
49
|
+
const keys = Object.keys(value)
|
|
50
|
+
|
|
51
|
+
if (keys.length > 0 && keys.every(k => !isNaN(parseInt(k, 10)))) {
|
|
52
|
+
const values = Object.values(value)
|
|
53
|
+
|
|
54
|
+
if (values.every(v => typeof v === 'number')) {
|
|
55
|
+
return Buffer.from(values)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
156
60
|
return value
|
|
157
61
|
}
|
|
158
62
|
}
|
|
159
63
|
|
|
160
|
-
const
|
|
161
|
-
const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase()]
|
|
162
|
-
return platformType ? platformType.toString() : '1'
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const getKeyAuthor = (key, meId = 'me') => {
|
|
166
|
-
return key?.fromMe ? meId : key?.participant || key?.remoteJid || ''
|
|
167
|
-
}
|
|
64
|
+
const getKeyAuthor = (key, meId = 'me') => (key?.fromMe ? meId : key?.participantAlt || key?.remoteJidAlt || key?.participant || key?.remoteJid) || ''
|
|
168
65
|
|
|
169
66
|
const writeRandomPadMax16 = (msg) => {
|
|
170
67
|
const pad = randomBytes(1)
|
|
@@ -230,9 +127,7 @@ const debouncedTimeout = (intervalMs = 1000, task) => {
|
|
|
230
127
|
}
|
|
231
128
|
}
|
|
232
129
|
|
|
233
|
-
const delay = (ms) =>
|
|
234
|
-
return delayCancellable(ms).delay
|
|
235
|
-
}
|
|
130
|
+
const delay = (ms) => delayCancellable(ms).delay
|
|
236
131
|
|
|
237
132
|
const delayCancellable = (ms) => {
|
|
238
133
|
const stack = new Error().stack
|
|
@@ -295,7 +190,7 @@ const generateParticipantHashV2 = (participants) => {
|
|
|
295
190
|
participants.sort()
|
|
296
191
|
const sha256Hash = sha256(Buffer.from(participants.join(''))).toString('base64')
|
|
297
192
|
return '2:' + sha256Hash.slice(0, 6)
|
|
298
|
-
}
|
|
193
|
+
}
|
|
299
194
|
|
|
300
195
|
function bindWaitForEvent(ev, event) {
|
|
301
196
|
return async (check, timeoutMs) => {
|
|
@@ -491,15 +386,22 @@ const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
|
491
386
|
return status
|
|
492
387
|
}
|
|
493
388
|
|
|
389
|
+
const UNEXPECTED_SERVER_CODE_TEXT = 'Unexpected server response: '
|
|
390
|
+
|
|
494
391
|
const getCodeFromWSError = (error) => {
|
|
495
392
|
let statusCode = 500
|
|
496
|
-
|
|
497
|
-
|
|
393
|
+
|
|
394
|
+
if (error?.message?.includes(UNEXPECTED_SERVER_CODE_TEXT)) {
|
|
395
|
+
const code = +error?.message.slice(UNEXPECTED_SERVER_CODE_TEXT.length)
|
|
396
|
+
|
|
498
397
|
if (!Number.isNaN(code) && code >= 400) {
|
|
499
398
|
statusCode = code
|
|
500
399
|
}
|
|
501
400
|
}
|
|
502
|
-
else if (
|
|
401
|
+
else if (
|
|
402
|
+
error?.code?.startsWith('E') ||
|
|
403
|
+
error?.message?.includes('timed out')) {
|
|
404
|
+
// handle ETIMEOUT, ENOTFOUND etc
|
|
503
405
|
statusCode = 408
|
|
504
406
|
}
|
|
505
407
|
return statusCode
|
|
@@ -560,10 +462,8 @@ const asciiDecode = (...codes) => {
|
|
|
560
462
|
}
|
|
561
463
|
|
|
562
464
|
module.exports = {
|
|
563
|
-
Browsers,
|
|
564
465
|
Itsuki,
|
|
565
466
|
BufferJSON,
|
|
566
|
-
getPlatformId,
|
|
567
467
|
getKeyAuthor,
|
|
568
468
|
writeRandomPadMax16,
|
|
569
469
|
unpadRandomMax16,
|
package/lib/Utils/history.js
CHANGED
|
@@ -6,7 +6,6 @@ const { promisify } = require("util")
|
|
|
6
6
|
const { inflate } = require("zlib")
|
|
7
7
|
const { proto } = require("../../WAProto")
|
|
8
8
|
const { WAMessageStubType } = require("../Types")
|
|
9
|
-
const { isJidUser } = require("../WABinary")
|
|
10
9
|
const { toNumber } = require("./generics")
|
|
11
10
|
const { normalizeMessageContent } = require("./messages")
|
|
12
11
|
const { downloadContentFromMessage } = require("./messages-media")
|
|
@@ -67,12 +66,7 @@ const processHistoryMessage = (item) => {
|
|
|
67
66
|
break
|
|
68
67
|
case proto.HistorySync.HistorySyncType.PUSH_NAME:
|
|
69
68
|
for (const c of item.pushnames) {
|
|
70
|
-
contacts.push({
|
|
71
|
-
id: c.id,
|
|
72
|
-
name: c.name || undefined,
|
|
73
|
-
lid: c.lidJid || undefined,
|
|
74
|
-
jid: isJidUser(c.id) ? c.id : undefined
|
|
75
|
-
})
|
|
69
|
+
contacts.push({ id: c.id, notify: c.pushname })
|
|
76
70
|
}
|
|
77
71
|
break
|
|
78
72
|
}
|
|
@@ -86,7 +80,15 @@ const processHistoryMessage = (item) => {
|
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
const downloadAndProcessHistorySyncNotification = async (msg, options) => {
|
|
89
|
-
|
|
83
|
+
let historyMsg
|
|
84
|
+
|
|
85
|
+
if (msg.initialHistBootstrapInlinePayload) {
|
|
86
|
+
historyMsg = proto.HistorySync.decode(await inflatePromise(msg.initialHistBootstrapInlinePayload))
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
historyMsg = await downloadHistory(msg, options)
|
|
90
|
+
}
|
|
91
|
+
|
|
90
92
|
return processHistoryMessage(historyMsg)
|
|
91
93
|
}
|
|
92
94
|
|
package/lib/Utils/index.js
CHANGED
|
@@ -47,7 +47,7 @@ __exportStar(require("./history"), exports)
|
|
|
47
47
|
__exportStar(require("./chat-utils"), exports)
|
|
48
48
|
__exportStar(require("./lt-hash"), exports)
|
|
49
49
|
__exportStar(require("./auth-utils"), exports)
|
|
50
|
-
__exportStar(require("./
|
|
50
|
+
__exportStar(require("./browser-utils"), exports)
|
|
51
51
|
__exportStar(require("./use-multi-file-auth-state"), exports)
|
|
52
52
|
__exportStar(require("./link-preview"), exports)
|
|
53
53
|
__exportStar(require("./event-buffer"), exports)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
4
4
|
|
|
5
|
+
const { unfurl } = require('unfurl.js')
|
|
5
6
|
const { prepareWAMessageMedia } = require("./messages")
|
|
6
7
|
const {
|
|
7
8
|
getHttpStream,
|
|
@@ -22,38 +23,24 @@ const getCompressedJpegThumbnail = async (url, { thumbnailWidth, fetchOpts }) =>
|
|
|
22
23
|
* @param text first matched URL in text
|
|
23
24
|
* @returns the URL info required to generate link preview
|
|
24
25
|
*/
|
|
25
|
-
const getUrlInfo = async (text, opts = { thumbnailWidth: THUMBNAIL_WIDTH_PX, fetchOpts: { timeout: 3000 }}) => {
|
|
26
|
+
const getUrlInfo = async (text, opts = { thumbnailWidth: THUMBNAIL_WIDTH_PX, fetchOpts: { timeout: 3000 } }) => {
|
|
26
27
|
try {
|
|
27
|
-
const retries = 0
|
|
28
|
-
const maxRetry = 5
|
|
29
|
-
const { getLinkPreview } = await Promise.resolve().then(() => __importStar(require('link-preview-js')))
|
|
30
28
|
let previewLink = text
|
|
29
|
+
|
|
31
30
|
if (!text.startsWith('https://') && !text.startsWith('http://')) {
|
|
32
31
|
previewLink = 'https://' + previewLink
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
|
|
34
|
+
const { open_graph: info } = await unfurl(previewLink, {
|
|
35
35
|
...opts.fetchOpts,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
if (forwardedURLObj.hostname === urlObj.hostname
|
|
44
|
-
|| forwardedURLObj.hostname === 'www.' + urlObj.hostname
|
|
45
|
-
|| 'www.' + forwardedURLObj.hostname === urlObj.hostname) {
|
|
46
|
-
retries + 1
|
|
47
|
-
return true
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
return false
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
headers: opts.fetchOpts
|
|
54
|
-
})
|
|
36
|
+
oembed: false,
|
|
37
|
+
compress: true,
|
|
38
|
+
size: 0,
|
|
39
|
+
follow: 50
|
|
40
|
+
})
|
|
41
|
+
|
|
55
42
|
if (info && 'title' in info && info.title) {
|
|
56
|
-
const
|
|
43
|
+
const image = info.images?.[0]?.url
|
|
57
44
|
const urlInfo = {
|
|
58
45
|
'canonical-url': info.url,
|
|
59
46
|
'matched-text': text,
|
|
@@ -61,22 +48,20 @@ const getUrlInfo = async (text, opts = { thumbnailWidth: THUMBNAIL_WIDTH_PX, fet
|
|
|
61
48
|
description: info.description,
|
|
62
49
|
originalThumbnailUrl: image
|
|
63
50
|
}
|
|
51
|
+
|
|
64
52
|
if (opts.uploadImage) {
|
|
65
|
-
const { imageMessage } = await
|
|
53
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: { url: image } }, {
|
|
66
54
|
upload: opts.uploadImage,
|
|
67
55
|
mediaTypeOverride: 'thumbnail-link',
|
|
68
56
|
options: opts.fetchOpts
|
|
69
57
|
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
: undefined
|
|
58
|
+
|
|
59
|
+
urlInfo.jpegThumbnail = imageMessage?.jpegThumbnail ? Buffer.from(imageMessage.jpegThumbnail) : undefined
|
|
73
60
|
urlInfo.highQualityThumbnail = imageMessage || undefined
|
|
74
61
|
}
|
|
75
62
|
else {
|
|
76
63
|
try {
|
|
77
|
-
urlInfo.jpegThumbnail = image
|
|
78
|
-
? (await getCompressedJpegThumbnail(image, opts)).buffer
|
|
79
|
-
: undefined
|
|
64
|
+
urlInfo.jpegThumbnail = image ? (await getCompressedJpegThumbnail(image, opts)).buffer : undefined
|
|
80
65
|
}
|
|
81
66
|
catch (error) {
|
|
82
67
|
opts.logger?.debug({ err: error.stack, url: previewLink }, 'error in generating thumbnail')
|
package/lib/Utils/lt-hash.js
CHANGED
|
@@ -10,48 +10,54 @@ const { hkdf } = require("./crypto")
|
|
|
10
10
|
* if the same series of mutations was made sequentially.
|
|
11
11
|
*/
|
|
12
12
|
const o = 128
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
class LTHash {
|
|
14
15
|
constructor(e) {
|
|
15
16
|
this.salt = e
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
|
|
19
|
+
async add(e, t) {
|
|
19
20
|
for (const item of t) {
|
|
20
|
-
e =
|
|
21
|
+
e = await this._addSingle(e, item)
|
|
21
22
|
}
|
|
22
23
|
return e
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
async subtract(e, t) {
|
|
26
27
|
for (const item of t) {
|
|
27
|
-
e =
|
|
28
|
+
e = await this._subtractSingle(e, item)
|
|
28
29
|
}
|
|
29
30
|
return e
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
async subtractThenAdd(e, addList, subtractList) {
|
|
34
|
+
const subtracted = await this.subtract(e, subtractList)
|
|
35
|
+
return this.add(subtracted, addList)
|
|
34
36
|
}
|
|
37
|
+
|
|
35
38
|
async _addSingle(e, t) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return r.performPointwiseWithOverflow(await e, n, ((e, t) => e + t))
|
|
39
|
+
const derived = new Uint8Array(await hkdf(Buffer.from(t), o, { info: this.salt })).buffer
|
|
40
|
+
return this.performPointwiseWithOverflow(e, derived, (a, b) => a + b)
|
|
39
41
|
}
|
|
42
|
+
|
|
40
43
|
async _subtractSingle(e, t) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return r.performPointwiseWithOverflow(await e, n, ((e, t) => e - t))
|
|
44
|
+
const derived = new Uint8Array(await hkdf(Buffer.from(t), o, { info: this.salt })).buffer
|
|
45
|
+
return this.performPointwiseWithOverflow(e, derived, (a, b) => a - b)
|
|
44
46
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
|
|
48
|
+
performPointwiseWithOverflow(e, t, op) {
|
|
49
|
+
const n = new DataView(e)
|
|
50
|
+
const i = new DataView(t)
|
|
51
|
+
const out = new ArrayBuffer(n.byteLength)
|
|
52
|
+
const s = new DataView(out)
|
|
53
|
+
for (let offset = 0; offset < n.byteLength; offset += 2) {
|
|
54
|
+
s.setUint16(offset, op(n.getUint16(offset, true), i.getUint16(offset, true)), true)
|
|
49
55
|
}
|
|
50
|
-
return
|
|
56
|
+
return out
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
const LT_HASH_ANTI_TAMPERING = new
|
|
60
|
+
const LT_HASH_ANTI_TAMPERING = new LTHash('WhatsApp Patch Integrity')
|
|
55
61
|
|
|
56
62
|
module.exports = {
|
|
57
63
|
LT_HASH_ANTI_TAMPERING
|
package/lib/Utils/make-mutex.js
CHANGED
|
@@ -2,43 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
4
4
|
|
|
5
|
+
const { Mutex: AsyncMutex } = require("async-mutex")
|
|
6
|
+
|
|
5
7
|
const makeMutex = () => {
|
|
6
|
-
|
|
7
|
-
let task = Promise.resolve()
|
|
8
|
-
let taskTimeout
|
|
8
|
+
const mutex = new AsyncMutex()
|
|
9
9
|
return {
|
|
10
10
|
mutex(code) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// if there is an error, we swallow so as to not block the queue
|
|
14
|
-
try {
|
|
15
|
-
await task
|
|
16
|
-
}
|
|
17
|
-
catch (_a) { }
|
|
18
|
-
try {
|
|
19
|
-
// execute the current task
|
|
20
|
-
const result = await code()
|
|
21
|
-
return result
|
|
22
|
-
}
|
|
23
|
-
finally {
|
|
24
|
-
clearTimeout(taskTimeout)
|
|
25
|
-
}
|
|
26
|
-
})()
|
|
27
|
-
// we replace the existing task, appending the new piece of execution to it
|
|
28
|
-
// so the next task will have to wait for this one to finish
|
|
29
|
-
return task
|
|
30
|
-
},
|
|
11
|
+
return mutex.runExclusive(code)
|
|
12
|
+
}
|
|
31
13
|
}
|
|
32
14
|
}
|
|
33
15
|
|
|
34
16
|
const makeKeyedMutex = () => {
|
|
35
|
-
const map =
|
|
17
|
+
const map = new Map()
|
|
36
18
|
return {
|
|
37
|
-
mutex(key, task) {
|
|
38
|
-
|
|
39
|
-
|
|
19
|
+
async mutex(key, task) {
|
|
20
|
+
let entry = map.get(key)
|
|
21
|
+
|
|
22
|
+
if (!entry) {
|
|
23
|
+
entry = { mutex: new AsyncMutex(), refCount: 0 }
|
|
24
|
+
map.set(key, entry)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
entry.refCount++
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
return await entry.mutex.runExclusive(task)
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
entry.refCount--
|
|
34
|
+
|
|
35
|
+
// only delete it if this is still the current entry
|
|
36
|
+
if (entry.refCount === 0 && map.get(key) === entry) {
|
|
37
|
+
map.delete(key)
|
|
38
|
+
}
|
|
40
39
|
}
|
|
41
|
-
return map[key].mutex(task)
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
}
|
|
@@ -7,6 +7,8 @@ const { LRUCache } = require("lru-cache")
|
|
|
7
7
|
/** Number of sent messages to cache in memory for handling retry receipts */
|
|
8
8
|
const RECENT_MESSAGES_SIZE = 512
|
|
9
9
|
|
|
10
|
+
const MESSAGE_KEY_SEPARATOR = '\u0000'
|
|
11
|
+
|
|
10
12
|
/** Timeout for session recreation - 1 hour */
|
|
11
13
|
const RECREATE_SESSION_TIMEOUT = 60 * 60 * 1000 // 1 hour in milliseconds
|
|
12
14
|
|
|
@@ -16,8 +18,18 @@ class MessageRetryManager {
|
|
|
16
18
|
constructor(logger, maxMsgRetryCount) {
|
|
17
19
|
this.logger = logger
|
|
18
20
|
this.recentMessagesMap = new LRUCache({
|
|
19
|
-
max: RECENT_MESSAGES_SIZE
|
|
21
|
+
max: RECENT_MESSAGES_SIZE,
|
|
22
|
+
ttl: 5 * 60 * 1000,
|
|
23
|
+
ttlAutopurge: true,
|
|
24
|
+
dispose: (_value, key) => {
|
|
25
|
+
const separatorIndex = key.lastIndexOf(MESSAGE_KEY_SEPARATOR)
|
|
26
|
+
if (separatorIndex > -1) {
|
|
27
|
+
const messageId = key.slice(separatorIndex + MESSAGE_KEY_SEPARATOR.length)
|
|
28
|
+
this.messageKeyIndex.delete(messageId)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
20
31
|
})
|
|
32
|
+
this.messageKeyIndex = new Map()
|
|
21
33
|
this.sessionRecreateHistory = new LRUCache({
|
|
22
34
|
ttl: RECREATE_SESSION_TIMEOUT * 2,
|
|
23
35
|
ttlAutopurge: true
|
|
@@ -39,27 +51,33 @@ class MessageRetryManager {
|
|
|
39
51
|
}
|
|
40
52
|
this.maxMsgRetryCount = maxMsgRetryCount
|
|
41
53
|
}
|
|
54
|
+
|
|
42
55
|
/**
|
|
43
56
|
* Add a recent message to the cache for retry handling
|
|
44
57
|
*/
|
|
45
58
|
addRecentMessage(to, id, message) {
|
|
46
59
|
const key = { to, id }
|
|
47
60
|
const keyStr = this.keyToString(key)
|
|
61
|
+
|
|
48
62
|
// Add new message
|
|
49
63
|
this.recentMessagesMap.set(keyStr, {
|
|
50
64
|
message,
|
|
51
65
|
timestamp: Date.now()
|
|
52
66
|
})
|
|
67
|
+
this.messageKeyIndex.set(id, keyStr)
|
|
53
68
|
this.logger.debug(`Added message to retry cache: ${to}/${id}`)
|
|
54
69
|
}
|
|
70
|
+
|
|
55
71
|
/**
|
|
56
72
|
* Get a recent message from the cache
|
|
57
73
|
*/
|
|
58
74
|
getRecentMessage(to, id) {
|
|
59
75
|
const key = { to, id }
|
|
60
76
|
const keyStr = this.keyToString(key)
|
|
77
|
+
|
|
61
78
|
return this.recentMessagesMap.get(keyStr)
|
|
62
79
|
}
|
|
80
|
+
|
|
63
81
|
/**
|
|
64
82
|
* Check if a session should be recreated based on retry count and history
|
|
65
83
|
*/
|
|
@@ -73,16 +91,20 @@ class MessageRetryManager {
|
|
|
73
91
|
recreate: true
|
|
74
92
|
}
|
|
75
93
|
}
|
|
94
|
+
|
|
76
95
|
// Only consider recreation if retry count > 1
|
|
77
96
|
if (retryCount < 2) {
|
|
78
97
|
return { reason: '', recreate: false }
|
|
79
98
|
}
|
|
99
|
+
|
|
80
100
|
const now = Date.now()
|
|
81
101
|
const prevTime = this.sessionRecreateHistory.get(jid)
|
|
102
|
+
|
|
82
103
|
// If no previous recreation or it's been more than an hour
|
|
83
104
|
if (!prevTime || now - prevTime > RECREATE_SESSION_TIMEOUT) {
|
|
84
105
|
this.sessionRecreateHistory.set(jid, now)
|
|
85
106
|
this.statistics.sessionRecreations++
|
|
107
|
+
|
|
86
108
|
return {
|
|
87
109
|
reason: 'retry count > 1 and over an hour since last recreation',
|
|
88
110
|
recreate: true
|
|
@@ -90,26 +112,31 @@ class MessageRetryManager {
|
|
|
90
112
|
}
|
|
91
113
|
return { reason: '', recreate: false }
|
|
92
114
|
}
|
|
115
|
+
|
|
93
116
|
/**
|
|
94
117
|
* Increment retry counter for a message
|
|
95
118
|
*/
|
|
96
119
|
incrementRetryCount(messageId) {
|
|
97
120
|
this.retryCounters.set(messageId, (this.retryCounters.get(messageId) || 0) + 1)
|
|
98
121
|
this.statistics.totalRetries++
|
|
122
|
+
|
|
99
123
|
return this.retryCounters.get(messageId)
|
|
100
124
|
}
|
|
125
|
+
|
|
101
126
|
/**
|
|
102
127
|
* Get retry count for a message
|
|
103
128
|
*/
|
|
104
129
|
getRetryCount(messageId) {
|
|
105
130
|
return this.retryCounters.get(messageId) || 0
|
|
106
131
|
}
|
|
132
|
+
|
|
107
133
|
/**
|
|
108
134
|
* Check if message has exceeded maximum retry attempts
|
|
109
135
|
*/
|
|
110
136
|
hasExceededMaxRetries(messageId) {
|
|
111
137
|
return this.getRetryCount(messageId) >= this.maxMsgRetryCount
|
|
112
138
|
}
|
|
139
|
+
|
|
113
140
|
/**
|
|
114
141
|
* Mark retry as successful
|
|
115
142
|
*/
|
|
@@ -118,14 +145,19 @@ class MessageRetryManager {
|
|
|
118
145
|
// Clean up retry counter for successful message
|
|
119
146
|
this.retryCounters.delete(messageId)
|
|
120
147
|
this.cancelPendingPhoneRequest(messageId)
|
|
148
|
+
this.removeRecentMessage(messageId)
|
|
121
149
|
}
|
|
150
|
+
|
|
122
151
|
/**
|
|
123
152
|
* Mark retry as failed
|
|
124
153
|
*/
|
|
125
154
|
markRetryFailed(messageId) {
|
|
126
155
|
this.statistics.failedRetries++
|
|
127
156
|
this.retryCounters.delete(messageId)
|
|
157
|
+
this.cancelPendingPhoneRequest(messageId)
|
|
158
|
+
this.removeRecentMessage(messageId)
|
|
128
159
|
}
|
|
160
|
+
|
|
129
161
|
/**
|
|
130
162
|
* Schedule a phone request with delay
|
|
131
163
|
*/
|
|
@@ -134,24 +166,40 @@ class MessageRetryManager {
|
|
|
134
166
|
this.cancelPendingPhoneRequest(messageId)
|
|
135
167
|
this.pendingPhoneRequests[messageId] = setTimeout(() => {
|
|
136
168
|
delete this.pendingPhoneRequests[messageId]
|
|
169
|
+
|
|
137
170
|
this.statistics.phoneRequests++
|
|
171
|
+
|
|
138
172
|
callback()
|
|
139
173
|
}, delay)
|
|
140
|
-
this.logger.debug(`Scheduled phone request for message ${messageId} with ${delay}ms delay`)
|
|
174
|
+
this.logger.debug(`Scheduled phone request for message ${messageId} with ${delay}ms delay`);
|
|
141
175
|
}
|
|
176
|
+
|
|
142
177
|
/**
|
|
143
178
|
* Cancel pending phone request
|
|
144
179
|
*/
|
|
145
180
|
cancelPendingPhoneRequest(messageId) {
|
|
146
181
|
const timeout = this.pendingPhoneRequests[messageId]
|
|
182
|
+
|
|
147
183
|
if (timeout) {
|
|
148
184
|
clearTimeout(timeout)
|
|
185
|
+
|
|
149
186
|
delete this.pendingPhoneRequests[messageId]
|
|
187
|
+
|
|
150
188
|
this.logger.debug(`Cancelled pending phone request for message ${messageId}`)
|
|
151
189
|
}
|
|
152
190
|
}
|
|
191
|
+
|
|
153
192
|
keyToString(key) {
|
|
154
|
-
return `${key.to}
|
|
193
|
+
return `${key.to}${MESSAGE_KEY_SEPARATOR}${key.id}`
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
removeRecentMessage(messageId) {
|
|
197
|
+
const keyStr = this.messageKeyIndex.get(messageId)
|
|
198
|
+
|
|
199
|
+
if (!keyStr) return
|
|
200
|
+
|
|
201
|
+
this.recentMessagesMap.delete(keyStr)
|
|
202
|
+
this.messageKeyIndex.delete(messageId)
|
|
155
203
|
}
|
|
156
204
|
}
|
|
157
205
|
|