@badzz88/baileys 8.5.4 → 8.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/WAProto/WAProto.proto +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +42 -0
- package/lib/Utils/crypto.js +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
package/lib/antiban.js
CHANGED
|
@@ -23,17 +23,17 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
23
23
|
var __toESM = (mod, isNodeMode, target) => (
|
|
24
24
|
(target = mod != null ? __create(__getProtoOf(mod)) : {}),
|
|
25
25
|
__copyProps(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
30
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, 'default', { value: mod, enumerable: true }) : target,
|
|
31
31
|
mod
|
|
32
32
|
)
|
|
33
33
|
)
|
|
34
34
|
var __toCommonJS = mod => __copyProps(__defProp({}, '__esModule', { value: true }), mod)
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// index.js
|
|
37
37
|
var index_exports = {}
|
|
38
38
|
__export(index_exports, {
|
|
39
39
|
AntiBan: () => AntiBan,
|
|
@@ -61,7 +61,6 @@ __export(index_exports, {
|
|
|
61
61
|
WebhookAlerts: () => WebhookAlerts,
|
|
62
62
|
applyFingerprint: () => applyFingerprint,
|
|
63
63
|
applyGroupMultiplier: () => applyGroupMultiplier,
|
|
64
|
-
buildContentSignature: () => buildContentSignature,
|
|
65
64
|
classifyDisconnect: () => classifyDisconnect,
|
|
66
65
|
createLidFirstResolver: () => createLidFirstResolver,
|
|
67
66
|
credsSnapshot: () => credsSnapshot,
|
|
@@ -83,44 +82,46 @@ __export(index_exports, {
|
|
|
83
82
|
})
|
|
84
83
|
module.exports = __toCommonJS(index_exports)
|
|
85
84
|
|
|
86
|
-
|
|
85
|
+
// rateLimiter.js
|
|
87
86
|
var TIME_CONSTANTS = {
|
|
88
87
|
MS_PER_SECOND: 1e3,
|
|
89
88
|
MS_PER_MINUTE: 6e4,
|
|
90
89
|
MS_PER_HOUR: 36e5,
|
|
91
90
|
MS_PER_DAY: 864e5,
|
|
92
91
|
BURST_RESET_MS: 3e4,
|
|
93
|
-
IDENTICAL_WINDOW_MS:
|
|
94
|
-
|
|
92
|
+
IDENTICAL_WINDOW_MS: 36e5
|
|
93
|
+
// 1 hour
|
|
95
94
|
}
|
|
96
95
|
var DEFAULT_CONFIG = {
|
|
97
|
-
maxPerMinute:
|
|
98
|
-
maxPerHour:
|
|
99
|
-
maxPerDay:
|
|
100
|
-
minDelayMs:
|
|
101
|
-
maxDelayMs:
|
|
102
|
-
newChatDelayMs:
|
|
103
|
-
maxIdenticalMessages:
|
|
104
|
-
burstAllowance:
|
|
96
|
+
maxPerMinute: 8,
|
|
97
|
+
maxPerHour: 200,
|
|
98
|
+
maxPerDay: 1500,
|
|
99
|
+
minDelayMs: 1500,
|
|
100
|
+
maxDelayMs: 5e3,
|
|
101
|
+
newChatDelayMs: 3e3,
|
|
102
|
+
maxIdenticalMessages: 3,
|
|
103
|
+
burstAllowance: 3,
|
|
105
104
|
identicalMessageWindowMs: TIME_CONSTANTS.IDENTICAL_WINDOW_MS
|
|
106
105
|
}
|
|
107
106
|
var RateLimiter = class {
|
|
108
107
|
config
|
|
109
108
|
messages = []
|
|
110
|
-
identicalCount =
|
|
111
|
-
knownChats =
|
|
109
|
+
identicalCount = /* @__PURE__ */ new Map()
|
|
110
|
+
knownChats = /* @__PURE__ */ new Set()
|
|
112
111
|
burstCount = 0
|
|
113
112
|
lastMessageTime = 0
|
|
114
|
-
statsCacheTime = 0
|
|
115
|
-
statsCache = null
|
|
116
113
|
constructor(config = {}) {
|
|
117
114
|
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
118
115
|
}
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Calculate delay before next message can be sent.
|
|
118
|
+
* Returns 0 if message can be sent immediately.
|
|
119
|
+
* Returns -1 if message should be blocked entirely.
|
|
120
|
+
*/
|
|
121
|
+
async getDelay(recipient, content) {
|
|
121
122
|
const now = Date.now()
|
|
122
123
|
this.cleanup(now)
|
|
123
|
-
const contentHash = this.hashContent(
|
|
124
|
+
const contentHash = this.hashContent(content)
|
|
124
125
|
const dayMessages = this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY)
|
|
125
126
|
if (dayMessages.length >= this.config.maxPerDay) {
|
|
126
127
|
return -1
|
|
@@ -166,14 +167,16 @@ var RateLimiter = class {
|
|
|
166
167
|
if (timeSinceLast < this.config.minDelayMs) {
|
|
167
168
|
delay = Math.max(delay, this.config.minDelayMs - timeSinceLast)
|
|
168
169
|
}
|
|
169
|
-
const typingDelay = Math.min(
|
|
170
|
+
const typingDelay = Math.min(content.length * 30, 3e3)
|
|
170
171
|
delay += this.jitter(typingDelay * 0.5, typingDelay)
|
|
171
172
|
return Math.round(delay)
|
|
172
173
|
}
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Record a sent message
|
|
176
|
+
*/
|
|
177
|
+
record(recipient, content) {
|
|
175
178
|
const now = Date.now()
|
|
176
|
-
const contentHash = this.hashContent(
|
|
179
|
+
const contentHash = this.hashContent(content)
|
|
177
180
|
const timeSinceLast = now - this.lastMessageTime
|
|
178
181
|
if (timeSinceLast > TIME_CONSTANTS.BURST_RESET_MS) {
|
|
179
182
|
this.burstCount = 0
|
|
@@ -193,26 +196,16 @@ var RateLimiter = class {
|
|
|
193
196
|
this.identicalCount.set(contentHash, { count: 1, firstSeen: now, lastSeen: now })
|
|
194
197
|
}
|
|
195
198
|
}
|
|
196
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Get current usage stats
|
|
201
|
+
*/
|
|
197
202
|
getStats() {
|
|
198
203
|
const now = Date.now()
|
|
199
|
-
if (this.statsCache && now - this.statsCacheTime < 100) {
|
|
200
|
-
return this.statsCache
|
|
201
|
-
}
|
|
202
204
|
this.cleanup(now)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const age = now - m.timestamp
|
|
208
|
-
if (age < TIME_CONSTANTS.MS_PER_MINUTE) lastMinute++
|
|
209
|
-
if (age < TIME_CONSTANTS.MS_PER_HOUR) lastHour++
|
|
210
|
-
if (age < TIME_CONSTANTS.MS_PER_DAY) lastDay++
|
|
211
|
-
}
|
|
212
|
-
this.statsCache = {
|
|
213
|
-
lastMinute,
|
|
214
|
-
lastHour,
|
|
215
|
-
lastDay,
|
|
205
|
+
return {
|
|
206
|
+
lastMinute: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_MINUTE).length,
|
|
207
|
+
lastHour: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_HOUR).length,
|
|
208
|
+
lastDay: this.messages.filter(m => now - m.timestamp < TIME_CONSTANTS.MS_PER_DAY).length,
|
|
216
209
|
limits: {
|
|
217
210
|
perMinute: this.config.maxPerMinute,
|
|
218
211
|
perHour: this.config.maxPerHour,
|
|
@@ -220,14 +213,12 @@ var RateLimiter = class {
|
|
|
220
213
|
},
|
|
221
214
|
knownChats: this.knownChats.size
|
|
222
215
|
}
|
|
223
|
-
this.statsCacheTime = now
|
|
224
|
-
return this.statsCache
|
|
225
216
|
}
|
|
226
|
-
|
|
217
|
+
/** Get the set of known chat JIDs (for state persistence) */
|
|
227
218
|
getKnownChats() {
|
|
228
219
|
return this.knownChats
|
|
229
220
|
}
|
|
230
|
-
|
|
221
|
+
/** Restore known chats from persisted state */
|
|
231
222
|
restoreKnownChats(chats) {
|
|
232
223
|
for (const jid of chats) {
|
|
233
224
|
this.knownChats.add(jid)
|
|
@@ -241,7 +232,7 @@ var RateLimiter = class {
|
|
|
241
232
|
}
|
|
242
233
|
}
|
|
243
234
|
}
|
|
244
|
-
|
|
235
|
+
/** Random delay between min and max (gaussian-ish distribution) */
|
|
245
236
|
jitter(min, max) {
|
|
246
237
|
const u1 = Math.random()
|
|
247
238
|
const u2 = Math.random()
|
|
@@ -250,7 +241,7 @@ var RateLimiter = class {
|
|
|
250
241
|
const clamped = Math.max(0, Math.min(1, normalized))
|
|
251
242
|
return Math.round(min + clamped * (max - min))
|
|
252
243
|
}
|
|
253
|
-
|
|
244
|
+
/** Simple hash for content dedup */
|
|
254
245
|
hashContent(content) {
|
|
255
246
|
let hash = 0
|
|
256
247
|
for (let i = 0; i < content.length; i++) {
|
|
@@ -262,12 +253,12 @@ var RateLimiter = class {
|
|
|
262
253
|
}
|
|
263
254
|
}
|
|
264
255
|
|
|
265
|
-
|
|
256
|
+
// warmup.js
|
|
266
257
|
var DEFAULT_CONFIG2 = {
|
|
267
|
-
warmUpDays:
|
|
268
|
-
day1Limit:
|
|
258
|
+
warmUpDays: 7,
|
|
259
|
+
day1Limit: 20,
|
|
269
260
|
growthFactor: 1.8,
|
|
270
|
-
inactivityThresholdHours:
|
|
261
|
+
inactivityThresholdHours: 72
|
|
271
262
|
}
|
|
272
263
|
var WarmUp = class {
|
|
273
264
|
config
|
|
@@ -276,7 +267,9 @@ var WarmUp = class {
|
|
|
276
267
|
this.config = { ...DEFAULT_CONFIG2, ...config }
|
|
277
268
|
this.state = existingState || this.freshState()
|
|
278
269
|
}
|
|
279
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Get the current daily message limit based on warm-up phase
|
|
272
|
+
*/
|
|
280
273
|
getDailyLimit() {
|
|
281
274
|
if (this.state.graduated) return Infinity
|
|
282
275
|
const day = this.getCurrentDay()
|
|
@@ -286,7 +279,9 @@ var WarmUp = class {
|
|
|
286
279
|
}
|
|
287
280
|
return Math.round(this.config.day1Limit * Math.pow(this.config.growthFactor, day))
|
|
288
281
|
}
|
|
289
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Check if a message can be sent (within warm-up limits)
|
|
284
|
+
*/
|
|
290
285
|
canSend() {
|
|
291
286
|
this.checkInactivity()
|
|
292
287
|
if (this.state.graduated) return true
|
|
@@ -294,7 +289,9 @@ var WarmUp = class {
|
|
|
294
289
|
const todayCount = this.state.dailyCounts[day] || 0
|
|
295
290
|
return todayCount < this.getDailyLimit()
|
|
296
291
|
}
|
|
297
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Record a sent message
|
|
294
|
+
*/
|
|
298
295
|
record() {
|
|
299
296
|
const now = Date.now()
|
|
300
297
|
const day = this.getCurrentDay()
|
|
@@ -304,7 +301,9 @@ var WarmUp = class {
|
|
|
304
301
|
this.state.dailyCounts[day]++
|
|
305
302
|
this.state.lastActiveAt = now
|
|
306
303
|
}
|
|
307
|
-
|
|
304
|
+
/**
|
|
305
|
+
* Get current warm-up status
|
|
306
|
+
*/
|
|
308
307
|
getStatus() {
|
|
309
308
|
const day = this.getCurrentDay()
|
|
310
309
|
const todaySent = this.state.dailyCounts[day] || 0
|
|
@@ -318,11 +317,15 @@ var WarmUp = class {
|
|
|
318
317
|
progress: this.state.graduated ? 100 : Math.round((day / this.config.warmUpDays) * 100)
|
|
319
318
|
}
|
|
320
319
|
}
|
|
321
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Export state for persistence
|
|
322
|
+
*/
|
|
322
323
|
exportState() {
|
|
323
324
|
return { ...this.state }
|
|
324
325
|
}
|
|
325
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Reset warm-up (e.g., after detected ban risk)
|
|
328
|
+
*/
|
|
326
329
|
reset() {
|
|
327
330
|
this.state = this.freshState()
|
|
328
331
|
}
|
|
@@ -347,7 +350,7 @@ var WarmUp = class {
|
|
|
347
350
|
}
|
|
348
351
|
}
|
|
349
352
|
|
|
350
|
-
|
|
353
|
+
// health.js
|
|
351
354
|
var DEFAULT_CONFIG3 = {
|
|
352
355
|
disconnectWarningThreshold: 3,
|
|
353
356
|
disconnectCriticalThreshold: 5,
|
|
@@ -365,7 +368,9 @@ var HealthMonitor = class {
|
|
|
365
368
|
constructor(config = {}) {
|
|
366
369
|
this.config = { ...DEFAULT_CONFIG3, ...config }
|
|
367
370
|
}
|
|
368
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Record a disconnection event
|
|
373
|
+
*/
|
|
369
374
|
recordDisconnect(reason) {
|
|
370
375
|
const reasonStr = String(reason)
|
|
371
376
|
if (reasonStr === '403' || reasonStr === 'forbidden') {
|
|
@@ -387,25 +392,33 @@ var HealthMonitor = class {
|
|
|
387
392
|
}
|
|
388
393
|
this.checkAndNotify()
|
|
389
394
|
}
|
|
390
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Record a successful reconnection
|
|
397
|
+
*/
|
|
391
398
|
recordReconnect() {
|
|
392
399
|
this.events.push({ type: 'reconnect', timestamp: Date.now() })
|
|
393
400
|
}
|
|
394
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Record a failed message send
|
|
403
|
+
*/
|
|
395
404
|
recordMessageFailed(error) {
|
|
396
405
|
this.events.push({ type: 'messageFailed', timestamp: Date.now(), detail: error })
|
|
397
406
|
this.lastBadEventTime = Date.now()
|
|
398
407
|
this.lastEventWasSevere = false
|
|
399
408
|
this.checkAndNotify()
|
|
400
409
|
}
|
|
401
|
-
|
|
410
|
+
/**
|
|
411
|
+
* Record a 463 reachout timelock error
|
|
412
|
+
*/
|
|
402
413
|
recordReachoutTimelock(detail) {
|
|
403
414
|
this.events.push({ type: 'reachoutTimelocked', timestamp: Date.now(), detail })
|
|
404
415
|
this.lastBadEventTime = Date.now()
|
|
405
416
|
this.lastEventWasSevere = false
|
|
406
417
|
this.checkAndNotify()
|
|
407
418
|
}
|
|
408
|
-
|
|
419
|
+
/**
|
|
420
|
+
* Get current health status
|
|
421
|
+
*/
|
|
409
422
|
getStatus() {
|
|
410
423
|
const now = Date.now()
|
|
411
424
|
this.cleanup(now)
|
|
@@ -481,18 +494,24 @@ var HealthMonitor = class {
|
|
|
481
494
|
}
|
|
482
495
|
}
|
|
483
496
|
}
|
|
484
|
-
|
|
497
|
+
/**
|
|
498
|
+
* Check if sending should be paused
|
|
499
|
+
*/
|
|
485
500
|
isPaused() {
|
|
486
501
|
if (this.paused) return true
|
|
487
502
|
const status = this.getStatus()
|
|
488
503
|
const riskOrder = ['low', 'medium', 'high', 'critical']
|
|
489
504
|
return riskOrder.indexOf(status.risk) >= riskOrder.indexOf(this.config.autoPauseAt)
|
|
490
505
|
}
|
|
491
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Manually pause/resume
|
|
508
|
+
*/
|
|
492
509
|
setPaused(paused) {
|
|
493
510
|
this.paused = paused
|
|
494
511
|
}
|
|
495
|
-
|
|
512
|
+
/**
|
|
513
|
+
* Reset all tracked events
|
|
514
|
+
*/
|
|
496
515
|
reset() {
|
|
497
516
|
this.events = []
|
|
498
517
|
this.startTime = Date.now()
|
|
@@ -513,7 +532,7 @@ var HealthMonitor = class {
|
|
|
513
532
|
}
|
|
514
533
|
}
|
|
515
534
|
|
|
516
|
-
|
|
535
|
+
// timelockGuard.js
|
|
517
536
|
var DEFAULT_CONFIG4 = {
|
|
518
537
|
resumeBufferMs: 1e4
|
|
519
538
|
}
|
|
@@ -523,21 +542,23 @@ var TimelockGuard = class {
|
|
|
523
542
|
isActive: false,
|
|
524
543
|
errorCount: 0
|
|
525
544
|
}
|
|
526
|
-
knownChats =
|
|
545
|
+
knownChats = /* @__PURE__ */ new Set()
|
|
527
546
|
resumeTimer = null
|
|
528
547
|
timerGeneration = 0
|
|
529
|
-
|
|
548
|
+
// BUG FIX 4: Track timer validity to prevent race conditions
|
|
530
549
|
constructor(config = {}) {
|
|
531
550
|
this.config = { ...DEFAULT_CONFIG4, ...config }
|
|
532
551
|
}
|
|
533
|
-
|
|
552
|
+
/**
|
|
553
|
+
* Update timelock state from Baileys connection.update event
|
|
554
|
+
*/
|
|
534
555
|
onTimelockUpdate(data) {
|
|
535
556
|
const wasActive = this.state.isActive
|
|
536
557
|
this.state.isActive = !!data.isActive
|
|
537
558
|
this.state.enforcementType = data.enforcementType
|
|
538
559
|
this.state.expiresAt = data.timeEnforcementEnds
|
|
539
560
|
if (this.state.isActive && !wasActive) {
|
|
540
|
-
this.state.detectedAt =
|
|
561
|
+
this.state.detectedAt = /* @__PURE__ */ new Date()
|
|
541
562
|
this.state.errorCount = 0
|
|
542
563
|
this.config.onTimelockDetected?.(this.getState())
|
|
543
564
|
this.scheduleResume()
|
|
@@ -549,28 +570,36 @@ var TimelockGuard = class {
|
|
|
549
570
|
this.config.onTimelockLifted?.(this.getState())
|
|
550
571
|
}
|
|
551
572
|
}
|
|
552
|
-
|
|
573
|
+
/**
|
|
574
|
+
* Record a 463 error from a failed send
|
|
575
|
+
*/
|
|
553
576
|
record463Error() {
|
|
554
577
|
this.state.errorCount++
|
|
555
578
|
if (!this.state.isActive) {
|
|
556
579
|
this.state.isActive = true
|
|
557
|
-
this.state.detectedAt =
|
|
580
|
+
this.state.detectedAt = /* @__PURE__ */ new Date()
|
|
558
581
|
this.state.expiresAt = new Date(Date.now() + 6e4)
|
|
559
582
|
this.config.onTimelockDetected?.(this.getState())
|
|
560
583
|
this.scheduleResume()
|
|
561
584
|
}
|
|
562
585
|
}
|
|
563
|
-
|
|
586
|
+
/**
|
|
587
|
+
* Register a JID as a known/existing chat (has tctoken / prior history)
|
|
588
|
+
*/
|
|
564
589
|
registerKnownChat(jid) {
|
|
565
590
|
this.knownChats.add(jid)
|
|
566
591
|
}
|
|
567
|
-
|
|
592
|
+
/**
|
|
593
|
+
* Register multiple known chats at once (e.g. from chat list on connect)
|
|
594
|
+
*/
|
|
568
595
|
registerKnownChats(jids) {
|
|
569
596
|
for (const jid of jids) {
|
|
570
597
|
this.knownChats.add(jid)
|
|
571
598
|
}
|
|
572
599
|
}
|
|
573
|
-
|
|
600
|
+
/**
|
|
601
|
+
* Check if a message to this recipient should be allowed
|
|
602
|
+
*/
|
|
574
603
|
canSend(jid) {
|
|
575
604
|
if (!this.state.isActive) {
|
|
576
605
|
return { allowed: true }
|
|
@@ -594,11 +623,15 @@ var TimelockGuard = class {
|
|
|
594
623
|
reason: `Reachout timelocked (${this.state.enforcementType || 'unknown'}). New contacts blocked. Expires in ${Math.ceil(expiresIn / 1e3)}s.`
|
|
595
624
|
}
|
|
596
625
|
}
|
|
597
|
-
|
|
626
|
+
/**
|
|
627
|
+
* Get current timelock state
|
|
628
|
+
*/
|
|
598
629
|
getState() {
|
|
599
630
|
return { ...this.state }
|
|
600
631
|
}
|
|
601
|
-
|
|
632
|
+
/**
|
|
633
|
+
* Check if currently timelocked
|
|
634
|
+
*/
|
|
602
635
|
isTimelocked() {
|
|
603
636
|
if (!this.state.isActive) return false
|
|
604
637
|
if (this.state.expiresAt) {
|
|
@@ -610,11 +643,15 @@ var TimelockGuard = class {
|
|
|
610
643
|
}
|
|
611
644
|
return true
|
|
612
645
|
}
|
|
613
|
-
|
|
646
|
+
/**
|
|
647
|
+
* Get the set of known chat JIDs
|
|
648
|
+
*/
|
|
614
649
|
getKnownChats() {
|
|
615
650
|
return new Set(this.knownChats)
|
|
616
651
|
}
|
|
617
|
-
|
|
652
|
+
/**
|
|
653
|
+
* Manually lift the timelock
|
|
654
|
+
*/
|
|
618
655
|
lift() {
|
|
619
656
|
if (this.state.isActive) {
|
|
620
657
|
this.state.isActive = false
|
|
@@ -622,7 +659,9 @@ var TimelockGuard = class {
|
|
|
622
659
|
this.config.onTimelockLifted?.(this.getState())
|
|
623
660
|
}
|
|
624
661
|
}
|
|
625
|
-
|
|
662
|
+
/**
|
|
663
|
+
* Reset all state
|
|
664
|
+
*/
|
|
626
665
|
reset() {
|
|
627
666
|
this.state = { isActive: false, errorCount: 0 }
|
|
628
667
|
this.knownChats.clear()
|
|
@@ -652,7 +691,7 @@ var TimelockGuard = class {
|
|
|
652
691
|
}
|
|
653
692
|
}
|
|
654
693
|
|
|
655
|
-
|
|
694
|
+
// replyRatio.js
|
|
656
695
|
var DEFAULT_CONFIG5 = {
|
|
657
696
|
enabled: false,
|
|
658
697
|
minRatio: 0.1,
|
|
@@ -664,11 +703,14 @@ var DEFAULT_CONFIG5 = {
|
|
|
664
703
|
}
|
|
665
704
|
var ReplyRatioGuard = class {
|
|
666
705
|
config
|
|
667
|
-
contacts =
|
|
706
|
+
contacts = /* @__PURE__ */ new Map()
|
|
668
707
|
constructor(config = {}) {
|
|
669
708
|
this.config = { ...DEFAULT_CONFIG5, ...config }
|
|
670
709
|
}
|
|
671
|
-
|
|
710
|
+
/**
|
|
711
|
+
* Check if message can be sent to this contact based on reply ratio.
|
|
712
|
+
* Call before sending.
|
|
713
|
+
*/
|
|
672
714
|
beforeSend(jid) {
|
|
673
715
|
if (!this.config.enabled) {
|
|
674
716
|
return { allowed: true }
|
|
@@ -699,14 +741,18 @@ var ReplyRatioGuard = class {
|
|
|
699
741
|
}
|
|
700
742
|
return { allowed: true }
|
|
701
743
|
}
|
|
702
|
-
|
|
744
|
+
/**
|
|
745
|
+
* Record an outbound message sent to this contact.
|
|
746
|
+
*/
|
|
703
747
|
recordSent(jid) {
|
|
704
748
|
if (!this.config.enabled) return
|
|
705
749
|
const record = this.contacts.get(jid) || { sent: 0, received: 0 }
|
|
706
750
|
record.sent++
|
|
707
751
|
this.contacts.set(jid, record)
|
|
708
752
|
}
|
|
709
|
-
|
|
753
|
+
/**
|
|
754
|
+
* Record an inbound message received from this contact.
|
|
755
|
+
*/
|
|
710
756
|
recordReceived(jid) {
|
|
711
757
|
if (!this.config.enabled) return
|
|
712
758
|
const record = this.contacts.get(jid) || { sent: 0, received: 0 }
|
|
@@ -714,7 +760,11 @@ var ReplyRatioGuard = class {
|
|
|
714
760
|
delete record.cooledUntil
|
|
715
761
|
this.contacts.set(jid, record)
|
|
716
762
|
}
|
|
717
|
-
|
|
763
|
+
/**
|
|
764
|
+
* Suggest whether to send an auto-reply to this incoming message.
|
|
765
|
+
* Returns { shouldReply: true, suggestedText: '👍' } if probability check passes.
|
|
766
|
+
* Caller is responsible for actually sending the message.
|
|
767
|
+
*/
|
|
718
768
|
suggestReply(jid, _msgText) {
|
|
719
769
|
if (!this.config.enabled) {
|
|
720
770
|
return { shouldReply: false }
|
|
@@ -729,7 +779,9 @@ var ReplyRatioGuard = class {
|
|
|
729
779
|
}
|
|
730
780
|
return { shouldReply: false }
|
|
731
781
|
}
|
|
732
|
-
|
|
782
|
+
/**
|
|
783
|
+
* Get statistics for all contacts and global metrics.
|
|
784
|
+
*/
|
|
733
785
|
getStats() {
|
|
734
786
|
const perContact = Array.from(this.contacts.entries()).map(([jid, record]) => ({
|
|
735
787
|
jid,
|
|
@@ -750,49 +802,60 @@ var ReplyRatioGuard = class {
|
|
|
750
802
|
contactsOnCooldown
|
|
751
803
|
}
|
|
752
804
|
}
|
|
753
|
-
|
|
805
|
+
/**
|
|
806
|
+
* Reset all counters.
|
|
807
|
+
*/
|
|
754
808
|
reset() {
|
|
755
809
|
this.contacts.clear()
|
|
756
810
|
}
|
|
757
|
-
|
|
811
|
+
/**
|
|
812
|
+
* Export state for persistence.
|
|
813
|
+
*/
|
|
758
814
|
exportState() {
|
|
759
815
|
return {
|
|
760
816
|
contacts: Array.from(this.contacts.entries())
|
|
761
817
|
}
|
|
762
818
|
}
|
|
763
|
-
|
|
819
|
+
/**
|
|
820
|
+
* Restore state from persistence.
|
|
821
|
+
*/
|
|
764
822
|
restoreState(state) {
|
|
765
823
|
if (state?.contacts && Array.isArray(state.contacts)) {
|
|
766
824
|
this.contacts = new Map(state.contacts)
|
|
767
825
|
}
|
|
768
826
|
}
|
|
769
|
-
|
|
827
|
+
/**
|
|
828
|
+
* Check if JID is a group.
|
|
829
|
+
*/
|
|
770
830
|
isGroup(jid) {
|
|
771
831
|
return jid.endsWith('@g.us')
|
|
772
832
|
}
|
|
773
833
|
}
|
|
774
834
|
|
|
775
|
-
|
|
835
|
+
// contactGraph.js
|
|
776
836
|
var DEFAULT_CONFIG6 = {
|
|
777
837
|
enabled: false,
|
|
778
838
|
requireHandshakeBeforeGroupSend: true,
|
|
779
839
|
handshakeMinDelayMs: 36e5,
|
|
780
|
-
|
|
840
|
+
// 1 hour
|
|
781
841
|
groupLurkPeriodMs: 432e5,
|
|
782
|
-
|
|
842
|
+
// 12 hours
|
|
783
843
|
maxStrangerMessagesPerDay: 5,
|
|
784
844
|
autoRegisterOnIncoming: true
|
|
785
845
|
}
|
|
786
846
|
var ContactGraphWarmer = class {
|
|
787
847
|
config
|
|
788
|
-
contacts =
|
|
789
|
-
groups =
|
|
848
|
+
contacts = /* @__PURE__ */ new Map()
|
|
849
|
+
groups = /* @__PURE__ */ new Map()
|
|
790
850
|
strangerMessagesToday = 0
|
|
791
851
|
lastStrangerResetDay = this.getCurrentDay()
|
|
792
852
|
constructor(config = {}) {
|
|
793
853
|
this.config = { ...DEFAULT_CONFIG6, ...config }
|
|
794
854
|
}
|
|
795
|
-
|
|
855
|
+
/**
|
|
856
|
+
* Check if message can be sent to this contact/group.
|
|
857
|
+
* Returns { allowed: false, needsHandshake: true } if handshake required.
|
|
858
|
+
*/
|
|
796
859
|
canMessage(jid) {
|
|
797
860
|
if (!this.config.enabled) {
|
|
798
861
|
return { allowed: true }
|
|
@@ -807,7 +870,9 @@ var ContactGraphWarmer = class {
|
|
|
807
870
|
}
|
|
808
871
|
return this.checkIndividualMessage(jid)
|
|
809
872
|
}
|
|
810
|
-
|
|
873
|
+
/**
|
|
874
|
+
* Mark handshake as sent to this contact.
|
|
875
|
+
*/
|
|
811
876
|
markHandshakeSent(jid) {
|
|
812
877
|
if (!this.config.enabled) return
|
|
813
878
|
if (this.isGroup(jid)) return
|
|
@@ -816,7 +881,9 @@ var ContactGraphWarmer = class {
|
|
|
816
881
|
record.handshakeSentAt = Date.now()
|
|
817
882
|
this.contacts.set(jid, record)
|
|
818
883
|
}
|
|
819
|
-
|
|
884
|
+
/**
|
|
885
|
+
* Mark handshake as complete with this contact.
|
|
886
|
+
*/
|
|
820
887
|
markHandshakeComplete(jid) {
|
|
821
888
|
if (!this.config.enabled) return
|
|
822
889
|
if (this.isGroup(jid)) return
|
|
@@ -824,7 +891,9 @@ var ContactGraphWarmer = class {
|
|
|
824
891
|
record.state = 'handshake_complete'
|
|
825
892
|
this.contacts.set(jid, record)
|
|
826
893
|
}
|
|
827
|
-
|
|
894
|
+
/**
|
|
895
|
+
* Register a contact as known (skip handshake requirement).
|
|
896
|
+
*/
|
|
828
897
|
registerKnownContact(jid) {
|
|
829
898
|
if (!this.config.enabled) return
|
|
830
899
|
if (this.isGroup(jid)) return
|
|
@@ -832,18 +901,24 @@ var ContactGraphWarmer = class {
|
|
|
832
901
|
record.state = 'known'
|
|
833
902
|
this.contacts.set(jid, record)
|
|
834
903
|
}
|
|
835
|
-
|
|
904
|
+
/**
|
|
905
|
+
* Register a group join event.
|
|
906
|
+
*/
|
|
836
907
|
registerGroupJoin(groupJid) {
|
|
837
908
|
if (!this.config.enabled) return
|
|
838
909
|
if (!this.isGroup(groupJid)) return
|
|
839
910
|
this.groups.set(groupJid, { joinedAt: Date.now() })
|
|
840
911
|
}
|
|
841
|
-
|
|
912
|
+
/**
|
|
913
|
+
* Get contact state.
|
|
914
|
+
*/
|
|
842
915
|
getContactState(jid) {
|
|
843
916
|
if (this.isGroup(jid)) return 'known'
|
|
844
917
|
return this.contacts.get(jid)?.state || 'stranger'
|
|
845
918
|
}
|
|
846
|
-
|
|
919
|
+
/**
|
|
920
|
+
* Handle incoming message — auto-register if enabled.
|
|
921
|
+
*/
|
|
847
922
|
onIncomingMessage(jid) {
|
|
848
923
|
if (!this.config.enabled) return
|
|
849
924
|
if (this.isGroup(jid)) return
|
|
@@ -851,7 +926,9 @@ var ContactGraphWarmer = class {
|
|
|
851
926
|
this.registerKnownContact(jid)
|
|
852
927
|
}
|
|
853
928
|
}
|
|
854
|
-
|
|
929
|
+
/**
|
|
930
|
+
* Get statistics.
|
|
931
|
+
*/
|
|
855
932
|
getStats() {
|
|
856
933
|
const knownContacts = Array.from(this.contacts.values()).filter(c => c.state === 'known').length
|
|
857
934
|
const pendingHandshakes = Array.from(this.contacts.values()).filter(c => c.state === 'handshake_sent').length
|
|
@@ -867,14 +944,18 @@ var ContactGraphWarmer = class {
|
|
|
867
944
|
groupsJoined
|
|
868
945
|
}
|
|
869
946
|
}
|
|
870
|
-
|
|
947
|
+
/**
|
|
948
|
+
* Reset all state.
|
|
949
|
+
*/
|
|
871
950
|
reset() {
|
|
872
951
|
this.contacts.clear()
|
|
873
952
|
this.groups.clear()
|
|
874
953
|
this.strangerMessagesToday = 0
|
|
875
954
|
this.lastStrangerResetDay = this.getCurrentDay()
|
|
876
955
|
}
|
|
877
|
-
|
|
956
|
+
/**
|
|
957
|
+
* Export state for persistence.
|
|
958
|
+
*/
|
|
878
959
|
exportState() {
|
|
879
960
|
return {
|
|
880
961
|
contacts: Array.from(this.contacts.entries()),
|
|
@@ -883,7 +964,9 @@ var ContactGraphWarmer = class {
|
|
|
883
964
|
lastStrangerResetDay: this.lastStrangerResetDay
|
|
884
965
|
}
|
|
885
966
|
}
|
|
886
|
-
|
|
967
|
+
/**
|
|
968
|
+
* Restore state from persistence.
|
|
969
|
+
*/
|
|
887
970
|
restoreState(state) {
|
|
888
971
|
if (state?.contacts && Array.isArray(state.contacts)) {
|
|
889
972
|
this.contacts = new Map(state.contacts)
|
|
@@ -898,7 +981,7 @@ var ContactGraphWarmer = class {
|
|
|
898
981
|
this.lastStrangerResetDay = state.lastStrangerResetDay
|
|
899
982
|
}
|
|
900
983
|
}
|
|
901
|
-
|
|
984
|
+
// Private helpers
|
|
902
985
|
isGroup(jid) {
|
|
903
986
|
return jid.endsWith('@g.us')
|
|
904
987
|
}
|
|
@@ -952,7 +1035,7 @@ var ContactGraphWarmer = class {
|
|
|
952
1035
|
}
|
|
953
1036
|
}
|
|
954
1037
|
|
|
955
|
-
|
|
1038
|
+
// presenceChoreographer.js
|
|
956
1039
|
var DEFAULT_CONFIG7 = {
|
|
957
1040
|
enabled: false,
|
|
958
1041
|
enableCircadianRhythm: true,
|
|
@@ -985,60 +1068,60 @@ var DEFAULT_CONFIG7 = {
|
|
|
985
1068
|
var ACTIVITY_CURVES = {
|
|
986
1069
|
office: [
|
|
987
1070
|
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
|
|
988
|
-
|
|
1071
|
+
// 0-7: night quiet
|
|
989
1072
|
0.5, 0.5,
|
|
990
|
-
|
|
1073
|
+
// 8-9: morning ramp
|
|
991
1074
|
0.95, 0.95,
|
|
992
|
-
|
|
1075
|
+
// 10-11: morning peak
|
|
993
1076
|
0.6,
|
|
994
|
-
|
|
1077
|
+
// 12: lunch dip
|
|
995
1078
|
0.9, 0.9, 0.9, 0.9,
|
|
996
|
-
|
|
1079
|
+
// 13-16: afternoon
|
|
997
1080
|
0.6, 0.6,
|
|
998
|
-
|
|
1081
|
+
// 17-18: wind-down
|
|
999
1082
|
0.4, 0.4,
|
|
1000
|
-
|
|
1083
|
+
// 19-20: evening
|
|
1001
1084
|
0.2, 0.2, 0.2, 0.2
|
|
1002
|
-
|
|
1085
|
+
// 21-24: taper
|
|
1003
1086
|
],
|
|
1004
1087
|
social: [
|
|
1005
1088
|
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
|
|
1006
|
-
|
|
1089
|
+
// 0-7: night quiet
|
|
1007
1090
|
0.3, 0.4,
|
|
1008
|
-
|
|
1091
|
+
// 8-9: slow start
|
|
1009
1092
|
0.7, 0.8,
|
|
1010
|
-
|
|
1093
|
+
// 10-11: ramp up
|
|
1011
1094
|
0.5,
|
|
1012
|
-
|
|
1095
|
+
// 12: lunch
|
|
1013
1096
|
0.7, 0.7,
|
|
1014
|
-
|
|
1097
|
+
// 13-14: afternoon
|
|
1015
1098
|
0.4,
|
|
1016
|
-
|
|
1099
|
+
// 15: tea time dip
|
|
1017
1100
|
0.8, 0.9, 0.9,
|
|
1018
|
-
|
|
1101
|
+
// 16-18: active
|
|
1019
1102
|
0.6,
|
|
1020
|
-
|
|
1103
|
+
// 19: dinner dip
|
|
1021
1104
|
0.8, 0.85, 0.9, 0.95, 1
|
|
1022
|
-
|
|
1105
|
+
// 20-24: evening peak
|
|
1023
1106
|
],
|
|
1024
1107
|
global: [
|
|
1025
1108
|
0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
|
|
1026
|
-
|
|
1109
|
+
// 0-5: night
|
|
1027
1110
|
0.4, 0.4,
|
|
1028
|
-
|
|
1111
|
+
// 6-7: dawn dip
|
|
1029
1112
|
0.6, 0.7, 0.8, 0.8,
|
|
1030
|
-
|
|
1113
|
+
// 8-11: morning
|
|
1031
1114
|
0.6,
|
|
1032
|
-
|
|
1115
|
+
// 12: lunch
|
|
1033
1116
|
0.8, 0.8, 0.8, 0.8,
|
|
1034
|
-
|
|
1117
|
+
// 13-16: afternoon
|
|
1035
1118
|
0.7, 0.7,
|
|
1036
|
-
|
|
1119
|
+
// 17-18: evening
|
|
1037
1120
|
0.6, 0.5, 0.5, 0.5, 0.5, 0.5
|
|
1038
|
-
|
|
1121
|
+
// 19-24: night taper
|
|
1039
1122
|
]
|
|
1040
1123
|
}
|
|
1041
|
-
function getCircadianMultiplier(date =
|
|
1124
|
+
function getCircadianMultiplier(date = /* @__PURE__ */ new Date(), profile = 'default', timezone) {
|
|
1042
1125
|
if (profile === 'always_on') {
|
|
1043
1126
|
return 1
|
|
1044
1127
|
}
|
|
@@ -1103,7 +1186,11 @@ var PresenceChoreographer = class {
|
|
|
1103
1186
|
}
|
|
1104
1187
|
}
|
|
1105
1188
|
}
|
|
1106
|
-
|
|
1189
|
+
/**
|
|
1190
|
+
* Get current activity factor (0.1 to 1.0).
|
|
1191
|
+
* Higher = more active = shorter delays.
|
|
1192
|
+
* If circadian disabled, returns 1.0.
|
|
1193
|
+
*/
|
|
1107
1194
|
getCurrentActivityFactor() {
|
|
1108
1195
|
if (!this.config.enabled || !this.config.enableCircadianRhythm) {
|
|
1109
1196
|
return 1
|
|
@@ -1112,7 +1199,10 @@ var PresenceChoreographer = class {
|
|
|
1112
1199
|
const curve = ACTIVITY_CURVES[this.config.activityCurve]
|
|
1113
1200
|
return curve[hour] || 0.5
|
|
1114
1201
|
}
|
|
1115
|
-
|
|
1202
|
+
/**
|
|
1203
|
+
* Check if should pause for distraction.
|
|
1204
|
+
* Returns { pause: true, durationMs: 600000 } if probability check passes.
|
|
1205
|
+
*/
|
|
1116
1206
|
shouldPauseForDistraction() {
|
|
1117
1207
|
if (!this.config.enabled) {
|
|
1118
1208
|
return { pause: false, durationMs: 0 }
|
|
@@ -1124,7 +1214,10 @@ var PresenceChoreographer = class {
|
|
|
1124
1214
|
}
|
|
1125
1215
|
return { pause: false, durationMs: 0 }
|
|
1126
1216
|
}
|
|
1127
|
-
|
|
1217
|
+
/**
|
|
1218
|
+
* Check if should take offline gap.
|
|
1219
|
+
* Returns { offline: true, durationMs: 600000 } if probability check passes.
|
|
1220
|
+
*/
|
|
1128
1221
|
shouldTakeOfflineGap() {
|
|
1129
1222
|
if (!this.config.enabled) {
|
|
1130
1223
|
return { offline: false, durationMs: 0 }
|
|
@@ -1136,7 +1229,12 @@ var PresenceChoreographer = class {
|
|
|
1136
1229
|
}
|
|
1137
1230
|
return { offline: false, durationMs: 0 }
|
|
1138
1231
|
}
|
|
1139
|
-
|
|
1232
|
+
/**
|
|
1233
|
+
* Check if should mark message as read.
|
|
1234
|
+
* Returns { mark: false } if skip probability hit.
|
|
1235
|
+
* Returns { mark: true, delayMs: 5000 } otherwise.
|
|
1236
|
+
* Applies circadian multiplier to delay.
|
|
1237
|
+
*/
|
|
1140
1238
|
shouldMarkRead() {
|
|
1141
1239
|
if (!this.config.enabled) {
|
|
1142
1240
|
return { mark: true, delayMs: 0 }
|
|
@@ -1149,7 +1247,7 @@ var PresenceChoreographer = class {
|
|
|
1149
1247
|
let delayMs = baseDelayMs
|
|
1150
1248
|
if (this.config.circadian.enabled) {
|
|
1151
1249
|
const circadianMultiplier = getCircadianMultiplier(
|
|
1152
|
-
|
|
1250
|
+
/* @__PURE__ */ new Date(),
|
|
1153
1251
|
this.config.circadian.profile,
|
|
1154
1252
|
this.config.circadian.timezone
|
|
1155
1253
|
)
|
|
@@ -1158,7 +1256,18 @@ var PresenceChoreographer = class {
|
|
|
1158
1256
|
this.stats.readReceiptsDelayed++
|
|
1159
1257
|
return { mark: true, delayMs }
|
|
1160
1258
|
}
|
|
1161
|
-
|
|
1259
|
+
/**
|
|
1260
|
+
* Compute realistic typing duration for a message of given length.
|
|
1261
|
+
* Includes Gaussian WPM variance + think-pause injection + circadian timing multiplier.
|
|
1262
|
+
* Returns a "typing plan": array of { state, durationMs } steps the caller should execute sequentially.
|
|
1263
|
+
*
|
|
1264
|
+
* plan = [
|
|
1265
|
+
* { state: 'composing', durationMs: 4200 },
|
|
1266
|
+
* { state: 'paused', durationMs: 950 }, // think pause
|
|
1267
|
+
* { state: 'composing', durationMs: 6800 },
|
|
1268
|
+
* { state: 'paused', durationMs: 600 }, // brief stop before send
|
|
1269
|
+
* ]
|
|
1270
|
+
*/
|
|
1162
1271
|
computeTypingPlan(messageLength) {
|
|
1163
1272
|
if (!this.config.enabled || !this.config.enableTypingModel) {
|
|
1164
1273
|
return [{ state: 'composing', durationMs: this.config.typingMinMs }]
|
|
@@ -1173,7 +1282,7 @@ var PresenceChoreographer = class {
|
|
|
1173
1282
|
let circadianMultiplier = 1
|
|
1174
1283
|
if (this.config.circadian.enabled) {
|
|
1175
1284
|
circadianMultiplier = getCircadianMultiplier(
|
|
1176
|
-
|
|
1285
|
+
/* @__PURE__ */ new Date(),
|
|
1177
1286
|
this.config.circadian.profile,
|
|
1178
1287
|
this.config.circadian.timezone
|
|
1179
1288
|
)
|
|
@@ -1216,7 +1325,13 @@ var PresenceChoreographer = class {
|
|
|
1216
1325
|
}
|
|
1217
1326
|
return plan
|
|
1218
1327
|
}
|
|
1219
|
-
|
|
1328
|
+
/**
|
|
1329
|
+
* Execute a typing plan against a Baileys-shaped sock with sendPresenceUpdate(state, jid).
|
|
1330
|
+
* Awaits each step's duration. Updates stats.
|
|
1331
|
+
*
|
|
1332
|
+
* await choreo.executeTypingPlan(sock, jid, plan);
|
|
1333
|
+
* await sock.sendMessage(jid, content);
|
|
1334
|
+
*/
|
|
1220
1335
|
async executeTypingPlan(sock, jid, plan, options) {
|
|
1221
1336
|
this.stats.typingPlansExecuted++
|
|
1222
1337
|
for (const step of plan) {
|
|
@@ -1229,7 +1344,9 @@ var PresenceChoreographer = class {
|
|
|
1229
1344
|
this.stats.totalTypingTimeMs += step.durationMs
|
|
1230
1345
|
}
|
|
1231
1346
|
}
|
|
1232
|
-
|
|
1347
|
+
/**
|
|
1348
|
+
* Get statistics.
|
|
1349
|
+
*/
|
|
1233
1350
|
getStats() {
|
|
1234
1351
|
return {
|
|
1235
1352
|
currentActivityFactor: this.getCurrentActivityFactor(),
|
|
@@ -1243,7 +1360,9 @@ var PresenceChoreographer = class {
|
|
|
1243
1360
|
totalTypingTimeMs: this.stats.totalTypingTimeMs
|
|
1244
1361
|
}
|
|
1245
1362
|
}
|
|
1246
|
-
|
|
1363
|
+
/**
|
|
1364
|
+
* Reset statistics.
|
|
1365
|
+
*/
|
|
1247
1366
|
reset() {
|
|
1248
1367
|
this.stats = {
|
|
1249
1368
|
distractionPausesInjected: 0,
|
|
@@ -1255,7 +1374,7 @@ var PresenceChoreographer = class {
|
|
|
1255
1374
|
totalTypingTimeMs: 0
|
|
1256
1375
|
}
|
|
1257
1376
|
}
|
|
1258
|
-
|
|
1377
|
+
// Private helpers
|
|
1259
1378
|
getLocalHour() {
|
|
1260
1379
|
try {
|
|
1261
1380
|
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
@@ -1263,13 +1382,13 @@ var PresenceChoreographer = class {
|
|
|
1263
1382
|
hour: 'numeric',
|
|
1264
1383
|
hour12: false
|
|
1265
1384
|
})
|
|
1266
|
-
const parts = formatter.formatToParts( new Date())
|
|
1385
|
+
const parts = formatter.formatToParts(/* @__PURE__ */ new Date())
|
|
1267
1386
|
const hourPart = parts.find(p => p.type === 'hour')
|
|
1268
1387
|
if (hourPart) {
|
|
1269
1388
|
return parseInt(hourPart.value, 10)
|
|
1270
1389
|
}
|
|
1271
1390
|
} catch (error) {}
|
|
1272
|
-
return
|
|
1391
|
+
return /* @__PURE__ */ new Date().getUTCHours()
|
|
1273
1392
|
}
|
|
1274
1393
|
randomBetween(min, max) {
|
|
1275
1394
|
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
@@ -1277,7 +1396,10 @@ var PresenceChoreographer = class {
|
|
|
1277
1396
|
clamp(value, min, max) {
|
|
1278
1397
|
return Math.max(min, Math.min(max, value))
|
|
1279
1398
|
}
|
|
1280
|
-
|
|
1399
|
+
/**
|
|
1400
|
+
* Generate Gaussian sample using Box-Muller transform.
|
|
1401
|
+
* Returns a sample from N(mean, stdDev).
|
|
1402
|
+
*/
|
|
1281
1403
|
gaussianSample(mean, stdDev) {
|
|
1282
1404
|
const u1 = Math.random()
|
|
1283
1405
|
const u2 = Math.random()
|
|
@@ -1289,7 +1411,7 @@ var PresenceChoreographer = class {
|
|
|
1289
1411
|
}
|
|
1290
1412
|
}
|
|
1291
1413
|
|
|
1292
|
-
|
|
1414
|
+
// retryTracker.js
|
|
1293
1415
|
var DEFAULT_CONFIG8 = {
|
|
1294
1416
|
enabled: false,
|
|
1295
1417
|
maxRetries: 5,
|
|
@@ -1298,7 +1420,7 @@ var DEFAULT_CONFIG8 = {
|
|
|
1298
1420
|
}
|
|
1299
1421
|
var RetryReasonTracker = class {
|
|
1300
1422
|
config
|
|
1301
|
-
retries =
|
|
1423
|
+
retries = /* @__PURE__ */ new Map()
|
|
1302
1424
|
totalRetries = 0
|
|
1303
1425
|
reasonCounts = {
|
|
1304
1426
|
no_session: 0,
|
|
@@ -1316,7 +1438,10 @@ var RetryReasonTracker = class {
|
|
|
1316
1438
|
constructor(config) {
|
|
1317
1439
|
this.config = { ...DEFAULT_CONFIG8, ...config }
|
|
1318
1440
|
}
|
|
1319
|
-
|
|
1441
|
+
/**
|
|
1442
|
+
* Call when a messages.update event arrives with a status/error.
|
|
1443
|
+
* Classifies and records the retry.
|
|
1444
|
+
*/
|
|
1320
1445
|
onMessageUpdate(update) {
|
|
1321
1446
|
if (!this.config.enabled) return
|
|
1322
1447
|
const msgId = update.key?.id
|
|
@@ -1325,7 +1450,9 @@ var RetryReasonTracker = class {
|
|
|
1325
1450
|
const reason = this.classify(update.error || update)
|
|
1326
1451
|
this.recordRetry(msgId, reason)
|
|
1327
1452
|
}
|
|
1328
|
-
|
|
1453
|
+
/**
|
|
1454
|
+
* Classify an arbitrary error object into a RetryReason
|
|
1455
|
+
*/
|
|
1329
1456
|
classify(err) {
|
|
1330
1457
|
if (!err) return 'unknown'
|
|
1331
1458
|
const statusCode = err.output?.statusCode || err.statusCode || err.status
|
|
@@ -1342,7 +1469,9 @@ var RetryReasonTracker = class {
|
|
|
1342
1469
|
if (errorMsg.includes('malformed') || errorMsg.includes('invalid node')) return 'node_malformed'
|
|
1343
1470
|
return 'unknown'
|
|
1344
1471
|
}
|
|
1345
|
-
|
|
1472
|
+
/**
|
|
1473
|
+
* Record a retry for a message
|
|
1474
|
+
*/
|
|
1346
1475
|
recordRetry(msgId, reason) {
|
|
1347
1476
|
const now = Date.now()
|
|
1348
1477
|
let record = this.retries.get(msgId)
|
|
@@ -1366,16 +1495,22 @@ var RetryReasonTracker = class {
|
|
|
1366
1495
|
this.config.onSpiral(msgId, reason)
|
|
1367
1496
|
}
|
|
1368
1497
|
}
|
|
1369
|
-
|
|
1498
|
+
/**
|
|
1499
|
+
* Should we warn the user this message is spiraling?
|
|
1500
|
+
*/
|
|
1370
1501
|
isSpiraling(msgId) {
|
|
1371
1502
|
const record = this.retries.get(msgId)
|
|
1372
1503
|
return record ? record.count >= this.config.spiralThreshold : false
|
|
1373
1504
|
}
|
|
1374
|
-
|
|
1505
|
+
/**
|
|
1506
|
+
* Reset counters for a specific message (call on successful delivery)
|
|
1507
|
+
*/
|
|
1375
1508
|
clear(msgId) {
|
|
1376
1509
|
this.retries.delete(msgId)
|
|
1377
1510
|
}
|
|
1378
|
-
|
|
1511
|
+
/**
|
|
1512
|
+
* Get current stats
|
|
1513
|
+
*/
|
|
1379
1514
|
getStats() {
|
|
1380
1515
|
return {
|
|
1381
1516
|
totalRetries: this.totalRetries,
|
|
@@ -1384,7 +1519,9 @@ var RetryReasonTracker = class {
|
|
|
1384
1519
|
activeRetries: this.retries.size
|
|
1385
1520
|
}
|
|
1386
1521
|
}
|
|
1387
|
-
|
|
1522
|
+
/**
|
|
1523
|
+
* Clean up old retry records (>5 minutes old)
|
|
1524
|
+
*/
|
|
1388
1525
|
cleanup() {
|
|
1389
1526
|
const now = Date.now()
|
|
1390
1527
|
const maxAge = 5 * 60 * 1e3
|
|
@@ -1394,14 +1531,16 @@ var RetryReasonTracker = class {
|
|
|
1394
1531
|
}
|
|
1395
1532
|
}
|
|
1396
1533
|
}
|
|
1397
|
-
|
|
1534
|
+
/**
|
|
1535
|
+
* Destroy and clean up
|
|
1536
|
+
*/
|
|
1398
1537
|
destroy() {
|
|
1399
1538
|
this.retries.clear()
|
|
1400
1539
|
this.cleanup()
|
|
1401
1540
|
}
|
|
1402
1541
|
}
|
|
1403
1542
|
|
|
1404
|
-
|
|
1543
|
+
// reconnectThrottle.js
|
|
1405
1544
|
var DEFAULT_CONFIG9 = {
|
|
1406
1545
|
enabled: false,
|
|
1407
1546
|
rampDurationMs: 6e4,
|
|
@@ -1416,11 +1555,11 @@ var PostReconnectThrottle = class {
|
|
|
1416
1555
|
lifetimeReconnects = 0
|
|
1417
1556
|
rampTimer = null
|
|
1418
1557
|
currentStep = 0
|
|
1419
|
-
|
|
1558
|
+
// Tracking sends in current window
|
|
1420
1559
|
sendsInCurrentWindow = 0
|
|
1421
1560
|
currentWindowStart = 0
|
|
1422
1561
|
WINDOW_DURATION_MS = 6e4
|
|
1423
|
-
|
|
1562
|
+
// 1 minute window
|
|
1424
1563
|
constructor(config) {
|
|
1425
1564
|
this.config = {
|
|
1426
1565
|
...DEFAULT_CONFIG9,
|
|
@@ -1428,7 +1567,9 @@ var PostReconnectThrottle = class {
|
|
|
1428
1567
|
baselineRatePerMinute: config?.baselineRatePerMinute || null
|
|
1429
1568
|
}
|
|
1430
1569
|
}
|
|
1431
|
-
|
|
1570
|
+
/**
|
|
1571
|
+
* Call when connection is re-established. Starts throttle window.
|
|
1572
|
+
*/
|
|
1432
1573
|
onReconnect() {
|
|
1433
1574
|
if (!this.config.enabled) return
|
|
1434
1575
|
this.throttledSince = Date.now()
|
|
@@ -1442,9 +1583,13 @@ var PostReconnectThrottle = class {
|
|
|
1442
1583
|
}
|
|
1443
1584
|
this.scheduleNextRampStep()
|
|
1444
1585
|
}
|
|
1445
|
-
|
|
1586
|
+
/**
|
|
1587
|
+
* Call when connection drops (optional — reset state).
|
|
1588
|
+
*/
|
|
1446
1589
|
onDisconnect() {}
|
|
1447
|
-
|
|
1590
|
+
/**
|
|
1591
|
+
* Schedule the next ramp step
|
|
1592
|
+
*/
|
|
1448
1593
|
scheduleNextRampStep() {
|
|
1449
1594
|
if (this.currentStep >= this.config.rampSteps) {
|
|
1450
1595
|
this.throttledSince = null
|
|
@@ -1457,7 +1602,9 @@ var PostReconnectThrottle = class {
|
|
|
1457
1602
|
this.scheduleNextRampStep()
|
|
1458
1603
|
}, stepDuration)
|
|
1459
1604
|
}
|
|
1460
|
-
|
|
1605
|
+
/**
|
|
1606
|
+
* Returns current rate multiplier (1.0 = no throttle)
|
|
1607
|
+
*/
|
|
1461
1608
|
getCurrentMultiplier() {
|
|
1462
1609
|
if (!this.config.enabled || !this.throttledSince) {
|
|
1463
1610
|
return 1
|
|
@@ -1470,7 +1617,9 @@ var PostReconnectThrottle = class {
|
|
|
1470
1617
|
const multiplier = this.config.initialRateMultiplier + (1 - this.config.initialRateMultiplier) * progress
|
|
1471
1618
|
return Math.min(1, multiplier)
|
|
1472
1619
|
}
|
|
1473
|
-
|
|
1620
|
+
/**
|
|
1621
|
+
* Checks if a send should be gated. Returns {allowed, reason, retryAfterMs?}
|
|
1622
|
+
*/
|
|
1474
1623
|
beforeSend() {
|
|
1475
1624
|
if (!this.config.enabled || !this.throttledSince) {
|
|
1476
1625
|
return { allowed: true }
|
|
@@ -1499,7 +1648,9 @@ var PostReconnectThrottle = class {
|
|
|
1499
1648
|
this.throttledSendCount++
|
|
1500
1649
|
return { allowed: true }
|
|
1501
1650
|
}
|
|
1502
|
-
|
|
1651
|
+
/**
|
|
1652
|
+
* Get current stats
|
|
1653
|
+
*/
|
|
1503
1654
|
getStats() {
|
|
1504
1655
|
const multiplier = this.getCurrentMultiplier()
|
|
1505
1656
|
const isThrottled = this.throttledSince !== null && multiplier < 1
|
|
@@ -1516,7 +1667,9 @@ var PostReconnectThrottle = class {
|
|
|
1516
1667
|
lifetimeReconnects: this.lifetimeReconnects
|
|
1517
1668
|
}
|
|
1518
1669
|
}
|
|
1519
|
-
|
|
1670
|
+
/**
|
|
1671
|
+
* Destroy and clean up timers
|
|
1672
|
+
*/
|
|
1520
1673
|
destroy() {
|
|
1521
1674
|
if (this.rampTimer) {
|
|
1522
1675
|
clearTimeout(this.rampTimer)
|
|
@@ -1526,7 +1679,7 @@ var PostReconnectThrottle = class {
|
|
|
1526
1679
|
}
|
|
1527
1680
|
}
|
|
1528
1681
|
|
|
1529
|
-
|
|
1682
|
+
// lidResolver.js
|
|
1530
1683
|
var DEFAULT_CONFIG10 = {
|
|
1531
1684
|
canonical: 'pn',
|
|
1532
1685
|
maxEntries: 1e4
|
|
@@ -1534,10 +1687,10 @@ var DEFAULT_CONFIG10 = {
|
|
|
1534
1687
|
var LidResolver = class {
|
|
1535
1688
|
config
|
|
1536
1689
|
persistence
|
|
1537
|
-
|
|
1538
|
-
lidToPn =
|
|
1539
|
-
pnToLid =
|
|
1540
|
-
|
|
1690
|
+
// Bidirectional maps: lid→pn and pn→lid
|
|
1691
|
+
lidToPn = /* @__PURE__ */ new Map()
|
|
1692
|
+
pnToLid = /* @__PURE__ */ new Map()
|
|
1693
|
+
// pn → lid (for quick reverse lookup)
|
|
1541
1694
|
stats = {
|
|
1542
1695
|
learnedFromEvents: 0,
|
|
1543
1696
|
lookupsServed: 0,
|
|
@@ -1550,7 +1703,10 @@ var LidResolver = class {
|
|
|
1550
1703
|
void this.hydrate()
|
|
1551
1704
|
}
|
|
1552
1705
|
}
|
|
1553
|
-
|
|
1706
|
+
/**
|
|
1707
|
+
* Learn from a message event. Idempotent.
|
|
1708
|
+
* Accepts partial mappings — will use whatever fields are available.
|
|
1709
|
+
*/
|
|
1554
1710
|
learn(mapping) {
|
|
1555
1711
|
let lid = mapping.lid ? this.normalizeJid(mapping.lid) : void 0
|
|
1556
1712
|
let pn = mapping.pn ? this.normalizeJid(mapping.pn) : void 0
|
|
@@ -1588,7 +1744,10 @@ var LidResolver = class {
|
|
|
1588
1744
|
void this.flush()
|
|
1589
1745
|
}
|
|
1590
1746
|
}
|
|
1591
|
-
|
|
1747
|
+
/**
|
|
1748
|
+
* Given any form (LID or PN), return the canonical form.
|
|
1749
|
+
* Falls back to input if unknown (no throw).
|
|
1750
|
+
*/
|
|
1592
1751
|
resolveCanonical(jid) {
|
|
1593
1752
|
const normalized = this.normalizeJid(jid)
|
|
1594
1753
|
if (this.config.canonical === 'pn') {
|
|
@@ -1622,7 +1781,9 @@ var LidResolver = class {
|
|
|
1622
1781
|
return normalized
|
|
1623
1782
|
}
|
|
1624
1783
|
}
|
|
1625
|
-
|
|
1784
|
+
/**
|
|
1785
|
+
* Lookup partner form. Returns null if unknown.
|
|
1786
|
+
*/
|
|
1626
1787
|
getLid(pn) {
|
|
1627
1788
|
const normalized = this.normalizeJid(pn)
|
|
1628
1789
|
const lid = this.pnToLid.get(normalized)
|
|
@@ -1643,7 +1804,9 @@ var LidResolver = class {
|
|
|
1643
1804
|
}
|
|
1644
1805
|
return null
|
|
1645
1806
|
}
|
|
1646
|
-
|
|
1807
|
+
/**
|
|
1808
|
+
* Full mapping for inspection
|
|
1809
|
+
*/
|
|
1647
1810
|
getMapping(jid) {
|
|
1648
1811
|
const normalized = this.normalizeJid(jid)
|
|
1649
1812
|
const byLid = this.lidToPn.get(normalized)
|
|
@@ -1661,7 +1824,9 @@ var LidResolver = class {
|
|
|
1661
1824
|
}
|
|
1662
1825
|
return null
|
|
1663
1826
|
}
|
|
1664
|
-
|
|
1827
|
+
/**
|
|
1828
|
+
* Seed from persistence (called automatically in constructor if persistence provided)
|
|
1829
|
+
*/
|
|
1665
1830
|
async hydrate() {
|
|
1666
1831
|
if (!this.persistence?.load) return
|
|
1667
1832
|
try {
|
|
@@ -1688,7 +1853,9 @@ var LidResolver = class {
|
|
|
1688
1853
|
}
|
|
1689
1854
|
} catch (error) {}
|
|
1690
1855
|
}
|
|
1691
|
-
|
|
1856
|
+
/**
|
|
1857
|
+
* Flush current map to persistence
|
|
1858
|
+
*/
|
|
1692
1859
|
async flush() {
|
|
1693
1860
|
if (!this.persistence?.save) return
|
|
1694
1861
|
try {
|
|
@@ -1708,7 +1875,9 @@ var LidResolver = class {
|
|
|
1708
1875
|
canonicalForm: this.config.canonical
|
|
1709
1876
|
}
|
|
1710
1877
|
}
|
|
1711
|
-
|
|
1878
|
+
/**
|
|
1879
|
+
* Clear everything
|
|
1880
|
+
*/
|
|
1712
1881
|
reset() {
|
|
1713
1882
|
this.lidToPn.clear()
|
|
1714
1883
|
this.pnToLid.clear()
|
|
@@ -1724,12 +1893,16 @@ var LidResolver = class {
|
|
|
1724
1893
|
void this.flush()
|
|
1725
1894
|
}
|
|
1726
1895
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1896
|
+
// Private helpers
|
|
1897
|
+
/**
|
|
1898
|
+
* Normalize JID: strip device suffix `:N`
|
|
1899
|
+
*/
|
|
1729
1900
|
normalizeJid(jid) {
|
|
1730
1901
|
return jid.replace(/:\d+@/, '@')
|
|
1731
1902
|
}
|
|
1732
|
-
|
|
1903
|
+
/**
|
|
1904
|
+
* Evict least recently accessed mapping (LRU)
|
|
1905
|
+
*/
|
|
1733
1906
|
evictLRU() {
|
|
1734
1907
|
let oldestLid = null
|
|
1735
1908
|
let oldestTime = Infinity
|
|
@@ -1749,7 +1922,7 @@ var LidResolver = class {
|
|
|
1749
1922
|
}
|
|
1750
1923
|
}
|
|
1751
1924
|
|
|
1752
|
-
|
|
1925
|
+
// jidCanonicalizer.js
|
|
1753
1926
|
var DEFAULT_CONFIG11 = {
|
|
1754
1927
|
enabled: false,
|
|
1755
1928
|
canonicalizeOutbound: true,
|
|
@@ -1759,7 +1932,7 @@ var JidCanonicalizer = class {
|
|
|
1759
1932
|
config
|
|
1760
1933
|
lidResolver
|
|
1761
1934
|
ownsResolver
|
|
1762
|
-
|
|
1935
|
+
// Track if we created the resolver (for destroy)
|
|
1763
1936
|
stats = {
|
|
1764
1937
|
outboundCanonicalized: 0,
|
|
1765
1938
|
outboundPassthrough: 0,
|
|
@@ -1777,11 +1950,15 @@ var JidCanonicalizer = class {
|
|
|
1777
1950
|
this.ownsResolver = true
|
|
1778
1951
|
}
|
|
1779
1952
|
}
|
|
1780
|
-
|
|
1953
|
+
/**
|
|
1954
|
+
* Access the underlying resolver (for cross-module sharing)
|
|
1955
|
+
*/
|
|
1781
1956
|
get resolver() {
|
|
1782
1957
|
return this.lidResolver
|
|
1783
1958
|
}
|
|
1784
|
-
|
|
1959
|
+
/**
|
|
1960
|
+
* Called by wrapper on every outbound send. Returns canonical JID.
|
|
1961
|
+
*/
|
|
1785
1962
|
canonicalizeTarget(jid) {
|
|
1786
1963
|
if (!this.config.enabled || !this.config.canonicalizeOutbound) {
|
|
1787
1964
|
return jid
|
|
@@ -1794,7 +1971,22 @@ var JidCanonicalizer = class {
|
|
|
1794
1971
|
}
|
|
1795
1972
|
return canonical
|
|
1796
1973
|
}
|
|
1797
|
-
|
|
1974
|
+
/**
|
|
1975
|
+
* Returns a stable, canonical thread key for storage / DB indexing.
|
|
1976
|
+
*
|
|
1977
|
+
* Different from `canonicalizeTarget()` (which picks the right send target):
|
|
1978
|
+
* - canonicalizeTarget('1234@lid') → '+27...@s.whatsapp.net' (best send target)
|
|
1979
|
+
* - canonicalKey('1234@lid') → 'thread:27...' (stable thread identifier)
|
|
1980
|
+
*
|
|
1981
|
+
* If LID has known PN mapping → use phone-number form
|
|
1982
|
+
* If only LID known → use LID stripped of suffix
|
|
1983
|
+
* Always lowercase, no @-suffix, prefixed with `thread:`
|
|
1984
|
+
*
|
|
1985
|
+
* Apps using this as their DB key won't double-thread on LID/PN drift.
|
|
1986
|
+
*
|
|
1987
|
+
* @param jid - WhatsApp JID (can be PN, LID, group, or broadcast)
|
|
1988
|
+
* @returns Stable thread key for DB indexing
|
|
1989
|
+
*/
|
|
1798
1990
|
canonicalKey(jid) {
|
|
1799
1991
|
if (!jid || typeof jid !== 'string' || jid.trim() === '') {
|
|
1800
1992
|
return 'thread:invalid'
|
|
@@ -1832,7 +2024,9 @@ var JidCanonicalizer = class {
|
|
|
1832
2024
|
}
|
|
1833
2025
|
return `thread:${domain}:${user}`
|
|
1834
2026
|
}
|
|
1835
|
-
|
|
2027
|
+
/**
|
|
2028
|
+
* Called by wrapper on messages.upsert event. Learns mappings.
|
|
2029
|
+
*/
|
|
1836
2030
|
onIncomingEvent(upsert) {
|
|
1837
2031
|
if (!this.config.enabled || !this.config.learnFromEvents) {
|
|
1838
2032
|
return
|
|
@@ -1841,7 +2035,9 @@ var JidCanonicalizer = class {
|
|
|
1841
2035
|
this.learnFromMessage(msg)
|
|
1842
2036
|
}
|
|
1843
2037
|
}
|
|
1844
|
-
|
|
2038
|
+
/**
|
|
2039
|
+
* Called by wrapper on messages.update event. Learns from sent-message refs.
|
|
2040
|
+
*/
|
|
1845
2041
|
onMessageUpdate(updates) {
|
|
1846
2042
|
if (!this.config.enabled || !this.config.learnFromEvents) {
|
|
1847
2043
|
return
|
|
@@ -1867,8 +2063,10 @@ var JidCanonicalizer = class {
|
|
|
1867
2063
|
this.lidResolver.destroy()
|
|
1868
2064
|
}
|
|
1869
2065
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
2066
|
+
// Private helpers
|
|
2067
|
+
/**
|
|
2068
|
+
* Extract LID↔PN mappings from a message object
|
|
2069
|
+
*/
|
|
1872
2070
|
learnFromMessage(msg) {
|
|
1873
2071
|
if (!msg.key) return
|
|
1874
2072
|
this.learnFromMessageKey(msg.key)
|
|
@@ -1880,7 +2078,9 @@ var JidCanonicalizer = class {
|
|
|
1880
2078
|
this.stats.inboundLearned++
|
|
1881
2079
|
}
|
|
1882
2080
|
}
|
|
1883
|
-
|
|
2081
|
+
/**
|
|
2082
|
+
* Extract mappings from message.key
|
|
2083
|
+
*/
|
|
1884
2084
|
learnFromMessageKey(key) {
|
|
1885
2085
|
if (!key) return
|
|
1886
2086
|
if (key.participant && key.participantPn) {
|
|
@@ -1913,7 +2113,7 @@ var JidCanonicalizer = class {
|
|
|
1913
2113
|
}
|
|
1914
2114
|
}
|
|
1915
2115
|
|
|
1916
|
-
|
|
2116
|
+
// sessionStability.js
|
|
1917
2117
|
function classifyDisconnect(statusCode) {
|
|
1918
2118
|
if (statusCode === 401 || statusCode === 440) {
|
|
1919
2119
|
return {
|
|
@@ -1952,7 +2152,7 @@ function classifyDisconnect(statusCode) {
|
|
|
1952
2152
|
category: 'recoverable',
|
|
1953
2153
|
shouldReconnect: true,
|
|
1954
2154
|
backoffMs: 3e4,
|
|
1955
|
-
|
|
2155
|
+
// 30 seconds
|
|
1956
2156
|
message: 'Precondition failed \u2014 auth state mismatch, retry after delay',
|
|
1957
2157
|
code: statusCode
|
|
1958
2158
|
}
|
|
@@ -1962,7 +2162,7 @@ function classifyDisconnect(statusCode) {
|
|
|
1962
2162
|
category: 'rate-limited',
|
|
1963
2163
|
shouldReconnect: true,
|
|
1964
2164
|
backoffMs: 3e5,
|
|
1965
|
-
|
|
2165
|
+
// 5 minutes
|
|
1966
2166
|
message: 'Rate limited by WhatsApp \u2014 cool-off period required',
|
|
1967
2167
|
code: statusCode
|
|
1968
2168
|
}
|
|
@@ -1972,7 +2172,7 @@ function classifyDisconnect(statusCode) {
|
|
|
1972
2172
|
category: 'rate-limited',
|
|
1973
2173
|
shouldReconnect: true,
|
|
1974
2174
|
backoffMs: 6e4,
|
|
1975
|
-
|
|
2175
|
+
// 1 minute
|
|
1976
2176
|
message: 'WhatsApp service unavailable \u2014 temporary outage',
|
|
1977
2177
|
code: statusCode
|
|
1978
2178
|
}
|
|
@@ -1982,7 +2182,7 @@ function classifyDisconnect(statusCode) {
|
|
|
1982
2182
|
category: 'recoverable',
|
|
1983
2183
|
shouldReconnect: true,
|
|
1984
2184
|
backoffMs: 5e3,
|
|
1985
|
-
|
|
2185
|
+
// 5 seconds
|
|
1986
2186
|
message: 'Connection timeout \u2014 network issue, safe to retry',
|
|
1987
2187
|
code: statusCode
|
|
1988
2188
|
}
|
|
@@ -1992,7 +2192,7 @@ function classifyDisconnect(statusCode) {
|
|
|
1992
2192
|
category: 'recoverable',
|
|
1993
2193
|
shouldReconnect: true,
|
|
1994
2194
|
backoffMs: 1e4,
|
|
1995
|
-
|
|
2195
|
+
// 10 seconds
|
|
1996
2196
|
message: 'WhatsApp internal error \u2014 temporary server issue',
|
|
1997
2197
|
code: statusCode
|
|
1998
2198
|
}
|
|
@@ -2002,7 +2202,7 @@ function classifyDisconnect(statusCode) {
|
|
|
2002
2202
|
category: 'recoverable',
|
|
2003
2203
|
shouldReconnect: true,
|
|
2004
2204
|
backoffMs: 2e3,
|
|
2005
|
-
|
|
2205
|
+
// 2 seconds
|
|
2006
2206
|
message: 'Connection closed gracefully \u2014 safe to reconnect',
|
|
2007
2207
|
code: statusCode
|
|
2008
2208
|
}
|
|
@@ -2011,7 +2211,7 @@ function classifyDisconnect(statusCode) {
|
|
|
2011
2211
|
category: 'unknown',
|
|
2012
2212
|
shouldReconnect: true,
|
|
2013
2213
|
backoffMs: 15e3,
|
|
2014
|
-
|
|
2214
|
+
// 15 seconds
|
|
2015
2215
|
message: `Unknown disconnect reason (code ${statusCode}) \u2014 reconnect with caution`,
|
|
2016
2216
|
code: statusCode
|
|
2017
2217
|
}
|
|
@@ -2036,12 +2236,16 @@ var SessionHealthMonitor = class {
|
|
|
2036
2236
|
this.onDegraded = config.onDegraded
|
|
2037
2237
|
this.onRecovered = config.onRecovered
|
|
2038
2238
|
}
|
|
2039
|
-
|
|
2239
|
+
/**
|
|
2240
|
+
* Record successful decrypt
|
|
2241
|
+
*/
|
|
2040
2242
|
recordDecryptSuccess() {
|
|
2041
2243
|
this.stats.decryptSuccess++
|
|
2042
2244
|
this.checkRecovery()
|
|
2043
2245
|
}
|
|
2044
|
-
|
|
2246
|
+
/**
|
|
2247
|
+
* Record failed decrypt (Bad MAC or similar)
|
|
2248
|
+
*/
|
|
2045
2249
|
recordDecryptFail(isBadMac = false) {
|
|
2046
2250
|
this.stats.decryptFail++
|
|
2047
2251
|
if (isBadMac) {
|
|
@@ -2058,7 +2262,9 @@ var SessionHealthMonitor = class {
|
|
|
2058
2262
|
}
|
|
2059
2263
|
}
|
|
2060
2264
|
}
|
|
2061
|
-
|
|
2265
|
+
/**
|
|
2266
|
+
* Check if session has recovered from degraded state
|
|
2267
|
+
*/
|
|
2062
2268
|
checkRecovery() {
|
|
2063
2269
|
if (!this.stats.isDegraded) return
|
|
2064
2270
|
const now = Date.now()
|
|
@@ -2070,11 +2276,15 @@ var SessionHealthMonitor = class {
|
|
|
2070
2276
|
this.onRecovered?.(this.getStats())
|
|
2071
2277
|
}
|
|
2072
2278
|
}
|
|
2073
|
-
|
|
2279
|
+
/**
|
|
2280
|
+
* Get current health stats
|
|
2281
|
+
*/
|
|
2074
2282
|
getStats() {
|
|
2075
2283
|
return { ...this.stats }
|
|
2076
2284
|
}
|
|
2077
|
-
|
|
2285
|
+
/**
|
|
2286
|
+
* Reset all counters
|
|
2287
|
+
*/
|
|
2078
2288
|
reset() {
|
|
2079
2289
|
this.stats = {
|
|
2080
2290
|
decryptSuccess: 0,
|
|
@@ -2107,51 +2317,51 @@ function wrapWithSessionStability(sock, config = {}) {
|
|
|
2107
2317
|
})
|
|
2108
2318
|
}
|
|
2109
2319
|
|
|
2110
|
-
|
|
2320
|
+
// presets.js
|
|
2111
2321
|
var PRESETS = {
|
|
2112
2322
|
conservative: {
|
|
2113
|
-
maxPerMinute:
|
|
2114
|
-
maxPerHour:
|
|
2115
|
-
maxPerDay:
|
|
2116
|
-
minDelayMs:
|
|
2117
|
-
maxDelayMs:
|
|
2118
|
-
newChatDelayMs:
|
|
2119
|
-
warmupDays:
|
|
2120
|
-
day1Limit:
|
|
2323
|
+
maxPerMinute: 5,
|
|
2324
|
+
maxPerHour: 100,
|
|
2325
|
+
maxPerDay: 800,
|
|
2326
|
+
minDelayMs: 2500,
|
|
2327
|
+
maxDelayMs: 7e3,
|
|
2328
|
+
newChatDelayMs: 4e3,
|
|
2329
|
+
warmupDays: 10,
|
|
2330
|
+
day1Limit: 15,
|
|
2121
2331
|
growthFactor: 1.8,
|
|
2122
|
-
inactivityThresholdHours:
|
|
2123
|
-
autoPauseAt: '
|
|
2332
|
+
inactivityThresholdHours: 72,
|
|
2333
|
+
autoPauseAt: 'medium',
|
|
2124
2334
|
groupMultiplier: 0.5,
|
|
2125
2335
|
groupProfiles: false,
|
|
2126
2336
|
logging: true
|
|
2127
2337
|
},
|
|
2128
2338
|
moderate: {
|
|
2129
|
-
maxPerMinute:
|
|
2130
|
-
maxPerHour:
|
|
2131
|
-
maxPerDay:
|
|
2132
|
-
minDelayMs:
|
|
2133
|
-
maxDelayMs:
|
|
2134
|
-
newChatDelayMs:
|
|
2135
|
-
warmupDays:
|
|
2136
|
-
day1Limit:
|
|
2339
|
+
maxPerMinute: 10,
|
|
2340
|
+
maxPerHour: 300,
|
|
2341
|
+
maxPerDay: 1500,
|
|
2342
|
+
minDelayMs: 1500,
|
|
2343
|
+
maxDelayMs: 5e3,
|
|
2344
|
+
newChatDelayMs: 3e3,
|
|
2345
|
+
warmupDays: 7,
|
|
2346
|
+
day1Limit: 20,
|
|
2137
2347
|
growthFactor: 1.8,
|
|
2138
|
-
inactivityThresholdHours:
|
|
2139
|
-
autoPauseAt: '
|
|
2348
|
+
inactivityThresholdHours: 72,
|
|
2349
|
+
autoPauseAt: 'high',
|
|
2140
2350
|
groupMultiplier: 0.7,
|
|
2141
2351
|
groupProfiles: false,
|
|
2142
2352
|
logging: true
|
|
2143
2353
|
},
|
|
2144
2354
|
aggressive: {
|
|
2145
|
-
maxPerMinute:
|
|
2146
|
-
maxPerHour:
|
|
2147
|
-
maxPerDay:
|
|
2148
|
-
minDelayMs:
|
|
2149
|
-
maxDelayMs:
|
|
2150
|
-
newChatDelayMs:
|
|
2151
|
-
warmupDays:
|
|
2152
|
-
day1Limit:
|
|
2355
|
+
maxPerMinute: 20,
|
|
2356
|
+
maxPerHour: 800,
|
|
2357
|
+
maxPerDay: 4e3,
|
|
2358
|
+
minDelayMs: 800,
|
|
2359
|
+
maxDelayMs: 3e3,
|
|
2360
|
+
newChatDelayMs: 2e3,
|
|
2361
|
+
warmupDays: 4,
|
|
2362
|
+
day1Limit: 35,
|
|
2153
2363
|
growthFactor: 2,
|
|
2154
|
-
inactivityThresholdHours:
|
|
2364
|
+
inactivityThresholdHours: 48,
|
|
2155
2365
|
autoPauseAt: 'critical',
|
|
2156
2366
|
groupMultiplier: 0.9,
|
|
2157
2367
|
groupProfiles: false,
|
|
@@ -2160,7 +2370,7 @@ var PRESETS = {
|
|
|
2160
2370
|
}
|
|
2161
2371
|
function resolveConfig(input) {
|
|
2162
2372
|
if (input === void 0) {
|
|
2163
|
-
return { ...PRESETS.
|
|
2373
|
+
return { ...PRESETS.conservative }
|
|
2164
2374
|
}
|
|
2165
2375
|
if (typeof input === 'string') {
|
|
2166
2376
|
if (!(input in PRESETS)) {
|
|
@@ -2168,14 +2378,14 @@ function resolveConfig(input) {
|
|
|
2168
2378
|
}
|
|
2169
2379
|
return { ...PRESETS[input] }
|
|
2170
2380
|
}
|
|
2171
|
-
const { preset = '
|
|
2381
|
+
const { preset = 'conservative', ...overrides } = input
|
|
2172
2382
|
if (!(preset in PRESETS)) {
|
|
2173
2383
|
throw new Error(`Unknown preset "${preset}". Valid: ${Object.keys(PRESETS).join(', ')}`)
|
|
2174
2384
|
}
|
|
2175
2385
|
return { ...PRESETS[preset], ...overrides }
|
|
2176
2386
|
}
|
|
2177
2387
|
|
|
2178
|
-
|
|
2388
|
+
// persist.js
|
|
2179
2389
|
var fs = __toESM(require('fs'), 1)
|
|
2180
2390
|
var KNOWN_CHATS_MAX = 1e3
|
|
2181
2391
|
var DEBOUNCE_MS = 5e3
|
|
@@ -2201,7 +2411,7 @@ var StateManager = class {
|
|
|
2201
2411
|
return null
|
|
2202
2412
|
}
|
|
2203
2413
|
}
|
|
2204
|
-
|
|
2414
|
+
/** Debounced save — called after every send (5s delay) */
|
|
2205
2415
|
saveDebounced(state) {
|
|
2206
2416
|
if (this.debounceTimer) {
|
|
2207
2417
|
clearTimeout(this.debounceTimer)
|
|
@@ -2211,7 +2421,7 @@ var StateManager = class {
|
|
|
2211
2421
|
this.debounceTimer = null
|
|
2212
2422
|
}, DEBOUNCE_MS)
|
|
2213
2423
|
}
|
|
2214
|
-
|
|
2424
|
+
/** Immediate save — called after health events (ban/restriction) */
|
|
2215
2425
|
saveImmediate(state) {
|
|
2216
2426
|
if (this.debounceTimer) {
|
|
2217
2427
|
clearTimeout(this.debounceTimer)
|
|
@@ -2219,7 +2429,7 @@ var StateManager = class {
|
|
|
2219
2429
|
}
|
|
2220
2430
|
this.writeFile(state)
|
|
2221
2431
|
}
|
|
2222
|
-
|
|
2432
|
+
/** Flush/cancel pending debounced write (for tests and process exit) */
|
|
2223
2433
|
flush() {
|
|
2224
2434
|
if (this.debounceTimer) {
|
|
2225
2435
|
clearTimeout(this.debounceTimer)
|
|
@@ -2233,7 +2443,7 @@ var StateManager = class {
|
|
|
2233
2443
|
const toSave = {
|
|
2234
2444
|
...state,
|
|
2235
2445
|
savedAt: Date.now(),
|
|
2236
|
-
|
|
2446
|
+
// LRU eviction: keep last KNOWN_CHATS_MAX entries
|
|
2237
2447
|
knownChats:
|
|
2238
2448
|
state.knownChats.length > KNOWN_CHATS_MAX ? state.knownChats.slice(-KNOWN_CHATS_MAX) : state.knownChats
|
|
2239
2449
|
}
|
|
@@ -2245,7 +2455,7 @@ var StateManager = class {
|
|
|
2245
2455
|
}
|
|
2246
2456
|
}
|
|
2247
2457
|
|
|
2248
|
-
|
|
2458
|
+
// profiles.js
|
|
2249
2459
|
function isGroup(jid) {
|
|
2250
2460
|
return jid.endsWith('@g.us')
|
|
2251
2461
|
}
|
|
@@ -2266,23 +2476,26 @@ function applyGroupMultiplier(limits, multiplier) {
|
|
|
2266
2476
|
}
|
|
2267
2477
|
}
|
|
2268
2478
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
const CORE_LEGACY_KEYS = ['rateLimiter', 'warmUp']
|
|
2278
|
-
function hasLegacyCoreNesting(cfg) {
|
|
2479
|
+
// antiban.js
|
|
2480
|
+
function isLegacyConfig(cfg) {
|
|
2279
2481
|
if (typeof cfg !== 'object' || cfg === null) return false
|
|
2280
|
-
return
|
|
2482
|
+
return (
|
|
2483
|
+
'rateLimiter' in cfg ||
|
|
2484
|
+
'warmUp' in cfg ||
|
|
2485
|
+
'health' in cfg ||
|
|
2486
|
+
'timelock' in cfg ||
|
|
2487
|
+
'replyRatio' in cfg ||
|
|
2488
|
+
'contactGraph' in cfg ||
|
|
2489
|
+
'presence' in cfg ||
|
|
2490
|
+
'retryTracker' in cfg ||
|
|
2491
|
+
'reconnectThrottle' in cfg ||
|
|
2492
|
+
'lidResolver' in cfg ||
|
|
2493
|
+
'jidCanonicalizer' in cfg ||
|
|
2494
|
+
'sessionStability' in cfg
|
|
2495
|
+
)
|
|
2281
2496
|
}
|
|
2282
2497
|
function mapLegacyToFlat(legacy) {
|
|
2283
|
-
process.stderr.write(
|
|
2284
|
-
'[baileys-antiban] DEPRECATED: nested { rateLimiter, warmUp } config detected. Migrate to flat fields: new AntiBan({ maxPerMinute: 8, warmupDays: 5 }). Flat top-level fields you also pass always take precedence.\n'
|
|
2285
|
-
)
|
|
2498
|
+
process.stderr.write('[baileys-antiban] DEPRECATED: Nested config (v2 style) detected. Migrate to flat config: new AntiBan({ maxPerMinute: 8 }).\n')
|
|
2286
2499
|
const flat = {}
|
|
2287
2500
|
if (legacy.rateLimiter?.maxPerMinute !== void 0) flat.maxPerMinute = legacy.rateLimiter.maxPerMinute
|
|
2288
2501
|
if (legacy.rateLimiter?.maxPerHour !== void 0) flat.maxPerHour = legacy.rateLimiter.maxPerHour
|
|
@@ -2293,6 +2506,7 @@ function mapLegacyToFlat(legacy) {
|
|
|
2293
2506
|
if (legacy.warmUp?.warmUpDays !== void 0) flat.warmupDays = legacy.warmUp.warmUpDays
|
|
2294
2507
|
if (legacy.warmUp?.day1Limit !== void 0) flat.day1Limit = legacy.warmUp.day1Limit
|
|
2295
2508
|
if (legacy.warmUp?.growthFactor !== void 0) flat.growthFactor = legacy.warmUp.growthFactor
|
|
2509
|
+
if (legacy.logging !== void 0) flat.logging = legacy.logging
|
|
2296
2510
|
return flat
|
|
2297
2511
|
}
|
|
2298
2512
|
var AntiBan = class {
|
|
@@ -2317,29 +2531,18 @@ var AntiBan = class {
|
|
|
2317
2531
|
totalDelayMs: 0
|
|
2318
2532
|
}
|
|
2319
2533
|
constructor(input, warmUpStateArg) {
|
|
2534
|
+
let flatConfig
|
|
2535
|
+
let legacyPassthrough = null
|
|
2320
2536
|
let warmUpState = warmUpStateArg
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
const
|
|
2329
|
-
const cfg = typeof input === 'string' ? resolveConfig(input) : resolveConfig(flatMerged)
|
|
2537
|
+
if (isLegacyConfig(input)) {
|
|
2538
|
+
legacyPassthrough = input
|
|
2539
|
+
flatConfig = mapLegacyToFlat(legacyPassthrough)
|
|
2540
|
+
} else {
|
|
2541
|
+
flatConfig = {}
|
|
2542
|
+
legacyPassthrough = null
|
|
2543
|
+
}
|
|
2544
|
+
const cfg = isLegacyConfig(input) ? resolveConfig(flatConfig) : resolveConfig(input)
|
|
2330
2545
|
this.resolvedConfig = cfg
|
|
2331
|
-
const rateLimiterOverrides = rawInput.rateLimiter || {}
|
|
2332
|
-
const warmUpOverrides = rawInput.warmUp || {}
|
|
2333
|
-
const healthOverrides = rawInput.health || {}
|
|
2334
|
-
const timelockOverrides = rawInput.timelock || {}
|
|
2335
|
-
const replyRatioCfg = rawInput.replyRatio
|
|
2336
|
-
const contactGraphCfg = rawInput.contactGraph
|
|
2337
|
-
const presenceCfg = rawInput.presence
|
|
2338
|
-
const retryTrackerCfg = rawInput.retryTracker
|
|
2339
|
-
const reconnectThrottleCfg = rawInput.reconnectThrottle
|
|
2340
|
-
const lidResolverCfg = rawInput.lidResolver
|
|
2341
|
-
const jidCanonicalizerCfg = rawInput.jidCanonicalizer
|
|
2342
|
-
const sessionStabilityCfg = rawInput.sessionStability
|
|
2343
2546
|
let savedState = null
|
|
2344
2547
|
if (cfg.persist) {
|
|
2345
2548
|
this.stateManager = new StateManager(cfg.persist)
|
|
@@ -2349,9 +2552,7 @@ var AntiBan = class {
|
|
|
2349
2552
|
}
|
|
2350
2553
|
}
|
|
2351
2554
|
this.logging = cfg.logging ?? true
|
|
2352
|
-
this._log = msg => {
|
|
2353
|
-
if (this.logging) process.stdout.write(`[baileys-antiban] ${msg}\n`)
|
|
2354
|
-
}
|
|
2555
|
+
this._log = (msg) => { if (this.logging) process.stdout.write(`[antiban] ${msg}\n`) }
|
|
2355
2556
|
this.rateLimiter = new RateLimiter({
|
|
2356
2557
|
maxPerMinute: cfg.maxPerMinute,
|
|
2357
2558
|
maxPerHour: cfg.maxPerHour,
|
|
@@ -2359,7 +2560,7 @@ var AntiBan = class {
|
|
|
2359
2560
|
minDelayMs: cfg.minDelayMs,
|
|
2360
2561
|
maxDelayMs: cfg.maxDelayMs,
|
|
2361
2562
|
newChatDelayMs: cfg.newChatDelayMs,
|
|
2362
|
-
...
|
|
2563
|
+
...(legacyPassthrough?.rateLimiter || {})
|
|
2363
2564
|
})
|
|
2364
2565
|
if (savedState?.knownChats) {
|
|
2365
2566
|
this.rateLimiter.restoreKnownChats(savedState.knownChats)
|
|
@@ -2370,73 +2571,69 @@ var AntiBan = class {
|
|
|
2370
2571
|
day1Limit: cfg.day1Limit,
|
|
2371
2572
|
growthFactor: cfg.growthFactor,
|
|
2372
2573
|
inactivityThresholdHours: cfg.inactivityThresholdHours,
|
|
2373
|
-
...
|
|
2574
|
+
...(legacyPassthrough?.warmUp || {})
|
|
2374
2575
|
},
|
|
2375
2576
|
warmUpState
|
|
2376
2577
|
)
|
|
2377
2578
|
this.health = new HealthMonitor({
|
|
2378
2579
|
autoPauseAt: cfg.autoPauseAt,
|
|
2379
|
-
...
|
|
2580
|
+
...(legacyPassthrough?.health || {}),
|
|
2380
2581
|
onRiskChange: status => {
|
|
2381
2582
|
const emoji = { low: '\u{1F7E2}', medium: '\u{1F7E1}', high: '\u{1F7E0}', critical: '\u{1F534}' }
|
|
2382
2583
|
this._log(`${emoji[status.risk]} Risk level: ${status.risk.toUpperCase()} (score: ${status.score})`)
|
|
2383
2584
|
this._log(status.recommendation)
|
|
2384
2585
|
status.reasons.forEach(r => this._log(` \u2192 ${r}`))
|
|
2385
|
-
|
|
2586
|
+
legacyPassthrough?.health?.onRiskChange?.(status)
|
|
2386
2587
|
}
|
|
2387
2588
|
})
|
|
2388
2589
|
this.timelockGuard = new TimelockGuard({
|
|
2389
|
-
...
|
|
2590
|
+
...(legacyPassthrough?.timelock || {}),
|
|
2390
2591
|
onTimelockDetected: state => {
|
|
2391
2592
|
this.health.recordReachoutTimelock(state.enforcementType)
|
|
2392
|
-
this._log(
|
|
2393
|
-
|
|
2394
|
-
)
|
|
2395
|
-
timelockOverrides.onTimelockDetected?.(state)
|
|
2593
|
+
this._log(`REACHOUT TIMELOCKED \u2014 ${state.enforcementType || 'unknown'}, expires ${state.expiresAt?.toISOString() || 'unknown'}`)
|
|
2594
|
+
legacyPassthrough?.timelock?.onTimelockDetected?.(state)
|
|
2396
2595
|
},
|
|
2397
2596
|
onTimelockLifted: state => {
|
|
2398
2597
|
this._log('Timelock lifted \u2014 resuming new contact messages')
|
|
2399
|
-
|
|
2598
|
+
legacyPassthrough?.timelock?.onTimelockLifted?.(state)
|
|
2400
2599
|
}
|
|
2401
2600
|
})
|
|
2402
|
-
this.replyRatioGuard = new ReplyRatioGuard(
|
|
2403
|
-
this.contactGraphWarmer = new ContactGraphWarmer(
|
|
2404
|
-
this.presenceChoreographer = new PresenceChoreographer(
|
|
2601
|
+
this.replyRatioGuard = new ReplyRatioGuard(legacyPassthrough?.replyRatio)
|
|
2602
|
+
this.contactGraphWarmer = new ContactGraphWarmer(legacyPassthrough?.contactGraph)
|
|
2603
|
+
this.presenceChoreographer = new PresenceChoreographer(legacyPassthrough?.presence)
|
|
2405
2604
|
this.retryTrackerModule = new RetryReasonTracker({
|
|
2406
|
-
...(
|
|
2605
|
+
...(legacyPassthrough?.retryTracker || {}),
|
|
2407
2606
|
onSpiral: (msgId, reason) => {
|
|
2408
2607
|
this._log(`\u26A0\uFE0F Message ${msgId} stuck in retry spiral (${reason})`)
|
|
2409
|
-
|
|
2608
|
+
legacyPassthrough?.retryTracker?.onSpiral?.(msgId, reason)
|
|
2410
2609
|
}
|
|
2411
2610
|
})
|
|
2412
2611
|
this.reconnectThrottleModule = new PostReconnectThrottle({
|
|
2413
|
-
...(
|
|
2612
|
+
...(legacyPassthrough?.reconnectThrottle || {}),
|
|
2414
2613
|
baselineRatePerMinute: () => this.rateLimiter.getStats().limits.perMinute
|
|
2415
2614
|
})
|
|
2416
|
-
if (
|
|
2417
|
-
if (
|
|
2418
|
-
this.jidCanonicalizerModule = new JidCanonicalizer(
|
|
2419
|
-
this.lidResolverModule =
|
|
2615
|
+
if (legacyPassthrough?.jidCanonicalizer?.enabled) {
|
|
2616
|
+
if (legacyPassthrough.jidCanonicalizer.resolver) {
|
|
2617
|
+
this.jidCanonicalizerModule = new JidCanonicalizer(legacyPassthrough.jidCanonicalizer)
|
|
2618
|
+
this.lidResolverModule = legacyPassthrough.jidCanonicalizer.resolver
|
|
2420
2619
|
} else {
|
|
2421
|
-
const resolverConfig =
|
|
2620
|
+
const resolverConfig = legacyPassthrough.lidResolver || legacyPassthrough.jidCanonicalizer.resolverConfig
|
|
2422
2621
|
const resolver = new LidResolver(resolverConfig)
|
|
2423
2622
|
this.lidResolverModule = resolver
|
|
2424
2623
|
this.jidCanonicalizerModule = new JidCanonicalizer({
|
|
2425
|
-
...
|
|
2624
|
+
...legacyPassthrough.jidCanonicalizer,
|
|
2426
2625
|
resolver
|
|
2427
2626
|
})
|
|
2428
2627
|
}
|
|
2429
|
-
} else if (
|
|
2430
|
-
this.lidResolverModule = new LidResolver(
|
|
2628
|
+
} else if (legacyPassthrough?.lidResolver) {
|
|
2629
|
+
this.lidResolverModule = new LidResolver(legacyPassthrough.lidResolver)
|
|
2431
2630
|
}
|
|
2432
|
-
if (
|
|
2631
|
+
if (legacyPassthrough?.sessionStability?.enabled) {
|
|
2433
2632
|
const healthConfig = {
|
|
2434
|
-
badMacThreshold:
|
|
2435
|
-
badMacWindowMs:
|
|
2633
|
+
badMacThreshold: legacyPassthrough.sessionStability.badMacThreshold,
|
|
2634
|
+
badMacWindowMs: legacyPassthrough.sessionStability.badMacWindowMs,
|
|
2436
2635
|
onDegraded: stats => {
|
|
2437
|
-
this._log(
|
|
2438
|
-
`\u{1F534} SESSION DEGRADED \u2014 Bad MAC rate: ${stats.badMacCount} in last ${sessionStabilityCfg.badMacWindowMs || 6e4}ms`
|
|
2439
|
-
)
|
|
2636
|
+
this._log(`\u{1F534} SESSION DEGRADED \u2014 Bad MAC rate: ${stats.badMacCount} in last ${legacyPassthrough?.sessionStability?.badMacWindowMs || 6e4}ms`)
|
|
2440
2637
|
this._log('Consider restarting session or switching to LID-based canonical form')
|
|
2441
2638
|
},
|
|
2442
2639
|
onRecovered: () => {
|
|
@@ -2446,8 +2643,11 @@ var AntiBan = class {
|
|
|
2446
2643
|
this.sessionStabilityMonitor = new SessionHealthMonitor(healthConfig)
|
|
2447
2644
|
}
|
|
2448
2645
|
}
|
|
2449
|
-
|
|
2450
|
-
|
|
2646
|
+
/**
|
|
2647
|
+
* Check if a message can be sent and get required delay.
|
|
2648
|
+
* Call this BEFORE every sendMessage().
|
|
2649
|
+
*/
|
|
2650
|
+
async beforeSend(recipient, content) {
|
|
2451
2651
|
const healthStatus = this.health.getStatus()
|
|
2452
2652
|
if (this.health.isPaused()) {
|
|
2453
2653
|
this.stats.messagesBlocked++
|
|
@@ -2473,9 +2673,7 @@ var AntiBan = class {
|
|
|
2473
2673
|
if (!this.warmUp.canSend()) {
|
|
2474
2674
|
this.stats.messagesBlocked++
|
|
2475
2675
|
const warmUpStatus = this.warmUp.getStatus()
|
|
2476
|
-
this._log(
|
|
2477
|
-
`\u23F3 BLOCKED \u2014 warm-up day ${warmUpStatus.day}/${warmUpStatus.totalDays}, limit reached (${warmUpStatus.todaySent}/${warmUpStatus.todayLimit})`
|
|
2478
|
-
)
|
|
2676
|
+
this._log(`\u23F3 BLOCKED \u2014 warm-up day ${warmUpStatus.day}/${warmUpStatus.totalDays}, limit reached (${warmUpStatus.todaySent}/${warmUpStatus.todayLimit})`)
|
|
2479
2677
|
return {
|
|
2480
2678
|
allowed: false,
|
|
2481
2679
|
delayMs: 0,
|
|
@@ -2537,7 +2735,7 @@ var AntiBan = class {
|
|
|
2537
2735
|
return { allowed: false, delayMs: 0, reason: 'Group rate limit exceeded', health: healthStatus }
|
|
2538
2736
|
}
|
|
2539
2737
|
}
|
|
2540
|
-
let delay = await this.rateLimiter.getDelay(recipient, content
|
|
2738
|
+
let delay = await this.rateLimiter.getDelay(recipient, content)
|
|
2541
2739
|
if (delay === -1) {
|
|
2542
2740
|
this.stats.messagesBlocked++
|
|
2543
2741
|
this._log(`\u{1F6AB} BLOCKED \u2014 rate limit or identical message spam`)
|
|
@@ -2570,19 +2768,26 @@ var AntiBan = class {
|
|
|
2570
2768
|
health: healthStatus
|
|
2571
2769
|
}
|
|
2572
2770
|
}
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2771
|
+
/**
|
|
2772
|
+
* Record a successfully sent message.
|
|
2773
|
+
* Call this AFTER every successful sendMessage().
|
|
2774
|
+
*/
|
|
2775
|
+
afterSend(recipient, content) {
|
|
2776
|
+
this.rateLimiter.record(recipient, content)
|
|
2576
2777
|
this.warmUp.record()
|
|
2577
2778
|
this.replyRatioGuard.recordSent(recipient)
|
|
2578
2779
|
this.stats.messagesAllowed++
|
|
2579
2780
|
this.persistStateDebounced()
|
|
2580
2781
|
}
|
|
2581
|
-
|
|
2782
|
+
/**
|
|
2783
|
+
* Record a failed message send
|
|
2784
|
+
*/
|
|
2582
2785
|
afterSendFailed(error) {
|
|
2583
2786
|
this.health.recordMessageFailed(error)
|
|
2584
2787
|
}
|
|
2585
|
-
|
|
2788
|
+
/**
|
|
2789
|
+
* Record a disconnection (call from connection.update handler)
|
|
2790
|
+
*/
|
|
2586
2791
|
onDisconnect(reason) {
|
|
2587
2792
|
this.health.recordDisconnect(reason)
|
|
2588
2793
|
this.reconnectThrottleModule.onDisconnect()
|
|
@@ -2591,18 +2796,25 @@ var AntiBan = class {
|
|
|
2591
2796
|
this.persistStateImmediate()
|
|
2592
2797
|
}
|
|
2593
2798
|
}
|
|
2594
|
-
|
|
2799
|
+
/**
|
|
2800
|
+
* Record a successful reconnection
|
|
2801
|
+
*/
|
|
2595
2802
|
onReconnect() {
|
|
2596
2803
|
this.health.recordReconnect()
|
|
2597
2804
|
this.reconnectThrottleModule.onReconnect()
|
|
2598
2805
|
}
|
|
2599
|
-
|
|
2806
|
+
/**
|
|
2807
|
+
* Handle incoming message — record in reply ratio + contact graph.
|
|
2808
|
+
* Returns suggested reply if reply ratio suggests auto-reply.
|
|
2809
|
+
*/
|
|
2600
2810
|
onIncomingMessage(jid, msgText) {
|
|
2601
2811
|
this.replyRatioGuard.recordReceived(jid)
|
|
2602
2812
|
this.contactGraphWarmer.onIncomingMessage(jid)
|
|
2603
2813
|
return this.replyRatioGuard.suggestReply(jid, msgText)
|
|
2604
2814
|
}
|
|
2605
|
-
|
|
2815
|
+
/**
|
|
2816
|
+
* Get comprehensive stats
|
|
2817
|
+
*/
|
|
2606
2818
|
getStats() {
|
|
2607
2819
|
const stats = {
|
|
2608
2820
|
...this.stats,
|
|
@@ -2636,57 +2848,65 @@ var AntiBan = class {
|
|
|
2636
2848
|
}
|
|
2637
2849
|
return stats
|
|
2638
2850
|
}
|
|
2639
|
-
|
|
2851
|
+
/** Get the timelock guard for direct access */
|
|
2640
2852
|
get timelock() {
|
|
2641
2853
|
return this.timelockGuard
|
|
2642
2854
|
}
|
|
2643
|
-
|
|
2855
|
+
/** Get the reply ratio guard for direct access */
|
|
2644
2856
|
get replyRatio() {
|
|
2645
2857
|
return this.replyRatioGuard
|
|
2646
2858
|
}
|
|
2647
|
-
|
|
2859
|
+
/** Get the contact graph warmer for direct access */
|
|
2648
2860
|
get contactGraph() {
|
|
2649
2861
|
return this.contactGraphWarmer
|
|
2650
2862
|
}
|
|
2651
|
-
|
|
2863
|
+
/** Get the presence choreographer for direct access */
|
|
2652
2864
|
get presence() {
|
|
2653
2865
|
return this.presenceChoreographer
|
|
2654
2866
|
}
|
|
2655
|
-
|
|
2867
|
+
/** Get the retry tracker for direct access */
|
|
2656
2868
|
get retryTracker() {
|
|
2657
2869
|
return this.retryTrackerModule
|
|
2658
2870
|
}
|
|
2659
|
-
|
|
2871
|
+
/** Get the reconnect throttle for direct access */
|
|
2660
2872
|
get reconnectThrottle() {
|
|
2661
2873
|
return this.reconnectThrottleModule
|
|
2662
2874
|
}
|
|
2663
|
-
|
|
2875
|
+
/** Get the LID resolver for direct access */
|
|
2664
2876
|
get lidResolver() {
|
|
2665
2877
|
return this.lidResolverModule
|
|
2666
2878
|
}
|
|
2667
|
-
|
|
2879
|
+
/** Get the JID canonicalizer for direct access */
|
|
2668
2880
|
get jidCanonicalizer() {
|
|
2669
2881
|
return this.jidCanonicalizerModule
|
|
2670
2882
|
}
|
|
2671
|
-
|
|
2883
|
+
/** Get the session stability monitor for direct access */
|
|
2672
2884
|
get sessionStability() {
|
|
2673
2885
|
return this.sessionStabilityMonitor
|
|
2674
2886
|
}
|
|
2675
|
-
|
|
2887
|
+
/**
|
|
2888
|
+
* Export warm-up state for persistence between restarts
|
|
2889
|
+
*/
|
|
2676
2890
|
exportWarmUpState() {
|
|
2677
2891
|
return this.warmUp.exportState()
|
|
2678
2892
|
}
|
|
2679
|
-
|
|
2893
|
+
/**
|
|
2894
|
+
* Force pause all sending
|
|
2895
|
+
*/
|
|
2680
2896
|
pause() {
|
|
2681
2897
|
this.health.setPaused(true)
|
|
2682
2898
|
this._log('\u23F8\uFE0F Sending paused manually')
|
|
2683
2899
|
}
|
|
2684
|
-
|
|
2900
|
+
/**
|
|
2901
|
+
* Resume sending
|
|
2902
|
+
*/
|
|
2685
2903
|
resume() {
|
|
2686
2904
|
this.health.setPaused(false)
|
|
2687
2905
|
this._log('\u25B6\uFE0F Sending resumed')
|
|
2688
2906
|
}
|
|
2689
|
-
|
|
2907
|
+
/**
|
|
2908
|
+
* Reset everything (use after a ban period)
|
|
2909
|
+
*/
|
|
2690
2910
|
reset() {
|
|
2691
2911
|
this.timelockGuard.reset()
|
|
2692
2912
|
this.health.reset()
|
|
@@ -2719,7 +2939,10 @@ var AntiBan = class {
|
|
|
2719
2939
|
}
|
|
2720
2940
|
this.stateManager.saveImmediate(state)
|
|
2721
2941
|
}
|
|
2722
|
-
|
|
2942
|
+
/**
|
|
2943
|
+
* Clean up all timers and resources.
|
|
2944
|
+
* Call this when disposing of the AntiBan instance or when the socket closes.
|
|
2945
|
+
*/
|
|
2723
2946
|
destroy() {
|
|
2724
2947
|
this.stateManager?.destroy()
|
|
2725
2948
|
this.timelockGuard.reset()
|
|
@@ -2735,14 +2958,17 @@ var AntiBan = class {
|
|
|
2735
2958
|
}
|
|
2736
2959
|
}
|
|
2737
2960
|
|
|
2738
|
-
|
|
2961
|
+
// lidFirstResolver.js
|
|
2739
2962
|
var fs2 = __toESM(require('fs'), 1)
|
|
2740
2963
|
var path = __toESM(require('path'), 1)
|
|
2741
2964
|
var LidFirstResolver = class {
|
|
2742
|
-
lidToPhone =
|
|
2743
|
-
phoneToLid =
|
|
2744
|
-
|
|
2745
|
-
|
|
2965
|
+
lidToPhone = /* @__PURE__ */ new Map()
|
|
2966
|
+
phoneToLid = /* @__PURE__ */ new Map()
|
|
2967
|
+
// phone → lid (quick reverse lookup)
|
|
2968
|
+
/**
|
|
2969
|
+
* Load mappings from Baileys auth state directory.
|
|
2970
|
+
* Looks for lid-mapping-*_reverse.json files.
|
|
2971
|
+
*/
|
|
2746
2972
|
loadFromAuthDir(authDir) {
|
|
2747
2973
|
try {
|
|
2748
2974
|
if (!fs2.existsSync(authDir)) {
|
|
@@ -2772,7 +2998,10 @@ var LidFirstResolver = class {
|
|
|
2772
2998
|
}
|
|
2773
2999
|
} catch (error) {}
|
|
2774
3000
|
}
|
|
2775
|
-
|
|
3001
|
+
/**
|
|
3002
|
+
* Learn a new mapping from a Baileys event (messages, contacts, etc.).
|
|
3003
|
+
* Accepts partial data — will extract what it can.
|
|
3004
|
+
*/
|
|
2776
3005
|
learnFromEvent(event) {
|
|
2777
3006
|
try {
|
|
2778
3007
|
if (event.key?.remoteJid) {
|
|
@@ -2791,19 +3020,28 @@ var LidFirstResolver = class {
|
|
|
2791
3020
|
}
|
|
2792
3021
|
} catch (error) {}
|
|
2793
3022
|
}
|
|
2794
|
-
|
|
3023
|
+
/**
|
|
3024
|
+
* Resolve phone number or phone JID to LID JID.
|
|
3025
|
+
* Returns null if not known.
|
|
3026
|
+
*/
|
|
2795
3027
|
resolveToLID(phoneOrJid) {
|
|
2796
3028
|
const phone = this.extractPhone(phoneOrJid)
|
|
2797
3029
|
if (!phone) return null
|
|
2798
3030
|
return this.phoneToLid.get(phone) || null
|
|
2799
3031
|
}
|
|
2800
|
-
|
|
3032
|
+
/**
|
|
3033
|
+
* Resolve LID JID to phone number.
|
|
3034
|
+
* Returns null if not known.
|
|
3035
|
+
*/
|
|
2801
3036
|
resolveToPhone(lid) {
|
|
2802
3037
|
const normalized = this.normalizeLid(lid)
|
|
2803
3038
|
const mapping = this.lidToPhone.get(normalized)
|
|
2804
3039
|
return mapping ? mapping.phone : null
|
|
2805
3040
|
}
|
|
2806
|
-
|
|
3041
|
+
/**
|
|
3042
|
+
* Get full mapping for a given JID (either LID or phone).
|
|
3043
|
+
* Returns null if not known.
|
|
3044
|
+
*/
|
|
2807
3045
|
getMapping(jid) {
|
|
2808
3046
|
const normalized = this.normalizeLid(jid)
|
|
2809
3047
|
const byLid = this.lidToPhone.get(normalized)
|
|
@@ -2815,16 +3053,20 @@ var LidFirstResolver = class {
|
|
|
2815
3053
|
}
|
|
2816
3054
|
return null
|
|
2817
3055
|
}
|
|
2818
|
-
|
|
3056
|
+
/**
|
|
3057
|
+
* Get total number of known mappings.
|
|
3058
|
+
*/
|
|
2819
3059
|
size() {
|
|
2820
3060
|
return this.lidToPhone.size
|
|
2821
3061
|
}
|
|
2822
|
-
|
|
3062
|
+
/**
|
|
3063
|
+
* Clear all mappings.
|
|
3064
|
+
*/
|
|
2823
3065
|
clear() {
|
|
2824
3066
|
this.lidToPhone.clear()
|
|
2825
3067
|
this.phoneToLid.clear()
|
|
2826
3068
|
}
|
|
2827
|
-
|
|
3069
|
+
// Private helpers
|
|
2828
3070
|
learnJid(_jid, _source) {}
|
|
2829
3071
|
extractPhone(jid) {
|
|
2830
3072
|
if (!jid) return null
|
|
@@ -2843,7 +3085,7 @@ function createLidFirstResolver() {
|
|
|
2843
3085
|
return new LidFirstResolver()
|
|
2844
3086
|
}
|
|
2845
3087
|
|
|
2846
|
-
|
|
3088
|
+
// retryReason.js
|
|
2847
3089
|
var MessageRetryReason
|
|
2848
3090
|
;(function (MessageRetryReason2) {
|
|
2849
3091
|
MessageRetryReason2[(MessageRetryReason2['UnknownError'] = 0)] = 'UnknownError'
|
|
@@ -2855,7 +3097,7 @@ var MessageRetryReason
|
|
|
2855
3097
|
MessageRetryReason2[(MessageRetryReason2['MessageExpired'] = 8)] = 'MessageExpired'
|
|
2856
3098
|
MessageRetryReason2[(MessageRetryReason2['DecryptionError'] = 9)] = 'DecryptionError'
|
|
2857
3099
|
})(MessageRetryReason || (MessageRetryReason = {}))
|
|
2858
|
-
var MAC_ERROR_CODES =
|
|
3100
|
+
var MAC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
2859
3101
|
MessageRetryReason.SignalErrorBadMac,
|
|
2860
3102
|
MessageRetryReason.SignalErrorInvalidMessage,
|
|
2861
3103
|
MessageRetryReason.SignalErrorNoSession,
|
|
@@ -2900,171 +3142,133 @@ function getRetryReasonDescription(reason) {
|
|
|
2900
3142
|
}
|
|
2901
3143
|
}
|
|
2902
3144
|
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
function mediaFingerprint(media) {
|
|
2906
|
-
if (Buffer.isBuffer(media)) {
|
|
2907
|
-
const len = media.length
|
|
2908
|
-
const head = media.subarray(0, Math.min(24, len)).toString('hex')
|
|
2909
|
-
const tail = len > 24 ? media.subarray(Math.max(24, len - 24)).toString('hex') : ''
|
|
2910
|
-
return `buf:${len}:${head}:${tail}`
|
|
2911
|
-
}
|
|
2912
|
-
if (media && typeof media === 'object' && typeof media.url === 'string') {
|
|
2913
|
-
return `url:${media.url}`
|
|
2914
|
-
}
|
|
2915
|
-
if (typeof media === 'string') {
|
|
2916
|
-
return `str:${media}`
|
|
2917
|
-
}
|
|
2918
|
-
if (media && typeof media === 'object') {
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
return `unresolvable:${Math.random().toString(36)}`
|
|
2923
|
-
}
|
|
2924
|
-
return 'none'
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
function buildContentSignature(content) {
|
|
2928
|
-
if (!content || typeof content !== 'object') return String(content ?? '')
|
|
2929
|
-
const parts = []
|
|
2930
|
-
const text =
|
|
2931
|
-
content.text ||
|
|
2932
|
-
content.caption ||
|
|
2933
|
-
content.image?.caption ||
|
|
2934
|
-
content.video?.caption ||
|
|
2935
|
-
content.document?.caption ||
|
|
2936
|
-
''
|
|
2937
|
-
if (text) parts.push(`text:${text}`)
|
|
2938
|
-
if (content.image !== undefined) parts.push(`image:${mediaFingerprint(content.image)}`)
|
|
2939
|
-
if (content.video !== undefined) parts.push(`video:${mediaFingerprint(content.video)}`)
|
|
2940
|
-
if (content.audio !== undefined) parts.push(`audio:${mediaFingerprint(content.audio)}`)
|
|
2941
|
-
if (content.sticker !== undefined) parts.push(`sticker:${mediaFingerprint(content.sticker)}`)
|
|
2942
|
-
if (content.document !== undefined) {
|
|
2943
|
-
parts.push(`document:${mediaFingerprint(content.document)}:${content.fileName || ''}`)
|
|
2944
|
-
}
|
|
2945
|
-
if (content.location) {
|
|
2946
|
-
parts.push(`location:${content.location.degreesLatitude}:${content.location.degreesLongitude}`)
|
|
2947
|
-
}
|
|
2948
|
-
if (content.contacts) {
|
|
2949
|
-
const vcards = content.contacts.contacts?.map(c => c.vcard).join('|') || JSON.stringify(content.contacts)
|
|
2950
|
-
parts.push(`contacts:${vcards}`)
|
|
2951
|
-
}
|
|
2952
|
-
if (content.poll) {
|
|
2953
|
-
parts.push(`poll:${content.poll.name}:${(content.poll.values || []).join(',')}`)
|
|
2954
|
-
}
|
|
2955
|
-
if (content.buttons) parts.push(`buttons:${JSON.stringify(content.buttons)}`)
|
|
2956
|
-
if (content.templateButtons) parts.push(`template:${JSON.stringify(content.templateButtons)}`)
|
|
2957
|
-
if (content.sections) parts.push(`sections:${JSON.stringify(content.sections)}`)
|
|
2958
|
-
if (content.react) parts.push(`react:${content.react.text}:${content.react.key?.id || ''}`)
|
|
2959
|
-
if (!parts.length) {
|
|
2960
|
-
|
|
2961
|
-
parts.push(`shape:${Object.keys(content).sort().join(',')}`)
|
|
2962
|
-
}
|
|
2963
|
-
return parts.join('|')
|
|
2964
|
-
}
|
|
3145
|
+
// wrapper.js
|
|
2965
3146
|
function wrapSocket(sock, config, warmUpState, wrapOptions) {
|
|
2966
3147
|
const antiban = new AntiBan(config, warmUpState)
|
|
2967
3148
|
const options = {
|
|
2968
3149
|
autoRespondToIncoming: false,
|
|
2969
3150
|
...wrapOptions
|
|
2970
3151
|
}
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown'
|
|
2989
|
-
antiban.onDisconnect(reason)
|
|
2990
|
-
}
|
|
2991
|
-
if (update.connection === 'open') {
|
|
2992
|
-
antiban.onReconnect()
|
|
2993
|
-
}
|
|
2994
|
-
if (update.reachoutTimeLock) {
|
|
2995
|
-
antiban.timelock.onTimelockUpdate({
|
|
2996
|
-
isActive: update.reachoutTimeLock.isActive,
|
|
2997
|
-
timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
|
|
2998
|
-
enforcementType: update.reachoutTimeLock.enforcementType
|
|
2999
|
-
})
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
const handleMessagesUpdate = updates => {
|
|
3003
|
-
for (const update of updates) {
|
|
3004
|
-
if (update?.update?.messageStubParameters) {
|
|
3005
|
-
const params = update.update.messageStubParameters
|
|
3006
|
-
if (params.includes(463) || params.includes('463')) {
|
|
3007
|
-
antiban.timelock.record463Error()
|
|
3152
|
+
if (typeof sock.ev.process === 'function') {
|
|
3153
|
+
sock.ev.process(async events => {
|
|
3154
|
+
if (events['connection.update']) {
|
|
3155
|
+
const update = events['connection.update']
|
|
3156
|
+
if (update.connection === 'close') {
|
|
3157
|
+
const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown'
|
|
3158
|
+
antiban.onDisconnect(reason)
|
|
3159
|
+
}
|
|
3160
|
+
if (update.connection === 'open') {
|
|
3161
|
+
antiban.onReconnect()
|
|
3162
|
+
}
|
|
3163
|
+
if (update.reachoutTimeLock) {
|
|
3164
|
+
antiban.timelock.onTimelockUpdate({
|
|
3165
|
+
isActive: update.reachoutTimeLock.isActive,
|
|
3166
|
+
timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
|
|
3167
|
+
enforcementType: update.reachoutTimeLock.enforcementType
|
|
3168
|
+
})
|
|
3008
3169
|
}
|
|
3009
3170
|
}
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
if (isSelf) continue
|
|
3023
|
-
const msgText =
|
|
3024
|
-
msg.message?.conversation ||
|
|
3025
|
-
msg.message?.extendedTextMessage?.text ||
|
|
3026
|
-
msg.message?.imageMessage?.caption ||
|
|
3027
|
-
msg.message?.videoMessage?.caption ||
|
|
3028
|
-
''
|
|
3029
|
-
const replySuggestion = antiban.onIncomingMessage(jid, msgText)
|
|
3030
|
-
if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
|
|
3031
|
-
scheduleAutoReply(jid, replySuggestion.suggestedText)
|
|
3171
|
+
if (events['messages.update']) {
|
|
3172
|
+
const updates = events['messages.update']
|
|
3173
|
+
for (const update of updates) {
|
|
3174
|
+
if (update?.update?.messageStubParameters) {
|
|
3175
|
+
const params = update.update.messageStubParameters
|
|
3176
|
+
if (params.includes(463) || params.includes('463')) {
|
|
3177
|
+
antiban.timelock.record463Error()
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
antiban.retryTracker.onMessageUpdate(update)
|
|
3181
|
+
}
|
|
3182
|
+
antiban.jidCanonicalizer?.onMessageUpdate(updates)
|
|
3032
3183
|
}
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3184
|
+
if (events['messages.upsert']) {
|
|
3185
|
+
const { messages } = events['messages.upsert']
|
|
3186
|
+
antiban.jidCanonicalizer?.onIncomingEvent(events['messages.upsert'])
|
|
3187
|
+
for (const msg of messages || []) {
|
|
3188
|
+
const jid = msg.key?.remoteJid
|
|
3189
|
+
if (!jid) continue
|
|
3190
|
+
antiban.timelock.registerKnownChat(jid)
|
|
3191
|
+
const isSelf = msg.key?.fromMe || false
|
|
3192
|
+
if (isSelf) continue
|
|
3193
|
+
const msgText =
|
|
3194
|
+
msg.message?.conversation ||
|
|
3195
|
+
msg.message?.extendedTextMessage?.text ||
|
|
3196
|
+
msg.message?.imageMessage?.caption ||
|
|
3197
|
+
msg.message?.videoMessage?.caption ||
|
|
3198
|
+
''
|
|
3199
|
+
const replySuggestion = antiban.onIncomingMessage(jid, msgText)
|
|
3200
|
+
if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
|
|
3201
|
+
const replyDelay = Math.floor(Math.random() * 12e3) + 3e3
|
|
3202
|
+
setTimeout(async () => {
|
|
3203
|
+
try {
|
|
3204
|
+
await sock.sendMessage(jid, { text: replySuggestion.suggestedText })
|
|
3205
|
+
} catch (error) {}
|
|
3206
|
+
}, replyDelay)
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
})
|
|
3046
3211
|
} else {
|
|
3047
|
-
sock.ev.on('connection.update',
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3212
|
+
sock.ev.on('connection.update', update => {
|
|
3213
|
+
if (update.connection === 'close') {
|
|
3214
|
+
const reason = update.lastDisconnect?.error?.output?.statusCode || 'unknown'
|
|
3215
|
+
antiban.onDisconnect(reason)
|
|
3216
|
+
}
|
|
3217
|
+
if (update.connection === 'open') {
|
|
3218
|
+
antiban.onReconnect()
|
|
3219
|
+
}
|
|
3220
|
+
if (update.reachoutTimeLock) {
|
|
3221
|
+
antiban.timelock.onTimelockUpdate({
|
|
3222
|
+
isActive: update.reachoutTimeLock.isActive,
|
|
3223
|
+
timeEnforcementEnds: update.reachoutTimeLock.timeEnforcementEnds,
|
|
3224
|
+
enforcementType: update.reachoutTimeLock.enforcementType
|
|
3225
|
+
})
|
|
3226
|
+
}
|
|
3227
|
+
})
|
|
3228
|
+
sock.ev.on('messages.update', updates => {
|
|
3229
|
+
for (const update of updates) {
|
|
3230
|
+
if (update?.update?.messageStubParameters) {
|
|
3231
|
+
const params = update.update.messageStubParameters
|
|
3232
|
+
if (params.includes(463) || params.includes('463')) {
|
|
3233
|
+
antiban.timelock.record463Error()
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
antiban.retryTracker.onMessageUpdate(update)
|
|
3237
|
+
}
|
|
3238
|
+
antiban.jidCanonicalizer?.onMessageUpdate(updates)
|
|
3239
|
+
})
|
|
3240
|
+
sock.ev.on('messages.upsert', upsert => {
|
|
3241
|
+
const { messages } = upsert
|
|
3242
|
+
antiban.jidCanonicalizer?.onIncomingEvent(upsert)
|
|
3243
|
+
for (const msg of messages || []) {
|
|
3244
|
+
const jid = msg.key?.remoteJid
|
|
3245
|
+
if (!jid) continue
|
|
3246
|
+
antiban.timelock.registerKnownChat(jid)
|
|
3247
|
+
const isSelf = msg.key?.fromMe || false
|
|
3248
|
+
if (isSelf) continue
|
|
3249
|
+
const msgText =
|
|
3250
|
+
msg.message?.conversation ||
|
|
3251
|
+
msg.message?.extendedTextMessage?.text ||
|
|
3252
|
+
msg.message?.imageMessage?.caption ||
|
|
3253
|
+
msg.message?.videoMessage?.caption ||
|
|
3254
|
+
''
|
|
3255
|
+
const replySuggestion = antiban.onIncomingMessage(jid, msgText)
|
|
3256
|
+
if (options.autoRespondToIncoming && replySuggestion.shouldReply && replySuggestion.suggestedText) {
|
|
3257
|
+
const replyDelay = Math.floor(Math.random() * 12e3) + 3e3
|
|
3258
|
+
setTimeout(async () => {
|
|
3259
|
+
try {
|
|
3260
|
+
await sock.sendMessage(jid, { text: replySuggestion.suggestedText })
|
|
3261
|
+
} catch (error) {}
|
|
3262
|
+
}, replyDelay)
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
})
|
|
3055
3266
|
}
|
|
3056
3267
|
const originalSendMessage = sock.sendMessage.bind(sock)
|
|
3057
3268
|
const wrappedSendMessage = async (jid, content, options2) => {
|
|
3058
3269
|
const canonicalJid = antiban.jidCanonicalizer?.canonicalizeTarget(jid) || jid
|
|
3059
|
-
const text =
|
|
3060
|
-
|
|
3061
|
-
content?.caption ||
|
|
3062
|
-
content?.image?.caption ||
|
|
3063
|
-
content?.video?.caption ||
|
|
3064
|
-
content?.document?.caption ||
|
|
3065
|
-
''
|
|
3066
|
-
const dedupKey = buildContentSignature(content)
|
|
3067
|
-
const decision = await antiban.beforeSend(canonicalJid, text, dedupKey)
|
|
3270
|
+
const text = content?.text || content?.caption || content?.image?.caption || ''
|
|
3271
|
+
const decision = await antiban.beforeSend(canonicalJid, text)
|
|
3068
3272
|
if (!decision.allowed) {
|
|
3069
3273
|
throw new Error(`[baileys-antiban] Message blocked: ${decision.reason}`)
|
|
3070
3274
|
}
|
|
@@ -3073,7 +3277,7 @@ function wrapSocket(sock, config, warmUpState, wrapOptions) {
|
|
|
3073
3277
|
}
|
|
3074
3278
|
try {
|
|
3075
3279
|
const result = await originalSendMessage(canonicalJid, content, options2)
|
|
3076
|
-
antiban.afterSend(canonicalJid, text
|
|
3280
|
+
antiban.afterSend(canonicalJid, text)
|
|
3077
3281
|
antiban.timelock.registerKnownChat(canonicalJid)
|
|
3078
3282
|
if (result?.key?.id) {
|
|
3079
3283
|
antiban.retryTracker.clear(result.key.id)
|
|
@@ -3087,17 +3291,11 @@ function wrapSocket(sock, config, warmUpState, wrapOptions) {
|
|
|
3087
3291
|
const wrapped = Object.create(sock)
|
|
3088
3292
|
wrapped.sendMessage = wrappedSendMessage
|
|
3089
3293
|
wrapped.antiban = antiban
|
|
3090
|
-
|
|
3091
|
-
wrapped.antiban.destroy = () => {
|
|
3092
|
-
stopEventBridge()
|
|
3093
|
-
for (const timer of pendingAutoReplyTimers) clearTimeout(timer)
|
|
3094
|
-
pendingAutoReplyTimers.clear()
|
|
3095
|
-
originalDestroy()
|
|
3096
|
-
}
|
|
3294
|
+
wrapped.antiban.destroy = antiban.destroy.bind(antiban)
|
|
3097
3295
|
return wrapped
|
|
3098
3296
|
}
|
|
3099
3297
|
|
|
3100
|
-
|
|
3298
|
+
// messageQueue.js
|
|
3101
3299
|
var import_events = require('events')
|
|
3102
3300
|
var DEFAULT_CONFIG12 = {
|
|
3103
3301
|
maxAttempts: 3,
|
|
@@ -3116,11 +3314,16 @@ var MessageQueue = class extends import_events.EventEmitter {
|
|
|
3116
3314
|
super()
|
|
3117
3315
|
this.config = { ...DEFAULT_CONFIG12, ...config }
|
|
3118
3316
|
}
|
|
3119
|
-
|
|
3317
|
+
/**
|
|
3318
|
+
* Set the send function (called for each message when drained)
|
|
3319
|
+
* This should be the anti-ban wrapped sendMessage
|
|
3320
|
+
*/
|
|
3120
3321
|
setSendFunction(fn) {
|
|
3121
3322
|
this.sendFn = fn
|
|
3122
3323
|
}
|
|
3123
|
-
|
|
3324
|
+
/**
|
|
3325
|
+
* Add a message to the queue
|
|
3326
|
+
*/
|
|
3124
3327
|
add(recipient, content, options) {
|
|
3125
3328
|
if (this.queue.length >= this.config.maxQueueSize) {
|
|
3126
3329
|
throw new Error(`Queue full (${this.config.maxQueueSize} messages)`)
|
|
@@ -3142,17 +3345,23 @@ var MessageQueue = class extends import_events.EventEmitter {
|
|
|
3142
3345
|
this.emit('added', message)
|
|
3143
3346
|
return id
|
|
3144
3347
|
}
|
|
3145
|
-
|
|
3348
|
+
/**
|
|
3349
|
+
* Add multiple messages (e.g., broadcast to many recipients)
|
|
3350
|
+
*/
|
|
3146
3351
|
addBulk(recipients, content, options) {
|
|
3147
3352
|
return recipients.map(r => this.add(r, content, options))
|
|
3148
3353
|
}
|
|
3149
|
-
|
|
3354
|
+
/**
|
|
3355
|
+
* Start processing the queue
|
|
3356
|
+
*/
|
|
3150
3357
|
start(intervalMs = 1e3) {
|
|
3151
3358
|
if (this.drainTimer) return
|
|
3152
3359
|
this.drainTimer = setInterval(() => this.processNext(), intervalMs)
|
|
3153
3360
|
this.emit('started')
|
|
3154
3361
|
}
|
|
3155
|
-
|
|
3362
|
+
/**
|
|
3363
|
+
* Stop processing
|
|
3364
|
+
*/
|
|
3156
3365
|
stop() {
|
|
3157
3366
|
if (this.drainTimer) {
|
|
3158
3367
|
clearInterval(this.drainTimer)
|
|
@@ -3160,11 +3369,16 @@ var MessageQueue = class extends import_events.EventEmitter {
|
|
|
3160
3369
|
}
|
|
3161
3370
|
this.emit('stopped')
|
|
3162
3371
|
}
|
|
3163
|
-
|
|
3372
|
+
/**
|
|
3373
|
+
* Clean up all timers and resources.
|
|
3374
|
+
* Call this when disposing of the queue.
|
|
3375
|
+
*/
|
|
3164
3376
|
destroy() {
|
|
3165
3377
|
this.stop()
|
|
3166
3378
|
}
|
|
3167
|
-
|
|
3379
|
+
/**
|
|
3380
|
+
* Process the next message in the queue
|
|
3381
|
+
*/
|
|
3168
3382
|
async processNext() {
|
|
3169
3383
|
if (this.processing || !this.sendFn) return
|
|
3170
3384
|
const now = Date.now()
|
|
@@ -3193,7 +3407,9 @@ var MessageQueue = class extends import_events.EventEmitter {
|
|
|
3193
3407
|
this.processing = false
|
|
3194
3408
|
}
|
|
3195
3409
|
}
|
|
3196
|
-
|
|
3410
|
+
/**
|
|
3411
|
+
* Get queue stats
|
|
3412
|
+
*/
|
|
3197
3413
|
getStats() {
|
|
3198
3414
|
const now = Date.now()
|
|
3199
3415
|
return {
|
|
@@ -3209,23 +3425,31 @@ var MessageQueue = class extends import_events.EventEmitter {
|
|
|
3209
3425
|
isRunning: this.drainTimer !== null
|
|
3210
3426
|
}
|
|
3211
3427
|
}
|
|
3212
|
-
|
|
3428
|
+
/**
|
|
3429
|
+
* Clear all messages
|
|
3430
|
+
*/
|
|
3213
3431
|
clear() {
|
|
3214
3432
|
const count = this.queue.length
|
|
3215
3433
|
this.queue = []
|
|
3216
3434
|
this.emit('cleared', count)
|
|
3217
3435
|
}
|
|
3218
|
-
|
|
3436
|
+
/**
|
|
3437
|
+
* Remove a specific message
|
|
3438
|
+
*/
|
|
3219
3439
|
remove(id) {
|
|
3220
3440
|
const before = this.queue.length
|
|
3221
3441
|
this.queue = this.queue.filter(m => m.id !== id)
|
|
3222
3442
|
return this.queue.length < before
|
|
3223
3443
|
}
|
|
3224
|
-
|
|
3444
|
+
/**
|
|
3445
|
+
* Export queue for persistence
|
|
3446
|
+
*/
|
|
3225
3447
|
export() {
|
|
3226
3448
|
return [...this.queue]
|
|
3227
3449
|
}
|
|
3228
|
-
|
|
3450
|
+
/**
|
|
3451
|
+
* Import queue (e.g., after restart)
|
|
3452
|
+
*/
|
|
3229
3453
|
import(messages) {
|
|
3230
3454
|
this.queue = [...messages]
|
|
3231
3455
|
this.sortQueue()
|
|
@@ -3241,7 +3465,7 @@ var MessageQueue = class extends import_events.EventEmitter {
|
|
|
3241
3465
|
}
|
|
3242
3466
|
}
|
|
3243
3467
|
|
|
3244
|
-
|
|
3468
|
+
// contentVariator.js
|
|
3245
3469
|
var DEFAULT_CONFIG13 = {
|
|
3246
3470
|
zeroWidthChars: true,
|
|
3247
3471
|
punctuationVariation: true,
|
|
@@ -3250,13 +3474,13 @@ var DEFAULT_CONFIG13 = {
|
|
|
3250
3474
|
}
|
|
3251
3475
|
var ZERO_WIDTH = [
|
|
3252
3476
|
'\u200B',
|
|
3253
|
-
|
|
3477
|
+
// zero-width space
|
|
3254
3478
|
'\u200C',
|
|
3255
|
-
|
|
3479
|
+
// zero-width non-joiner
|
|
3256
3480
|
'\u200D',
|
|
3257
|
-
|
|
3481
|
+
// zero-width joiner
|
|
3258
3482
|
'\uFEFF'
|
|
3259
|
-
|
|
3483
|
+
// zero-width no-break space
|
|
3260
3484
|
]
|
|
3261
3485
|
var SYNONYMS = {
|
|
3262
3486
|
hello: ['hi', 'hey', 'howdy'],
|
|
@@ -3283,7 +3507,10 @@ var ContentVariator = class {
|
|
|
3283
3507
|
constructor(config = {}) {
|
|
3284
3508
|
this.config = { ...DEFAULT_CONFIG13, ...config }
|
|
3285
3509
|
}
|
|
3286
|
-
|
|
3510
|
+
/**
|
|
3511
|
+
* Create a unique variation of a message
|
|
3512
|
+
* Each call produces a slightly different version
|
|
3513
|
+
*/
|
|
3287
3514
|
vary(text) {
|
|
3288
3515
|
let result = text
|
|
3289
3516
|
this.counter++
|
|
@@ -3304,15 +3531,22 @@ var ContentVariator = class {
|
|
|
3304
3531
|
}
|
|
3305
3532
|
return result
|
|
3306
3533
|
}
|
|
3307
|
-
|
|
3534
|
+
/**
|
|
3535
|
+
* Create N unique variations of a message
|
|
3536
|
+
*/
|
|
3308
3537
|
varyBulk(text, count) {
|
|
3309
3538
|
const results = []
|
|
3310
|
-
const
|
|
3539
|
+
const seen = /* @__PURE__ */ new Set()
|
|
3311
3540
|
for (let i = 0; i < count; i++) {
|
|
3312
|
-
|
|
3313
|
-
|
|
3541
|
+
let variation = this.vary(text)
|
|
3542
|
+
let attempts = 0
|
|
3543
|
+
while (seen.has(variation) && attempts < 10) {
|
|
3544
|
+
variation = this.vary(text)
|
|
3545
|
+
attempts++
|
|
3546
|
+
}
|
|
3547
|
+
seen.add(variation)
|
|
3548
|
+
results.push(variation)
|
|
3314
3549
|
}
|
|
3315
|
-
this.counter = startCounter + count
|
|
3316
3550
|
return results
|
|
3317
3551
|
}
|
|
3318
3552
|
addZeroWidth(text) {
|
|
@@ -3331,14 +3565,14 @@ var ContentVariator = class {
|
|
|
3331
3565
|
}
|
|
3332
3566
|
varyPunctuation(text) {
|
|
3333
3567
|
const variations = [
|
|
3334
|
-
|
|
3568
|
+
// Trailing space variations
|
|
3335
3569
|
() => text + ' ',
|
|
3336
3570
|
() => text + ' ',
|
|
3337
|
-
|
|
3571
|
+
// Period variations
|
|
3338
3572
|
() => (text.endsWith('.') ? text.slice(0, -1) : text + '.'),
|
|
3339
|
-
|
|
3573
|
+
// Nothing
|
|
3340
3574
|
() => text,
|
|
3341
|
-
|
|
3575
|
+
// Capitalize first letter variation
|
|
3342
3576
|
() => (text.charAt(0) === text.charAt(0).toUpperCase() ? text.charAt(0).toLowerCase() + text.slice(1) : text)
|
|
3343
3577
|
]
|
|
3344
3578
|
return variations[this.counter % variations.length]()
|
|
@@ -3348,18 +3582,21 @@ var ContentVariator = class {
|
|
|
3348
3582
|
return text + emojis[this.counter % emojis.length]
|
|
3349
3583
|
}
|
|
3350
3584
|
applySynonyms(text) {
|
|
3351
|
-
const
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3585
|
+
const words = text.split(/\b/)
|
|
3586
|
+
let replaced = false
|
|
3587
|
+
return words
|
|
3588
|
+
.map(word => {
|
|
3589
|
+
if (replaced) return word
|
|
3590
|
+
const lower = word.toLowerCase()
|
|
3591
|
+
const synonymList = SYNONYMS[lower]
|
|
3592
|
+
if (synonymList && Math.random() > 0.5) {
|
|
3593
|
+
replaced = true
|
|
3594
|
+
const synonym = synonymList[Math.floor(Math.random() * synonymList.length)]
|
|
3595
|
+
return word[0] === word[0].toUpperCase() ? synonym.charAt(0).toUpperCase() + synonym.slice(1) : synonym
|
|
3596
|
+
}
|
|
3597
|
+
return word
|
|
3598
|
+
})
|
|
3599
|
+
.join('')
|
|
3363
3600
|
}
|
|
3364
3601
|
randomPositions(max, count) {
|
|
3365
3602
|
const positions = []
|
|
@@ -3371,7 +3608,7 @@ var ContentVariator = class {
|
|
|
3371
3608
|
}
|
|
3372
3609
|
}
|
|
3373
3610
|
|
|
3374
|
-
|
|
3611
|
+
// webhooks.js
|
|
3375
3612
|
var DEFAULT_CONFIG14 = {
|
|
3376
3613
|
urls: [],
|
|
3377
3614
|
minRiskLevel: 'medium',
|
|
@@ -3384,7 +3621,9 @@ var WebhookAlerts = class {
|
|
|
3384
3621
|
constructor(config = {}) {
|
|
3385
3622
|
this.config = { ...DEFAULT_CONFIG14, ...config }
|
|
3386
3623
|
}
|
|
3387
|
-
|
|
3624
|
+
/**
|
|
3625
|
+
* Send alert if risk level warrants it
|
|
3626
|
+
*/
|
|
3388
3627
|
async alert(data) {
|
|
3389
3628
|
const riskOrder = ['low', 'medium', 'high', 'critical']
|
|
3390
3629
|
if (riskOrder.indexOf(data.risk) < riskOrder.indexOf(this.config.minRiskLevel)) {
|
|
@@ -3397,7 +3636,7 @@ var WebhookAlerts = class {
|
|
|
3397
3636
|
this.lastAlertTime = now
|
|
3398
3637
|
const payload = {
|
|
3399
3638
|
source: 'baileys-antiban',
|
|
3400
|
-
timestamp:
|
|
3639
|
+
timestamp: /* @__PURE__ */ new Date().toISOString(),
|
|
3401
3640
|
...data
|
|
3402
3641
|
}
|
|
3403
3642
|
for (const url of this.config.urls) {
|
|
@@ -3432,7 +3671,7 @@ ${data.reasons.map(r => `\u2022 ${r}`).join('\n')}`
|
|
|
3432
3671
|
{ name: 'Recommendation', value: data.recommendation },
|
|
3433
3672
|
{ name: 'Reasons', value: data.reasons.join('\n') }
|
|
3434
3673
|
],
|
|
3435
|
-
timestamp:
|
|
3674
|
+
timestamp: /* @__PURE__ */ new Date().toISOString()
|
|
3436
3675
|
}
|
|
3437
3676
|
]
|
|
3438
3677
|
}).catch(() => {})
|
|
@@ -3457,7 +3696,7 @@ ${data.reasons.map(r => `\u2022 ${r}`).join('\n')}`
|
|
|
3457
3696
|
}
|
|
3458
3697
|
}
|
|
3459
3698
|
|
|
3460
|
-
|
|
3699
|
+
// scheduler.js
|
|
3461
3700
|
var DEFAULT_CONFIG15 = {
|
|
3462
3701
|
timezone: 'UTC',
|
|
3463
3702
|
activeHours: [8, 21],
|
|
@@ -3472,13 +3711,18 @@ var Scheduler = class {
|
|
|
3472
3711
|
constructor(config = {}) {
|
|
3473
3712
|
this.config = { ...DEFAULT_CONFIG15, ...config }
|
|
3474
3713
|
}
|
|
3475
|
-
|
|
3714
|
+
/**
|
|
3715
|
+
* Check if now is within active hours
|
|
3716
|
+
*/
|
|
3476
3717
|
isActiveTime() {
|
|
3477
3718
|
const hour = this.getCurrentHour()
|
|
3478
3719
|
const [start, end] = this.config.activeHours
|
|
3479
3720
|
return hour >= start && hour < end
|
|
3480
3721
|
}
|
|
3481
|
-
|
|
3722
|
+
/**
|
|
3723
|
+
* Get the speed multiplier for current time
|
|
3724
|
+
* > 1 = faster, < 1 = slower, 0 = don't send
|
|
3725
|
+
*/
|
|
3482
3726
|
getSpeedFactor() {
|
|
3483
3727
|
if (!this.isActiveTime()) return 0
|
|
3484
3728
|
const hour = this.getCurrentHour()
|
|
@@ -3497,10 +3741,12 @@ var Scheduler = class {
|
|
|
3497
3741
|
}
|
|
3498
3742
|
return factor
|
|
3499
3743
|
}
|
|
3500
|
-
|
|
3744
|
+
/**
|
|
3745
|
+
* Get ms until next active window
|
|
3746
|
+
*/
|
|
3501
3747
|
msUntilActive() {
|
|
3502
3748
|
if (this.isActiveTime()) return 0
|
|
3503
|
-
const now =
|
|
3749
|
+
const now = /* @__PURE__ */ new Date()
|
|
3504
3750
|
const hour = now.getHours()
|
|
3505
3751
|
const [start] = this.config.activeHours
|
|
3506
3752
|
let nextActive
|
|
@@ -3514,13 +3760,17 @@ var Scheduler = class {
|
|
|
3514
3760
|
}
|
|
3515
3761
|
return nextActive.getTime() - now.getTime()
|
|
3516
3762
|
}
|
|
3517
|
-
|
|
3763
|
+
/**
|
|
3764
|
+
* Adjust a delay based on current time factors
|
|
3765
|
+
*/
|
|
3518
3766
|
adjustDelay(baseDelayMs) {
|
|
3519
3767
|
const factor = this.getSpeedFactor()
|
|
3520
3768
|
if (factor === 0) return -1
|
|
3521
3769
|
return Math.round(baseDelayMs / factor)
|
|
3522
3770
|
}
|
|
3523
|
-
|
|
3771
|
+
/**
|
|
3772
|
+
* Get current schedule status
|
|
3773
|
+
*/
|
|
3524
3774
|
getStatus() {
|
|
3525
3775
|
const hour = this.getCurrentHour()
|
|
3526
3776
|
const day = this.getCurrentDay()
|
|
@@ -3536,14 +3786,14 @@ var Scheduler = class {
|
|
|
3536
3786
|
}
|
|
3537
3787
|
}
|
|
3538
3788
|
getCurrentHour() {
|
|
3539
|
-
return
|
|
3789
|
+
return /* @__PURE__ */ new Date().getHours()
|
|
3540
3790
|
}
|
|
3541
3791
|
getCurrentDay() {
|
|
3542
|
-
return
|
|
3792
|
+
return /* @__PURE__ */ new Date().getDay()
|
|
3543
3793
|
}
|
|
3544
3794
|
}
|
|
3545
3795
|
|
|
3546
|
-
|
|
3796
|
+
// stateAdapter.js
|
|
3547
3797
|
var _fsPromises = require('fs/promises')
|
|
3548
3798
|
var _fsSync = require('fs')
|
|
3549
3799
|
var _path3 = require('path')
|
|
@@ -3586,11 +3836,11 @@ var FileStateAdapter = class {
|
|
|
3586
3836
|
}
|
|
3587
3837
|
}
|
|
3588
3838
|
|
|
3589
|
-
|
|
3839
|
+
// messageRecovery.js
|
|
3590
3840
|
var DEFAULT_CONFIG16 = {
|
|
3591
3841
|
maxTrackedChats: 1e3,
|
|
3592
3842
|
maxGapMs: 30 * 6e4,
|
|
3593
|
-
|
|
3843
|
+
// 30 minutes
|
|
3594
3844
|
persistDebounceMs: 2e3,
|
|
3595
3845
|
onGapFilled: () => {},
|
|
3596
3846
|
logger: {
|
|
@@ -3602,7 +3852,7 @@ var DEFAULT_CONFIG16 = {
|
|
|
3602
3852
|
function messageRecovery(sock, config) {
|
|
3603
3853
|
const cfg = { ...DEFAULT_CONFIG16, ...config }
|
|
3604
3854
|
const logger = cfg.logger
|
|
3605
|
-
const lastSeen =
|
|
3855
|
+
const lastSeen = /* @__PURE__ */ new Map()
|
|
3606
3856
|
let disconnectedAt = null
|
|
3607
3857
|
let totalRecovered = 0
|
|
3608
3858
|
let lastReconnectAt = null
|
|
@@ -3702,7 +3952,7 @@ function messageRecovery(sock, config) {
|
|
|
3702
3952
|
loggedFetchWarning = true
|
|
3703
3953
|
}
|
|
3704
3954
|
disconnectedAt = null
|
|
3705
|
-
lastReconnectAt =
|
|
3955
|
+
lastReconnectAt = /* @__PURE__ */ new Date()
|
|
3706
3956
|
lastGapMs = gapMs
|
|
3707
3957
|
await cfg.onRecoveryComplete?.({
|
|
3708
3958
|
chats: 0,
|
|
@@ -3715,7 +3965,7 @@ function messageRecovery(sock, config) {
|
|
|
3715
3965
|
try {
|
|
3716
3966
|
const messages = await sock.fetchMessageHistory(jid, 50, {
|
|
3717
3967
|
before: void 0
|
|
3718
|
-
|
|
3968
|
+
// Get latest
|
|
3719
3969
|
})
|
|
3720
3970
|
if (!messages || !Array.isArray(messages)) continue
|
|
3721
3971
|
const gapMessages = messages.filter(msg => {
|
|
@@ -3748,7 +3998,7 @@ function messageRecovery(sock, config) {
|
|
|
3748
3998
|
}
|
|
3749
3999
|
}
|
|
3750
4000
|
totalRecovered += recovered
|
|
3751
|
-
lastReconnectAt =
|
|
4001
|
+
lastReconnectAt = /* @__PURE__ */ new Date()
|
|
3752
4002
|
lastGapMs = gapMs
|
|
3753
4003
|
disconnectedAt = null
|
|
3754
4004
|
logger.info?.(
|
|
@@ -3835,33 +4085,29 @@ function messageRecovery(sock, config) {
|
|
|
3835
4085
|
}
|
|
3836
4086
|
}
|
|
3837
4087
|
|
|
3838
|
-
|
|
4088
|
+
// deviceFingerprint.js
|
|
3839
4089
|
var DEFAULT_APP_VERSION_POOL = [
|
|
3840
|
-
[2,
|
|
3841
|
-
[2,
|
|
3842
|
-
[2,
|
|
3843
|
-
[2,
|
|
3844
|
-
[2,
|
|
3845
|
-
[2,
|
|
3846
|
-
[2, 24, 22, 78],
|
|
3847
|
-
[2, 24, 20, 86]
|
|
4090
|
+
[2, 24, 5, 18],
|
|
4091
|
+
[2, 24, 5, 17],
|
|
4092
|
+
[2, 24, 4, 77],
|
|
4093
|
+
[2, 24, 5, 15],
|
|
4094
|
+
[2, 24, 3, 91],
|
|
4095
|
+
[2, 24, 5, 20]
|
|
3848
4096
|
]
|
|
3849
|
-
var DEFAULT_OS_VERSION_POOL = ['11', '12', '13', '14'
|
|
4097
|
+
var DEFAULT_OS_VERSION_POOL = ['10', '11', '12', '13', '14']
|
|
3850
4098
|
var DEFAULT_DEVICE_MODEL_POOL = [
|
|
3851
|
-
'Pixel
|
|
3852
|
-
'Pixel 9',
|
|
4099
|
+
'Pixel 6',
|
|
3853
4100
|
'Pixel 7',
|
|
3854
|
-
'Galaxy S24',
|
|
3855
|
-
'Galaxy S23',
|
|
3856
4101
|
'Galaxy S22',
|
|
3857
|
-
'
|
|
4102
|
+
'Galaxy S23',
|
|
3858
4103
|
'Xiaomi 13',
|
|
3859
|
-
'
|
|
4104
|
+
'Xiaomi 12',
|
|
3860
4105
|
'OnePlus 11',
|
|
3861
4106
|
'Moto G84',
|
|
3862
|
-
'
|
|
3863
|
-
'
|
|
3864
|
-
'
|
|
4107
|
+
'Moto G54',
|
|
4108
|
+
'Realme 11',
|
|
4109
|
+
'Vivo V29',
|
|
4110
|
+
'Oppo Find X6'
|
|
3865
4111
|
]
|
|
3866
4112
|
var SeededRandom = class {
|
|
3867
4113
|
state
|
|
@@ -3901,7 +4147,7 @@ function generateFingerprint(config = {}, sessionId) {
|
|
|
3901
4147
|
const deviceModel = enabled && randomizeDeviceModel ? rng.pick(deviceModelPool) : deviceModelPool[0]
|
|
3902
4148
|
return {
|
|
3903
4149
|
appVersion: [...appVersion],
|
|
3904
|
-
|
|
4150
|
+
// Copy to avoid mutation
|
|
3905
4151
|
osVersion,
|
|
3906
4152
|
deviceModel,
|
|
3907
4153
|
sessionId: finalSessionId
|
|
@@ -3918,7 +4164,7 @@ function applyFingerprint(socketConfig, fp) {
|
|
|
3918
4164
|
return config
|
|
3919
4165
|
}
|
|
3920
4166
|
|
|
3921
|
-
|
|
4167
|
+
// credsSnapshot.js
|
|
3922
4168
|
var import_fs = require('fs')
|
|
3923
4169
|
var path2 = __toESM(require('path'), 1)
|
|
3924
4170
|
var noop = () => {}
|
|
@@ -3938,7 +4184,7 @@ function credsSnapshot(config) {
|
|
|
3938
4184
|
return null
|
|
3939
4185
|
}
|
|
3940
4186
|
await import_fs.promises.mkdir(snapshotDir, { recursive: true })
|
|
3941
|
-
const timestamp =
|
|
4187
|
+
const timestamp = /* @__PURE__ */ new Date().toISOString().replace(/[:.]/g, '-')
|
|
3942
4188
|
const snapshotPath = path2.join(snapshotDir, `creds-${timestamp}.json`)
|
|
3943
4189
|
const tmpPath = `${snapshotPath}.tmp`
|
|
3944
4190
|
await import_fs.promises.copyFile(credsPath, tmpPath)
|
|
@@ -4019,7 +4265,7 @@ function credsSnapshot(config) {
|
|
|
4019
4265
|
}
|
|
4020
4266
|
}
|
|
4021
4267
|
|
|
4022
|
-
|
|
4268
|
+
// readReceiptVariance.js
|
|
4023
4269
|
function gaussianRandom() {
|
|
4024
4270
|
let u = 0
|
|
4025
4271
|
let v = 0
|
|
@@ -4029,7 +4275,7 @@ function gaussianRandom() {
|
|
|
4029
4275
|
}
|
|
4030
4276
|
function readReceiptVariance(config = {}) {
|
|
4031
4277
|
const { meanMs = 1500, stdDevMs = 800, minMs = 200, maxMs = 8e3, skipIfOlderThanMs = 6e4 } = config
|
|
4032
|
-
const pendingTimers =
|
|
4278
|
+
const pendingTimers = /* @__PURE__ */ new Set()
|
|
4033
4279
|
function delayMs() {
|
|
4034
4280
|
const gaussian = gaussianRandom()
|
|
4035
4281
|
const value = meanMs + gaussian * stdDevMs
|
|
@@ -4086,7 +4332,7 @@ function readReceiptVariance(config = {}) {
|
|
|
4086
4332
|
}
|
|
4087
4333
|
}
|
|
4088
4334
|
|
|
4089
|
-
|
|
4335
|
+
// proxyRotator.js
|
|
4090
4336
|
var import_node_module = require('node:module')
|
|
4091
4337
|
var require2 = (0, import_node_module.createRequire)(__importMetaUrl)
|
|
4092
4338
|
var NoopLogger = {
|
|
@@ -4102,7 +4348,7 @@ function proxyRotator(config) {
|
|
|
4102
4348
|
scheduledIntervalMs = 0,
|
|
4103
4349
|
maxFailures = 3,
|
|
4104
4350
|
deadCooldownMs = 6e5,
|
|
4105
|
-
|
|
4351
|
+
// 10 minutes
|
|
4106
4352
|
logger = NoopLogger
|
|
4107
4353
|
} = config
|
|
4108
4354
|
if (!pool || pool.length === 0) {
|
|
@@ -4124,7 +4370,7 @@ function proxyRotator(config) {
|
|
|
4124
4370
|
let totalRotations = 0
|
|
4125
4371
|
const rotationsByTrigger = {}
|
|
4126
4372
|
let scheduledTimer = null
|
|
4127
|
-
const agentCache =
|
|
4373
|
+
const agentCache = /* @__PURE__ */ new Map()
|
|
4128
4374
|
const moduleCache = {}
|
|
4129
4375
|
function buildProxyUrl(endpoint) {
|
|
4130
4376
|
const { type, host, port, username, password } = endpoint
|
|
@@ -4263,7 +4509,7 @@ function proxyRotator(config) {
|
|
|
4263
4509
|
} else {
|
|
4264
4510
|
currentIndex = nextIdx
|
|
4265
4511
|
}
|
|
4266
|
-
states[currentIndex].lastUsedAt =
|
|
4512
|
+
states[currentIndex].lastUsedAt = /* @__PURE__ */ new Date()
|
|
4267
4513
|
totalRotations++
|
|
4268
4514
|
rotationsByTrigger[reason] = (rotationsByTrigger[reason] || 0) + 1
|
|
4269
4515
|
const label = states[currentIndex].endpoint.label || states[currentIndex].endpoint.host
|
|
@@ -4331,7 +4577,7 @@ function proxyRotator(config) {
|
|
|
4331
4577
|
}, scheduledIntervalMs)
|
|
4332
4578
|
logger.info?.(`Scheduled rotation enabled (every ${scheduledIntervalMs}ms)`)
|
|
4333
4579
|
}
|
|
4334
|
-
states[0].lastUsedAt =
|
|
4580
|
+
states[0].lastUsedAt = /* @__PURE__ */ new Date()
|
|
4335
4581
|
return {
|
|
4336
4582
|
currentAgent: currentAgentImpl,
|
|
4337
4583
|
current: currentImpl,
|
|
@@ -4342,7 +4588,7 @@ function proxyRotator(config) {
|
|
|
4342
4588
|
getStats: getStatsImpl
|
|
4343
4589
|
}
|
|
4344
4590
|
}
|
|
4345
|
-
|
|
4591
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4346
4592
|
0 &&
|
|
4347
4593
|
(module.exports = {
|
|
4348
4594
|
AntiBan,
|