@badzz88/baileys 8.4.7 → 8.5.0
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/LICENSE +21 -0
- package/README.md +1055 -337
- package/WAProto/index.js +5 -5
- package/lib/Defaults/index.js +187 -127
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Signal/Group/ciphertext-message.js +14 -11
- package/lib/Signal/Group/group-session-builder.js +91 -29
- package/lib/Signal/Group/group_cipher.js +88 -81
- package/lib/Signal/Group/index.js +136 -12
- package/lib/Signal/Group/keyhelper.js +70 -15
- package/lib/Signal/Group/sender-chain-key.js +31 -25
- package/lib/Signal/Group/sender-key-distribution-message.js +65 -62
- package/lib/Signal/Group/sender-key-message.js +68 -65
- package/lib/Signal/Group/sender-key-name.js +43 -41
- package/lib/Signal/Group/sender-key-record.js +43 -40
- package/lib/Signal/Group/sender-key-state.js +96 -83
- package/lib/Signal/Group/sender-message-key.js +29 -25
- package/lib/Signal/libsignal.js +483 -424
- package/lib/Signal/lid-mapping.js +261 -276
- package/lib/Socket/Client/index.js +30 -3
- package/lib/Socket/Client/types.js +12 -10
- package/lib/Socket/Client/websocket.js +61 -53
- package/lib/Socket/aigroups.js +240 -0
- package/lib/Socket/business.js +422 -379
- package/lib/Socket/chats.js +1816 -1193
- package/lib/Socket/communities.js +549 -431
- package/lib/Socket/graphql.js +656 -0
- package/lib/Socket/groups.js +764 -374
- package/lib/Socket/index.js +38 -11
- package/lib/Socket/interactive-handler.js +522 -0
- package/lib/Socket/interop.js +549 -0
- package/lib/Socket/luxu.js +6 -5
- package/lib/Socket/managed-account.js +183 -0
- package/lib/Socket/messages-recv.js +2439 -1915
- package/lib/Socket/messages-send.js +1642 -1242
- package/lib/Socket/mex.js +59 -40
- package/lib/Socket/newsletter.js +812 -249
- package/lib/Socket/privacy.js +449 -0
- package/lib/Socket/registration.js +427 -0
- package/lib/Socket/socket.js +1077 -965
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +237 -140
- package/lib/Store/index.js +36 -10
- package/lib/Store/make-cache-manager-store.js +85 -80
- package/lib/Store/make-in-memory-store.js +591 -185
- package/lib/Store/make-ordered-dictionary.js +78 -72
- package/lib/Store/object-repository.js +25 -28
- package/lib/Types/Auth.js +38 -2
- package/lib/Types/Bussines.js +2 -2
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.js +4 -8
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.js +26 -24
- package/lib/Types/LabelAssociation.js +8 -6
- package/lib/Types/Message.js +95 -11
- package/lib/Types/Mex.js +112 -37
- package/lib/Types/Newsletter.js +121 -0
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.js +70 -56
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.js +55 -26
- package/lib/Utils/auth-utils.js +292 -288
- package/lib/Utils/browser-utils.js +113 -48
- package/lib/Utils/business.js +240 -224
- package/lib/Utils/chat-utils.js +1205 -869
- 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 +41 -34
- package/lib/Utils/consumer-application.js +107 -0
- package/lib/Utils/crypto.js +144 -107
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +518 -308
- package/lib/Utils/event-buffer.js +596 -611
- package/lib/Utils/generics.js +514 -354
- package/lib/Utils/group-history.js +60 -0
- package/lib/Utils/history.js +244 -134
- package/lib/Utils/identity-change-handler.js +51 -49
- package/lib/Utils/index.js +57 -23
- package/lib/Utils/jid-display-normalization.js +218 -0
- package/lib/Utils/link-preview.js +135 -77
- package/lib/Utils/logger.js +9 -3
- package/lib/Utils/lt-hash.js +37 -6
- package/lib/Utils/make-mutex.js +36 -33
- package/lib/Utils/message-composer.js +439 -233
- package/lib/Utils/message-retry-manager.js +265 -260
- package/lib/Utils/messages-media.js +855 -747
- package/lib/Utils/messages.js +2528 -884
- package/lib/Utils/meta-ai-msmsg.js +262 -0
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +195 -200
- package/lib/Utils/offline-node-processor.js +35 -33
- package/lib/Utils/pre-key-manager.js +103 -102
- package/lib/Utils/process-message.js +978 -560
- package/lib/Utils/reporting-utils.js +257 -253
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +218 -195
- package/lib/Utils/stanza-ack.js +31 -29
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +50 -45
- package/lib/Utils/tc-token-utils.js +137 -139
- package/lib/Utils/use-multi-file-auth-state.js +109 -109
- package/lib/Utils/validate-connection.js +218 -202
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/Utils/voip-rekey.js +22 -0
- package/lib/WABinary/constants.js +1302 -1299
- package/lib/WABinary/decode.js +343 -262
- package/lib/WABinary/encode.js +12 -4
- package/lib/WABinary/generic-utils.js +223 -187
- package/lib/WABinary/index.js +33 -6
- package/lib/WABinary/jid-utils.js +351 -88
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.js +12 -9
- package/lib/WAM/constants.js +39486 -22853
- package/lib/WAM/encode.js +132 -140
- package/lib/WAM/index.js +31 -4
- package/lib/WAUSync/Protocols/USyncBotProfileProtocol.js +55 -0
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +43 -0
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +53 -51
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +56 -53
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +29 -26
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +59 -0
- package/lib/WAUSync/Protocols/USyncLIDProtocol.js +31 -0
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +32 -0
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +29 -0
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +38 -37
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +38 -0
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +26 -24
- package/lib/WAUSync/Protocols/index.js +39 -6
- package/lib/WAUSync/USyncQuery.js +132 -97
- package/lib/WAUSync/USyncUser.js +49 -30
- package/lib/WAUSync/index.js +31 -4
- package/lib/antiban.js +4637 -0
- package/lib/index.js +48 -15
- package/package.json +55 -92
- package/lib/WAUSync/Protocols/USyncNewsletterProtocol.js +0 -263
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +0 -51
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +0 -29
package/lib/Utils/generics.js
CHANGED
|
@@ -1,403 +1,563 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
+
exports.isWABusinessPlatform =
|
|
4
|
+
exports.getCodeFromWSError =
|
|
5
|
+
exports.getCallStatusFromNode =
|
|
6
|
+
exports.getErrorCodeFromStreamError =
|
|
7
|
+
exports.getStatusFromReceiptType =
|
|
8
|
+
exports.generateMdTagPrefix =
|
|
9
|
+
exports.fetchLatestWaWebVersion =
|
|
10
|
+
exports.fetchLatestBaileysVersion =
|
|
11
|
+
exports.bindWaitForConnectionUpdate =
|
|
12
|
+
exports.generateMessageID =
|
|
13
|
+
exports.generateMessageIDV2 =
|
|
14
|
+
exports.delayCancellable =
|
|
15
|
+
exports.delay =
|
|
16
|
+
exports.debouncedTimeout =
|
|
17
|
+
exports.unixTimestampSeconds =
|
|
18
|
+
exports.toNumber =
|
|
19
|
+
exports.encodeBigEndian =
|
|
20
|
+
exports.generateRegistrationId =
|
|
21
|
+
exports.encodeWAMessage =
|
|
22
|
+
exports.generateParticipantHashV2 =
|
|
23
|
+
exports.unpadRandomMax16 =
|
|
24
|
+
exports.writeRandomPadMax16 =
|
|
25
|
+
exports.isStringNullOrEmpty =
|
|
26
|
+
exports.getKeyAuthor =
|
|
27
|
+
exports.BufferJSON =
|
|
28
|
+
exports.jitterDelay =
|
|
29
|
+
exports.exponentialBackoff =
|
|
30
|
+
exports.bytesToHex =
|
|
31
|
+
exports.hexToBytes =
|
|
32
|
+
exports.bytesToBase64Url =
|
|
33
|
+
exports.sha256Hex =
|
|
34
|
+
exports.hmacSha256 =
|
|
35
|
+
exports.normalizeJidBatch =
|
|
36
|
+
exports.sleep =
|
|
37
|
+
exports.withRetry =
|
|
38
|
+
void 0
|
|
39
|
+
exports.promiseTimeout = promiseTimeout
|
|
40
|
+
exports.bindWaitForEvent = bindWaitForEvent
|
|
41
|
+
exports.trimUndefined = trimUndefined
|
|
42
|
+
exports.bytesToCrockford = bytesToCrockford
|
|
43
|
+
exports.encodeNewsletterMessage = encodeNewsletterMessage
|
|
44
|
+
exports.generateIOSMessageID = void 0
|
|
45
|
+
exports.generateAndroMessageID = void 0
|
|
46
|
+
const boom_1 = require('@hapi/boom')
|
|
47
|
+
const crypto_1 = require('crypto')
|
|
48
|
+
const Crypto_1 = require('./crypto')
|
|
49
|
+
const DEFAULTS_1 = require('../Defaults')
|
|
50
|
+
const index_js_1 = require('../../WAProto/index.js')
|
|
51
|
+
const baileysVersion = DEFAULTS_1.VERSION
|
|
52
|
+
const Types_1 = require('../Types')
|
|
53
|
+
const WABinary_1 = require('../WABinary')
|
|
54
|
+
const crypto_2 = require('./crypto')
|
|
55
|
+
exports.BufferJSON = {
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
replacer: (k, value) => {
|
|
58
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array || value?.type === 'Buffer') {
|
|
59
|
+
return { type: 'Buffer', data: Buffer.from(value?.data || value).toString('base64') }
|
|
60
|
+
}
|
|
61
|
+
return value
|
|
62
|
+
},
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
reviver: (_, value) => {
|
|
65
|
+
if (typeof value === 'object' && value !== null && value.type === 'Buffer' && typeof value.data === 'string') {
|
|
66
|
+
return Buffer.from(value.data, 'base64')
|
|
67
|
+
}
|
|
68
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
69
|
+
const keys = Object.keys(value)
|
|
70
|
+
if (keys.length > 0 && keys.every(k => !isNaN(parseInt(k, 10)))) {
|
|
71
|
+
const values = Object.values(value)
|
|
72
|
+
if (values.every(v => typeof v === 'number')) {
|
|
73
|
+
return Buffer.from(values)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return value
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const getKeyAuthor = (key, meId = 'me') =>
|
|
81
|
+
(key?.fromMe ? meId : key?.participantAlt || key?.remoteJidAlt || key?.participant || key?.remoteJid) || ''
|
|
82
|
+
exports.getKeyAuthor = getKeyAuthor
|
|
83
|
+
const isStringNullOrEmpty = value =>
|
|
84
|
+
// eslint-disable-next-line eqeqeq
|
|
85
|
+
value == null || value === ''
|
|
86
|
+
exports.isStringNullOrEmpty = isStringNullOrEmpty
|
|
87
|
+
const writeRandomPadMax16 = msg => {
|
|
88
|
+
const pad = (0, crypto_1.randomBytes)(1)
|
|
89
|
+
const padLength = (pad[0] & 0x0f) + 1
|
|
90
|
+
return Buffer.concat([msg, Buffer.alloc(padLength, padLength)])
|
|
91
|
+
}
|
|
92
|
+
exports.writeRandomPadMax16 = writeRandomPadMax16
|
|
93
|
+
const unpadRandomMax16 = e => {
|
|
94
|
+
const t = new Uint8Array(e)
|
|
95
|
+
if (0 === t.length) {
|
|
96
|
+
throw new Error('unpadPkcs7 given empty bytes')
|
|
97
|
+
}
|
|
98
|
+
var r = t[t.length - 1]
|
|
99
|
+
if (r > t.length) {
|
|
100
|
+
throw new Error(`unpad given ${t.length} bytes, but pad is ${r}`)
|
|
101
|
+
}
|
|
102
|
+
return new Uint8Array(t.buffer, t.byteOffset, t.length - r)
|
|
103
|
+
}
|
|
104
|
+
exports.unpadRandomMax16 = unpadRandomMax16
|
|
53
105
|
// code is inspired by whatsmeow
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
106
|
+
const generateParticipantHashV2 = participants => {
|
|
107
|
+
participants.sort()
|
|
108
|
+
const sha256Hash = (0, crypto_2.sha256)(Buffer.from(participants.join(''))).toString('base64')
|
|
109
|
+
return '2:' + sha256Hash.slice(0, 6)
|
|
110
|
+
}
|
|
111
|
+
exports.generateParticipantHashV2 = generateParticipantHashV2
|
|
112
|
+
const encodeWAMessage = message => (0, exports.writeRandomPadMax16)(index_js_1.proto.Message.encode(message).finish())
|
|
113
|
+
exports.encodeWAMessage = encodeWAMessage
|
|
114
|
+
const generateRegistrationId = () => {
|
|
115
|
+
return Uint16Array.from((0, crypto_1.randomBytes)(2))[0] & 16383
|
|
116
|
+
}
|
|
117
|
+
exports.generateRegistrationId = generateRegistrationId
|
|
118
|
+
const encodeBigEndian = (e, t = 4) => {
|
|
119
|
+
let r = e
|
|
120
|
+
const a = new Uint8Array(t)
|
|
121
|
+
for (let i = t - 1; i >= 0; i--) {
|
|
122
|
+
a[i] = 255 & r
|
|
123
|
+
r >>>= 8
|
|
124
|
+
}
|
|
125
|
+
return a
|
|
126
|
+
}
|
|
127
|
+
exports.encodeBigEndian = encodeBigEndian
|
|
128
|
+
const toNumber = t => (typeof t === 'object' && t ? ('toNumber' in t ? t.toNumber() : t.low) : t || 0)
|
|
129
|
+
exports.toNumber = toNumber
|
|
73
130
|
/** unix timestamp of a date in seconds */
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
const unixTimestampSeconds = (date = new Date()) => Math.floor(date.getTime() / 1000)
|
|
132
|
+
exports.unixTimestampSeconds = unixTimestampSeconds
|
|
133
|
+
const debouncedTimeout = (intervalMs = 1000, task) => {
|
|
134
|
+
let timeout
|
|
135
|
+
return {
|
|
136
|
+
start: (newIntervalMs, newTask) => {
|
|
137
|
+
task = newTask || task
|
|
138
|
+
intervalMs = newIntervalMs || intervalMs
|
|
139
|
+
timeout && clearTimeout(timeout)
|
|
140
|
+
timeout = setTimeout(() => task?.(), intervalMs)
|
|
141
|
+
},
|
|
142
|
+
cancel: () => {
|
|
143
|
+
timeout && clearTimeout(timeout)
|
|
144
|
+
timeout = undefined
|
|
145
|
+
},
|
|
146
|
+
setTask: newTask => (task = newTask),
|
|
147
|
+
setInterval: newInterval => (intervalMs = newInterval)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.debouncedTimeout = debouncedTimeout
|
|
151
|
+
const delay = ms => (0, exports.delayCancellable)(ms).delay
|
|
152
|
+
exports.delay = delay
|
|
153
|
+
const delayCancellable = ms => {
|
|
154
|
+
const stack = new Error().stack
|
|
155
|
+
let timeout
|
|
156
|
+
let reject
|
|
157
|
+
const delay = new Promise((resolve, _reject) => {
|
|
158
|
+
timeout = setTimeout(resolve, ms)
|
|
159
|
+
reject = _reject
|
|
160
|
+
})
|
|
161
|
+
const cancel = () => {
|
|
162
|
+
clearTimeout(timeout)
|
|
163
|
+
reject(
|
|
164
|
+
new boom_1.Boom('Cancelled', {
|
|
165
|
+
statusCode: 500,
|
|
166
|
+
data: {
|
|
167
|
+
stack
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
return { delay, cancel }
|
|
173
|
+
}
|
|
174
|
+
exports.delayCancellable = delayCancellable
|
|
175
|
+
async function promiseTimeout(ms, promise) {
|
|
176
|
+
if (!ms) {
|
|
177
|
+
return new Promise(promise)
|
|
178
|
+
}
|
|
179
|
+
const stack = new Error().stack
|
|
180
|
+
// Create a promise that rejects in <ms> milliseconds
|
|
181
|
+
const { delay, cancel } = (0, exports.delayCancellable)(ms)
|
|
182
|
+
const p = new Promise((resolve, reject) => {
|
|
183
|
+
delay
|
|
184
|
+
.then(() =>
|
|
185
|
+
reject(
|
|
186
|
+
new boom_1.Boom('Timed Out', {
|
|
187
|
+
statusCode: Types_1.DisconnectReason.timedOut,
|
|
188
|
+
data: {
|
|
189
|
+
stack
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
.catch(err => reject(err))
|
|
195
|
+
promise(resolve, reject)
|
|
196
|
+
}).finally(cancel)
|
|
197
|
+
return p
|
|
131
198
|
}
|
|
132
199
|
// inspired from whatsmeow code
|
|
133
200
|
// https://github.com/tulir/whatsmeow/blob/64bc969fbe78d31ae0dd443b8d4c80a5d026d07a/send.go#L42
|
|
134
|
-
|
|
201
|
+
const generateMessageIDV2 = (userId) => {
|
|
135
202
|
const data = Buffer.alloc(8 + 20 + 16);
|
|
136
203
|
data.writeBigUInt64BE(BigInt(Math.floor(Date.now() / 1000)));
|
|
137
204
|
if (userId) {
|
|
138
|
-
const id = jidDecode(userId);
|
|
205
|
+
const id = (0, WABinary_1.jidDecode)(userId);
|
|
139
206
|
if (id?.user) {
|
|
140
207
|
data.write(id.user, 8);
|
|
141
208
|
data.write('@c.us', 8 + id.user.length);
|
|
142
209
|
}
|
|
143
210
|
}
|
|
144
|
-
const random = randomBytes(16);
|
|
211
|
+
const random = (0, crypto_1.randomBytes)(16);
|
|
145
212
|
random.copy(data, 28);
|
|
146
|
-
const hash = createHash('sha256').update(data).digest();
|
|
213
|
+
const hash = (0, crypto_1.createHash)('sha256').update(data).digest();
|
|
147
214
|
return 'XZCYYX-' + hash.toString('hex').toUpperCase().substring(0, 18);
|
|
148
215
|
};
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
export function bindWaitForEvent(ev, event) {
|
|
152
|
-
return async (check, timeoutMs) => {
|
|
153
|
-
let listener;
|
|
154
|
-
let closeListener;
|
|
155
|
-
await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
156
|
-
closeListener = ({ connection, lastDisconnect }) => {
|
|
157
|
-
if (connection === 'close') {
|
|
158
|
-
reject(lastDisconnect?.error || new Boom('Connection Closed', { statusCode: DisconnectReason.connectionClosed }));
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
ev.on('connection.update', closeListener);
|
|
162
|
-
listener = async (update) => {
|
|
163
|
-
if (await check(update)) {
|
|
164
|
-
resolve();
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
ev.on(event, listener);
|
|
168
|
-
}).finally(() => {
|
|
169
|
-
ev.off(event, listener);
|
|
170
|
-
ev.off('connection.update', closeListener);
|
|
171
|
-
});
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
export const generateIOSMessageID = () => {
|
|
216
|
+
exports.generateMessageIDV2 = generateMessageIDV2
|
|
217
|
+
const generateIOSMessageID = () => {
|
|
175
218
|
const prefix = '3A';
|
|
176
|
-
const random = randomBytes(
|
|
219
|
+
const random = (0, crypto_1.randomBytes)(9.5); // 19 hex chars = 9.5 bytes
|
|
177
220
|
return (prefix + random.toString('hex')).toUpperCase().substring(0, 21);
|
|
178
221
|
};
|
|
179
|
-
|
|
222
|
+
exports.generateIOSMessageID = generateIOSMessageID
|
|
223
|
+
const generateAndroMessageID = () => {
|
|
180
224
|
const prefix = '3A';
|
|
181
|
-
const random = randomBytes(16); // 32 hex chars = 16 bytes
|
|
225
|
+
const random = (0, crypto_1.randomBytes)(16); // 32 hex chars = 16 bytes
|
|
182
226
|
return (random.toString('hex')).toUpperCase().substring(0, 21);
|
|
183
227
|
};
|
|
184
|
-
|
|
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()
|
|
231
|
+
exports.generateMessageID = generateMessageID
|
|
232
|
+
function bindWaitForEvent(ev, event) {
|
|
233
|
+
return async (check, timeoutMs) => {
|
|
234
|
+
let listener
|
|
235
|
+
let closeListener
|
|
236
|
+
await promiseTimeout(timeoutMs, (resolve, reject) => {
|
|
237
|
+
closeListener = ({ connection, lastDisconnect }) => {
|
|
238
|
+
if (connection === 'close') {
|
|
239
|
+
reject(
|
|
240
|
+
lastDisconnect?.error ||
|
|
241
|
+
new boom_1.Boom('Connection Closed', { statusCode: Types_1.DisconnectReason.connectionClosed })
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
ev.on('connection.update', closeListener)
|
|
246
|
+
listener = async update => {
|
|
247
|
+
if (await check(update)) {
|
|
248
|
+
resolve()
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
ev.on(event, listener)
|
|
252
|
+
}).finally(() => {
|
|
253
|
+
ev.off(event, listener)
|
|
254
|
+
ev.off('connection.update', closeListener)
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const bindWaitForConnectionUpdate = ev => bindWaitForEvent(ev, 'connection.update')
|
|
259
|
+
exports.bindWaitForConnectionUpdate = bindWaitForConnectionUpdate
|
|
185
260
|
/**
|
|
186
261
|
* utility that fetches latest baileys version from the master branch.
|
|
187
262
|
* Use to ensure your WA connection is always on the latest version
|
|
188
263
|
*/
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
264
|
+
const fetchLatestBaileysVersion = async (options = {}) => {
|
|
265
|
+
const URL = 'https://raw.githubusercontent.com/WhiskeySockets/Baileys/master/src/Defaults/index.ts'
|
|
266
|
+
try {
|
|
267
|
+
const response = await fetch(URL, {
|
|
268
|
+
dispatcher: options.dispatcher,
|
|
269
|
+
method: 'GET',
|
|
270
|
+
headers: options.headers
|
|
271
|
+
})
|
|
272
|
+
if (!response.ok) {
|
|
273
|
+
throw new boom_1.Boom(`Failed to fetch latest Baileys version: ${response.statusText}`, {
|
|
274
|
+
statusCode: response.status
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
const text = await response.text()
|
|
278
|
+
// Extract version from line 7 (const version = [...])
|
|
279
|
+
const lines = text.split('\n')
|
|
280
|
+
const versionLine = lines[6] // Line 7 (0-indexed)
|
|
281
|
+
const versionMatch = versionLine.match(/const version = \[(\d+),\s*(\d+),\s*(\d+)\]/)
|
|
282
|
+
if (versionMatch) {
|
|
283
|
+
const version = [parseInt(versionMatch[1]), parseInt(versionMatch[2]), parseInt(versionMatch[3])]
|
|
284
|
+
return {
|
|
285
|
+
version,
|
|
286
|
+
isLatest: true
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
throw new Error('Could not parse version from Defaults/index.ts')
|
|
290
|
+
}
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return {
|
|
293
|
+
version: baileysVersion,
|
|
294
|
+
isLatest: false,
|
|
295
|
+
error
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
exports.fetchLatestBaileysVersion = fetchLatestBaileysVersion
|
|
224
300
|
/**
|
|
225
301
|
* A utility that fetches the latest web version of whatsapp.
|
|
226
302
|
* Use to ensure your WA connection is always on the latest version
|
|
227
303
|
*/
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
304
|
+
const fetchLatestWaWebVersion = async (options = {}) => {
|
|
305
|
+
try {
|
|
306
|
+
// Absolute minimal headers required to bypass anti-bot detection
|
|
307
|
+
const defaultHeaders = {
|
|
308
|
+
'sec-fetch-site': 'none',
|
|
309
|
+
'user-agent':
|
|
310
|
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
311
|
+
}
|
|
312
|
+
const headers = { ...defaultHeaders, ...options.headers }
|
|
313
|
+
const response = await fetch('https://web.whatsapp.com/sw.js', {
|
|
314
|
+
...options,
|
|
315
|
+
method: 'GET',
|
|
316
|
+
headers
|
|
317
|
+
})
|
|
318
|
+
if (!response.ok) {
|
|
319
|
+
throw new boom_1.Boom(`Failed to fetch sw.js: ${response.statusText}`, { statusCode: response.status })
|
|
320
|
+
}
|
|
321
|
+
const data = await response.text()
|
|
322
|
+
const regex = /\\?"client_revision\\?":\s*(\d+)/
|
|
323
|
+
const match = data.match(regex)
|
|
324
|
+
if (!match?.[1]) {
|
|
325
|
+
return {
|
|
326
|
+
version: baileysVersion,
|
|
327
|
+
isLatest: false,
|
|
328
|
+
error: {
|
|
329
|
+
message: 'Could not find client revision in the fetched content'
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const clientRevision = match[1]
|
|
334
|
+
return {
|
|
335
|
+
version: [2, 3000, +clientRevision],
|
|
336
|
+
isLatest: true
|
|
337
|
+
}
|
|
338
|
+
} catch (error) {
|
|
339
|
+
return {
|
|
340
|
+
version: baileysVersion,
|
|
341
|
+
isLatest: false,
|
|
342
|
+
error
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
exports.fetchLatestWaWebVersion = fetchLatestWaWebVersion
|
|
270
347
|
/** unique message tag prefix for MD clients */
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
348
|
+
const generateMdTagPrefix = () => {
|
|
349
|
+
const bytes = (0, crypto_1.randomBytes)(4)
|
|
350
|
+
return `${bytes.readUInt16BE()}.${bytes.readUInt16BE(2)}-`
|
|
351
|
+
}
|
|
352
|
+
exports.generateMdTagPrefix = generateMdTagPrefix
|
|
275
353
|
const STATUS_MAP = {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
354
|
+
sender: index_js_1.proto.WebMessageInfo.Status.SERVER_ACK,
|
|
355
|
+
played: index_js_1.proto.WebMessageInfo.Status.PLAYED,
|
|
356
|
+
read: index_js_1.proto.WebMessageInfo.Status.READ,
|
|
357
|
+
'read-self': index_js_1.proto.WebMessageInfo.Status.READ
|
|
358
|
+
}
|
|
281
359
|
/**
|
|
282
360
|
* Given a type of receipt, returns what the new status of the message should be
|
|
283
361
|
* @param type type from receipt
|
|
284
362
|
*/
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
363
|
+
const getStatusFromReceiptType = type => {
|
|
364
|
+
const status = STATUS_MAP[type]
|
|
365
|
+
if (typeof type === 'undefined') {
|
|
366
|
+
return index_js_1.proto.WebMessageInfo.Status.DELIVERY_ACK
|
|
367
|
+
}
|
|
368
|
+
return status
|
|
369
|
+
}
|
|
370
|
+
exports.getStatusFromReceiptType = getStatusFromReceiptType
|
|
292
371
|
const CODE_MAP = {
|
|
293
|
-
|
|
294
|
-
}
|
|
372
|
+
conflict: Types_1.DisconnectReason.connectionReplaced
|
|
373
|
+
}
|
|
295
374
|
/**
|
|
296
375
|
* Stream errors generally provide a reason, map that to a baileys DisconnectReason
|
|
297
376
|
* @param reason the string reason given, eg. "conflict"
|
|
298
377
|
*/
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
error?.code?.startsWith('E') ||
|
|
360
|
-
error?.message?.includes('timed out')) {
|
|
361
|
-
// handle ETIMEOUT, ENOTFOUND etc
|
|
362
|
-
statusCode = 408;
|
|
363
|
-
}
|
|
364
|
-
return statusCode;
|
|
365
|
-
};
|
|
378
|
+
const getErrorCodeFromStreamError = node => {
|
|
379
|
+
const [reasonNode] = (0, WABinary_1.getAllBinaryNodeChildren)(node)
|
|
380
|
+
let reason = reasonNode?.tag || 'unknown'
|
|
381
|
+
const statusCode = +(node.attrs.code || CODE_MAP[reason] || Types_1.DisconnectReason.badSession)
|
|
382
|
+
if (statusCode === Types_1.DisconnectReason.restartRequired) {
|
|
383
|
+
reason = 'restart required'
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
reason,
|
|
387
|
+
statusCode
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
exports.getErrorCodeFromStreamError = getErrorCodeFromStreamError
|
|
391
|
+
const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
392
|
+
let status
|
|
393
|
+
switch (tag) {
|
|
394
|
+
case 'offer':
|
|
395
|
+
case 'offer_notice':
|
|
396
|
+
status = 'offer'
|
|
397
|
+
break
|
|
398
|
+
case 'terminate':
|
|
399
|
+
if (attrs.reason === 'timeout') {
|
|
400
|
+
status = 'timeout'
|
|
401
|
+
} else {
|
|
402
|
+
//fired when accepted/rejected/timeout/caller hangs up
|
|
403
|
+
status = 'terminate'
|
|
404
|
+
}
|
|
405
|
+
break
|
|
406
|
+
case 'reject':
|
|
407
|
+
status = 'reject'
|
|
408
|
+
break
|
|
409
|
+
case 'accept':
|
|
410
|
+
status = 'accept'
|
|
411
|
+
break
|
|
412
|
+
default:
|
|
413
|
+
status = 'ringing'
|
|
414
|
+
break
|
|
415
|
+
}
|
|
416
|
+
return status
|
|
417
|
+
}
|
|
418
|
+
exports.getCallStatusFromNode = getCallStatusFromNode
|
|
419
|
+
const UNEXPECTED_SERVER_CODE_TEXT = 'Unexpected server response: '
|
|
420
|
+
const getCodeFromWSError = error => {
|
|
421
|
+
let statusCode = 500
|
|
422
|
+
if (error?.message?.includes(UNEXPECTED_SERVER_CODE_TEXT)) {
|
|
423
|
+
const code = +error?.message.slice(UNEXPECTED_SERVER_CODE_TEXT.length)
|
|
424
|
+
if (!Number.isNaN(code) && code >= 400) {
|
|
425
|
+
statusCode = code
|
|
426
|
+
}
|
|
427
|
+
} else if (
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
429
|
+
error?.code?.startsWith('E') ||
|
|
430
|
+
error?.message?.includes('timed out')
|
|
431
|
+
) {
|
|
432
|
+
// handle ETIMEOUT, ENOTFOUND etc
|
|
433
|
+
statusCode = 408
|
|
434
|
+
}
|
|
435
|
+
return statusCode
|
|
436
|
+
}
|
|
437
|
+
exports.getCodeFromWSError = getCodeFromWSError
|
|
366
438
|
/**
|
|
367
439
|
* Is the given platform WA business
|
|
368
440
|
* @param platform AuthenticationCreds.platform
|
|
369
441
|
*/
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
442
|
+
const isWABusinessPlatform = platform => {
|
|
443
|
+
return platform === 'smbi' || platform === 'smba'
|
|
444
|
+
}
|
|
445
|
+
exports.isWABusinessPlatform = isWABusinessPlatform
|
|
373
446
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
447
|
+
function trimUndefined(obj) {
|
|
448
|
+
for (const key in obj) {
|
|
449
|
+
if (typeof obj[key] === 'undefined') {
|
|
450
|
+
delete obj[key]
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return obj
|
|
381
454
|
}
|
|
382
|
-
const CROCKFORD_CHARACTERS = '123456789ABCDEFGHJKLMNPQRSTVWXYZ'
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
455
|
+
const CROCKFORD_CHARACTERS = '123456789ABCDEFGHJKLMNPQRSTVWXYZ'
|
|
456
|
+
function bytesToCrockford(buffer) {
|
|
457
|
+
let value = 0
|
|
458
|
+
let bitCount = 0
|
|
459
|
+
const crockford = []
|
|
460
|
+
for (const element of buffer) {
|
|
461
|
+
value = (value << 8) | (element & 0xff)
|
|
462
|
+
bitCount += 8
|
|
463
|
+
while (bitCount >= 5) {
|
|
464
|
+
crockford.push(CROCKFORD_CHARACTERS.charAt((value >>> (bitCount - 5)) & 31))
|
|
465
|
+
bitCount -= 5
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (bitCount > 0) {
|
|
469
|
+
crockford.push(CROCKFORD_CHARACTERS.charAt((value << (5 - bitCount)) & 31))
|
|
470
|
+
}
|
|
471
|
+
return crockford.join('')
|
|
472
|
+
}
|
|
473
|
+
function encodeNewsletterMessage(message) {
|
|
474
|
+
return index_js_1.proto.Message.encode(message).finish()
|
|
475
|
+
}
|
|
476
|
+
|
|
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
|
+
*/
|
|
482
|
+
const jitterDelay = (baseMs, varianceFraction = 0.3) => {
|
|
483
|
+
const variance = baseMs * varianceFraction
|
|
484
|
+
return baseMs + (Math.random() * 2 - 1) * variance
|
|
399
485
|
}
|
|
400
|
-
|
|
401
|
-
|
|
486
|
+
exports.jitterDelay = jitterDelay
|
|
487
|
+
|
|
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
|
+
*/
|
|
494
|
+
const exponentialBackoff = (attempt, baseMs = 500, maxMs = 30000) => {
|
|
495
|
+
return Math.min(baseMs * Math.pow(2, attempt), maxMs)
|
|
496
|
+
}
|
|
497
|
+
exports.exponentialBackoff = exponentialBackoff
|
|
498
|
+
|
|
499
|
+
/** Convert buffer/Uint8Array to hex string */
|
|
500
|
+
const bytesToHex = buf => Buffer.from(buf).toString('hex')
|
|
501
|
+
exports.bytesToHex = bytesToHex
|
|
502
|
+
|
|
503
|
+
/** Convert hex string to Buffer */
|
|
504
|
+
const hexToBytes = hex => Buffer.from(hex, 'hex')
|
|
505
|
+
exports.hexToBytes = hexToBytes
|
|
506
|
+
|
|
507
|
+
/** Convert buffer to base64url string */
|
|
508
|
+
const bytesToBase64Url = buf => Buffer.from(buf).toString('base64url')
|
|
509
|
+
exports.bytesToBase64Url = bytesToBase64Url
|
|
510
|
+
|
|
511
|
+
/** Compute SHA-256 hex digest of data */
|
|
512
|
+
const sha256Hex = data => (0, crypto_1.createHash)('sha256').update(data).digest('hex')
|
|
513
|
+
exports.sha256Hex = sha256Hex
|
|
514
|
+
|
|
515
|
+
/** Compute HMAC-SHA-256 of data with key, returns Buffer */
|
|
516
|
+
const hmacSha256 = (data, key) => (0, crypto_1.createHmac)('sha256', key).update(data).digest()
|
|
517
|
+
exports.hmacSha256 = hmacSha256
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Normalize and deduplicate an array of JIDs
|
|
521
|
+
*/
|
|
522
|
+
const normalizeJidBatch = (jids, normalizer) => {
|
|
523
|
+
const seen = new Set()
|
|
524
|
+
const result = []
|
|
525
|
+
for (const jid of jids) {
|
|
526
|
+
if (!jid) continue
|
|
527
|
+
const normalized = normalizer ? normalizer(jid) : jid
|
|
528
|
+
if (!seen.has(normalized)) {
|
|
529
|
+
seen.add(normalized)
|
|
530
|
+
result.push(normalized)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
return result
|
|
534
|
+
}
|
|
535
|
+
exports.normalizeJidBatch = normalizeJidBatch
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Sleep for a given number of milliseconds, returns a cancellable promise
|
|
539
|
+
*/
|
|
540
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
541
|
+
exports.sleep = sleep
|
|
542
|
+
|
|
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
|
+
*/
|
|
549
|
+
const withRetry = async (fn, maxAttempts = 3, baseDelayMs = 500) => {
|
|
550
|
+
let lastErr
|
|
551
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
552
|
+
try {
|
|
553
|
+
return await fn()
|
|
554
|
+
} catch (err) {
|
|
555
|
+
lastErr = err
|
|
556
|
+
if (i < maxAttempts - 1) {
|
|
557
|
+
await sleep(exponentialBackoff(i, baseDelayMs))
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
throw lastErr
|
|
402
562
|
}
|
|
403
|
-
|
|
563
|
+
exports.withRetry = withRetry
|