baileys-antiban 3.8.9 → 3.8.11
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/CHANGELOG.md +10 -0
- package/README.md +54 -7
- package/dist/antiban.js +9 -0
- package/dist/cjs/antiban.js +9 -0
- package/dist/cjs/persist.js +10 -2
- package/dist/cjs/proxyRotator.js +9 -10
- package/dist/cjs/rateLimiter.js +9 -0
- package/dist/persist.js +10 -2
- package/dist/proxyRotator.js +9 -10
- package/dist/rateLimiter.js +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.8.9] - 2026-05-19
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **`maxIdenticalMessages`, `identicalMessageWindowMs`, `burstAllowance` silently ignored.** These fields were missing from `ResolvedConfig` so passing them in flat config had no effect — the `RateLimiter` used its hardcoded defaults (3/3/3600000). All three are now in `ResolvedConfig`, set via preset defaults, and forwarded to `RateLimiter`. Reported in #8.
|
|
12
|
+
- **Flat top-level fields dropped when legacy config is detected.** If a config object contained any nested key (`health`, `timelock`, `jidCanonicalizer`, `lidResolver`, etc.), `isLegacyConfig()` returned true and `mapLegacyToFlat()` only extracted fields from nested sub-objects — silently dropping flat top-level fields like `warmUpDays`, `day1Limit`, `growthFactor`, `inactivityThresholdHours`, `maxIdenticalMessages`, `burstAllowance`. Mixed configs (flat rate/warmup fields + nested callback objects) now preserve the flat fields correctly. Reported in #8.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **`AntiBan.getConfig()`** — returns a copy of the effective resolved config after preset merging and defaults. Useful for debugging which values are actually in use.
|
|
16
|
+
- **Preset defaults for new fields** — `maxIdenticalMessages`: 3/5/10/20, `identicalMessageWindowMs`: 3600000 (all presets), `burstAllowance`: 3/5/8/15 for conservative/moderate/aggressive/high-volume.
|
|
17
|
+
|
|
8
18
|
## [3.8.8] - 2026-05-15
|
|
9
19
|
|
|
10
20
|
### Added
|
package/README.md
CHANGED
|
@@ -574,14 +574,38 @@ const ab = new AntiBan();
|
|
|
574
574
|
// Or pick a preset
|
|
575
575
|
const ab = new AntiBan('moderate');
|
|
576
576
|
|
|
577
|
-
// Full control
|
|
577
|
+
// Full control — all ResolvedConfig fields (import ResolvedConfig for type safety)
|
|
578
578
|
const ab = new AntiBan({
|
|
579
|
-
preset: 'moderate',
|
|
579
|
+
preset: 'moderate', // base preset: conservative|moderate|aggressive|high-volume
|
|
580
|
+
// Rate limits
|
|
581
|
+
maxPerMinute: 15, // override any field from the preset
|
|
582
|
+
maxPerHour: 400,
|
|
583
|
+
maxPerDay: 2000,
|
|
584
|
+
minDelayMs: 1000,
|
|
585
|
+
maxDelayMs: 4000,
|
|
586
|
+
newChatDelayMs: 2500,
|
|
587
|
+
// Identical message protection
|
|
588
|
+
maxIdenticalMessages: 5, // block after N identical msgs in window (default: 5)
|
|
589
|
+
identicalMessageWindowMs: 3600000, // tracking window in ms (default: 1h)
|
|
590
|
+
burstAllowance: 5, // fast messages before throttle kicks in
|
|
591
|
+
// Warm-up
|
|
592
|
+
warmupDays: 7,
|
|
593
|
+
day1Limit: 20,
|
|
594
|
+
growthFactor: 1.8,
|
|
595
|
+
inactivityThresholdHours: 72,
|
|
596
|
+
// Health
|
|
597
|
+
autoPauseAt: 'high', // pause at: low|medium|high|critical
|
|
598
|
+
// Groups
|
|
599
|
+
groupProfiles: true,
|
|
600
|
+
groupMultiplier: 0.7,
|
|
601
|
+
// Persistence
|
|
580
602
|
persist: './antiban-state.json', // survives restarts
|
|
581
|
-
|
|
582
|
-
maxPerMinute: 15, // override any value
|
|
603
|
+
logging: true,
|
|
583
604
|
});
|
|
584
605
|
|
|
606
|
+
// Debug: see the effective config after preset merging
|
|
607
|
+
console.log(ab.getConfig());
|
|
608
|
+
|
|
585
609
|
// Usage unchanged
|
|
586
610
|
const result = await ab.beforeSend(jid, text);
|
|
587
611
|
if (result.allowed) {
|
|
@@ -637,7 +661,30 @@ try {
|
|
|
637
661
|
}
|
|
638
662
|
```
|
|
639
663
|
|
|
640
|
-
|
|
664
|
+
### `wrapSocket` with optional features
|
|
665
|
+
|
|
666
|
+
`deafSession` (deaf-session detector) and `autoRespondToIncoming` are **wrapOptions** — the 4th argument to `wrapSocket`, separate from the antiban config:
|
|
667
|
+
|
|
668
|
+
```typescript
|
|
669
|
+
import { wrapSocket } from 'baileys-antiban';
|
|
670
|
+
|
|
671
|
+
const safeSock = wrapSocket(
|
|
672
|
+
sock,
|
|
673
|
+
{ maxPerMinute: 15, autoPauseAt: 'high' }, // 2nd: AntiBanConfig (flat)
|
|
674
|
+
undefined, // 3rd: warmUpState (or pass saved state)
|
|
675
|
+
{ // 4th: WrapSocketOptions
|
|
676
|
+
deafSession: {
|
|
677
|
+
timeoutMs: 300000, // declare deaf after 5min without incoming msgs
|
|
678
|
+
minUptimeMs: 120000, // must be up 2min before deaf detection starts
|
|
679
|
+
autoReconnect: true,
|
|
680
|
+
onDeafSession: () => { /* reconnect logic */ },
|
|
681
|
+
},
|
|
682
|
+
autoRespondToIncoming: false,
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
## Quick Start (Legacy — v2 API, still works)
|
|
641
688
|
|
|
642
689
|
### Option 1: Wrap Your Socket (Easiest)
|
|
643
690
|
|
|
@@ -696,9 +743,9 @@ sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
|
696
743
|
});
|
|
697
744
|
```
|
|
698
745
|
|
|
699
|
-
## Configuration
|
|
746
|
+
## Configuration (Legacy nested format — deprecated)
|
|
700
747
|
|
|
701
|
-
|
|
748
|
+
> **Use the flat config in Quick Start (v3) above instead.** The nested format below still works but triggers a deprecation warning. Callbacks (`onRiskChange`, `onTimelockDetected`, `onTimelockLifted`) and advanced options for `jidCanonicalizer`/`lidResolver` currently still require the nested format — flat equivalents planned for v3.9.
|
|
702
749
|
|
|
703
750
|
```typescript
|
|
704
751
|
import { AntiBan } from 'baileys-antiban';
|
package/dist/antiban.js
CHANGED
|
@@ -79,6 +79,15 @@ function mapLegacyToFlat(legacy) {
|
|
|
79
79
|
flat.identicalMessageWindowMs = legacyAsFlat.identicalMessageWindowMs;
|
|
80
80
|
if (flat.burstAllowance === undefined && typeof legacyAsFlat.burstAllowance === 'number')
|
|
81
81
|
flat.burstAllowance = legacyAsFlat.burstAllowance;
|
|
82
|
+
// v3 fields that may coexist with legacy nested keys
|
|
83
|
+
if (flat.autoPauseAt === undefined && typeof legacyAsFlat.autoPauseAt === 'string')
|
|
84
|
+
flat.autoPauseAt = legacyAsFlat.autoPauseAt;
|
|
85
|
+
if (flat.groupMultiplier === undefined && typeof legacyAsFlat.groupMultiplier === 'number')
|
|
86
|
+
flat.groupMultiplier = legacyAsFlat.groupMultiplier;
|
|
87
|
+
if (flat.groupProfiles === undefined && typeof legacyAsFlat.groupProfiles === 'boolean')
|
|
88
|
+
flat.groupProfiles = legacyAsFlat.groupProfiles;
|
|
89
|
+
if (flat.persist === undefined && typeof legacyAsFlat.persist === 'string')
|
|
90
|
+
flat.persist = legacyAsFlat.persist;
|
|
82
91
|
return flat;
|
|
83
92
|
}
|
|
84
93
|
export class AntiBan {
|
package/dist/cjs/antiban.js
CHANGED
|
@@ -82,6 +82,15 @@ function mapLegacyToFlat(legacy) {
|
|
|
82
82
|
flat.identicalMessageWindowMs = legacyAsFlat.identicalMessageWindowMs;
|
|
83
83
|
if (flat.burstAllowance === undefined && typeof legacyAsFlat.burstAllowance === 'number')
|
|
84
84
|
flat.burstAllowance = legacyAsFlat.burstAllowance;
|
|
85
|
+
// v3 fields that may coexist with legacy nested keys
|
|
86
|
+
if (flat.autoPauseAt === undefined && typeof legacyAsFlat.autoPauseAt === 'string')
|
|
87
|
+
flat.autoPauseAt = legacyAsFlat.autoPauseAt;
|
|
88
|
+
if (flat.groupMultiplier === undefined && typeof legacyAsFlat.groupMultiplier === 'number')
|
|
89
|
+
flat.groupMultiplier = legacyAsFlat.groupMultiplier;
|
|
90
|
+
if (flat.groupProfiles === undefined && typeof legacyAsFlat.groupProfiles === 'boolean')
|
|
91
|
+
flat.groupProfiles = legacyAsFlat.groupProfiles;
|
|
92
|
+
if (flat.persist === undefined && typeof legacyAsFlat.persist === 'string')
|
|
93
|
+
flat.persist = legacyAsFlat.persist;
|
|
85
94
|
return flat;
|
|
86
95
|
}
|
|
87
96
|
class AntiBan {
|
package/dist/cjs/persist.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.StateManager = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
38
39
|
const KNOWN_CHATS_MAX = 1000;
|
|
39
40
|
const DEBOUNCE_MS = 5000;
|
|
40
41
|
/**
|
|
@@ -48,13 +49,20 @@ class StateManager {
|
|
|
48
49
|
path;
|
|
49
50
|
debounceTimer = null;
|
|
50
51
|
constructor(filePath) {
|
|
51
|
-
|
|
52
|
+
// Resolve to absolute path and reject null bytes to prevent path injection
|
|
53
|
+
if (filePath.includes('\0'))
|
|
54
|
+
throw new Error('[baileys-antiban] Invalid state file path: null byte');
|
|
55
|
+
this.path = path.resolve(filePath);
|
|
52
56
|
}
|
|
53
57
|
load() {
|
|
54
58
|
try {
|
|
55
59
|
const raw = fs.readFileSync(this.path, 'utf-8');
|
|
56
60
|
const parsed = JSON.parse(raw);
|
|
57
|
-
|
|
61
|
+
// Strict shape validation before trusting file content
|
|
62
|
+
if (typeof parsed !== 'object' || parsed === null ||
|
|
63
|
+
parsed.version !== 3 ||
|
|
64
|
+
typeof parsed.savedAt !== 'number' ||
|
|
65
|
+
!Array.isArray(parsed.knownChats)) {
|
|
58
66
|
console.warn('[baileys-antiban] WARN: corrupt state file or version mismatch, starting fresh');
|
|
59
67
|
return null;
|
|
60
68
|
}
|
package/dist/cjs/proxyRotator.js
CHANGED
|
@@ -19,20 +19,19 @@
|
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.proxyRotator = proxyRotator;
|
|
22
|
-
//
|
|
23
|
-
//
|
|
22
|
+
// Load optional peer dependencies synchronously in both ESM and CJS builds.
|
|
23
|
+
// CJS: native require is available. ESM: new Function() reads import.meta.url
|
|
24
|
+
// without causing TypeScript parse errors in CJS compilation. The string passed
|
|
25
|
+
// to new Function is a static literal — not user-controlled.
|
|
24
26
|
function lazyRequire(moduleName) {
|
|
25
|
-
// In CJS: use native require
|
|
26
27
|
if (typeof require !== 'undefined') {
|
|
27
28
|
return require(moduleName);
|
|
28
29
|
}
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const dynamicRequire = createRequire((0, eval)('import.meta.url'));
|
|
35
|
-
return dynamicRequire(moduleName);
|
|
30
|
+
// ESM path: new Function with static literal string avoids TS CJS-parse error on import.meta.
|
|
31
|
+
// Not user-controlled — both strings are compile-time constants.
|
|
32
|
+
const { createRequire } = (new Function('return require')())('node:module');
|
|
33
|
+
const metaUrl = new Function('return import.meta.url')();
|
|
34
|
+
return createRequire(metaUrl)(moduleName);
|
|
36
35
|
}
|
|
37
36
|
const NoopLogger = {
|
|
38
37
|
info: () => { },
|
package/dist/cjs/rateLimiter.js
CHANGED
|
@@ -177,6 +177,15 @@ class RateLimiter {
|
|
|
177
177
|
this.identicalCount.delete(hash);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
+
// LRU size cap — prevents unbounded growth when sending many unique messages.
|
|
181
|
+
// Evict oldest-by-lastSeen entries when map exceeds 10,000 entries.
|
|
182
|
+
const IDENTICAL_COUNT_MAX = 10_000;
|
|
183
|
+
if (this.identicalCount.size > IDENTICAL_COUNT_MAX) {
|
|
184
|
+
const sorted = [...this.identicalCount.entries()].sort(([, a], [, b]) => a.lastSeen - b.lastSeen);
|
|
185
|
+
const excess = this.identicalCount.size - IDENTICAL_COUNT_MAX;
|
|
186
|
+
for (let i = 0; i < excess; i++)
|
|
187
|
+
this.identicalCount.delete(sorted[i][0]);
|
|
188
|
+
}
|
|
180
189
|
}
|
|
181
190
|
/** Random delay between min and max (gaussian-ish distribution) */
|
|
182
191
|
jitter(min, max) {
|
package/dist/persist.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
2
3
|
const KNOWN_CHATS_MAX = 1000;
|
|
3
4
|
const DEBOUNCE_MS = 5000;
|
|
4
5
|
/**
|
|
@@ -12,13 +13,20 @@ export class StateManager {
|
|
|
12
13
|
path;
|
|
13
14
|
debounceTimer = null;
|
|
14
15
|
constructor(filePath) {
|
|
15
|
-
|
|
16
|
+
// Resolve to absolute path and reject null bytes to prevent path injection
|
|
17
|
+
if (filePath.includes('\0'))
|
|
18
|
+
throw new Error('[baileys-antiban] Invalid state file path: null byte');
|
|
19
|
+
this.path = path.resolve(filePath);
|
|
16
20
|
}
|
|
17
21
|
load() {
|
|
18
22
|
try {
|
|
19
23
|
const raw = fs.readFileSync(this.path, 'utf-8');
|
|
20
24
|
const parsed = JSON.parse(raw);
|
|
21
|
-
|
|
25
|
+
// Strict shape validation before trusting file content
|
|
26
|
+
if (typeof parsed !== 'object' || parsed === null ||
|
|
27
|
+
parsed.version !== 3 ||
|
|
28
|
+
typeof parsed.savedAt !== 'number' ||
|
|
29
|
+
!Array.isArray(parsed.knownChats)) {
|
|
22
30
|
console.warn('[baileys-antiban] WARN: corrupt state file or version mismatch, starting fresh');
|
|
23
31
|
return null;
|
|
24
32
|
}
|
package/dist/proxyRotator.js
CHANGED
|
@@ -16,20 +16,19 @@
|
|
|
16
16
|
* @author Kobus Wentzel <kobie@pop.co.za>
|
|
17
17
|
* @license MIT
|
|
18
18
|
*/
|
|
19
|
-
//
|
|
20
|
-
//
|
|
19
|
+
// Load optional peer dependencies synchronously in both ESM and CJS builds.
|
|
20
|
+
// CJS: native require is available. ESM: new Function() reads import.meta.url
|
|
21
|
+
// without causing TypeScript parse errors in CJS compilation. The string passed
|
|
22
|
+
// to new Function is a static literal — not user-controlled.
|
|
21
23
|
function lazyRequire(moduleName) {
|
|
22
|
-
// In CJS: use native require
|
|
23
24
|
if (typeof require !== 'undefined') {
|
|
24
25
|
return require(moduleName);
|
|
25
26
|
}
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const dynamicRequire = createRequire((0, eval)('import.meta.url'));
|
|
32
|
-
return dynamicRequire(moduleName);
|
|
27
|
+
// ESM path: new Function with static literal string avoids TS CJS-parse error on import.meta.
|
|
28
|
+
// Not user-controlled — both strings are compile-time constants.
|
|
29
|
+
const { createRequire } = (new Function('return require')())('node:module');
|
|
30
|
+
const metaUrl = new Function('return import.meta.url')();
|
|
31
|
+
return createRequire(metaUrl)(moduleName);
|
|
33
32
|
}
|
|
34
33
|
const NoopLogger = {
|
|
35
34
|
info: () => { },
|
package/dist/rateLimiter.js
CHANGED
|
@@ -174,6 +174,15 @@ export class RateLimiter {
|
|
|
174
174
|
this.identicalCount.delete(hash);
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
+
// LRU size cap — prevents unbounded growth when sending many unique messages.
|
|
178
|
+
// Evict oldest-by-lastSeen entries when map exceeds 10,000 entries.
|
|
179
|
+
const IDENTICAL_COUNT_MAX = 10_000;
|
|
180
|
+
if (this.identicalCount.size > IDENTICAL_COUNT_MAX) {
|
|
181
|
+
const sorted = [...this.identicalCount.entries()].sort(([, a], [, b]) => a.lastSeen - b.lastSeen);
|
|
182
|
+
const excess = this.identicalCount.size - IDENTICAL_COUNT_MAX;
|
|
183
|
+
for (let i = 0; i < excess; i++)
|
|
184
|
+
this.identicalCount.delete(sorted[i][0]);
|
|
185
|
+
}
|
|
177
186
|
}
|
|
178
187
|
/** Random delay between min and max (gaussian-ish distribution) */
|
|
179
188
|
jitter(min, max) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "baileys-antiban",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.11",
|
|
4
4
|
"description": "Anti-ban middleware for Baileys WhatsApp bots. Rate limiting, warmup, health monitor, LID resolver, disconnect classifier. Free Whapi.Cloud alternative.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|