baileys-antiban 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +291 -0
- package/package.json +32 -0
- package/stress-test.ts +199 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kobus Wentzel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# baileys-antiban ๐ก๏ธ
|
|
2
|
+
|
|
3
|
+
Anti-ban middleware for [Baileys](https://github.com/WhiskeySockets/Baileys) โ protect your WhatsApp number with human-like messaging patterns.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
WhatsApp bans numbers that behave like bots. This library makes your Baileys bot behave like a human:
|
|
8
|
+
|
|
9
|
+
- **Rate limiting** with human-like timing (gaussian jitter, typing simulation)
|
|
10
|
+
- **Warm-up** for new numbers (gradual activity increase over 7 days)
|
|
11
|
+
- **Health monitoring** that detects ban warning signs before it's too late
|
|
12
|
+
- **Auto-pause** when risk gets too high
|
|
13
|
+
- **Drop-in wrapper** โ one line to protect your existing bot
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install baileys-antiban
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Option 1: Wrap your socket (easiest)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import makeWASocket from 'baileys';
|
|
27
|
+
import { wrapSocket } from 'baileys-antiban';
|
|
28
|
+
|
|
29
|
+
const sock = makeWASocket({ /* your config */ });
|
|
30
|
+
const safeSock = wrapSocket(sock);
|
|
31
|
+
|
|
32
|
+
// Use safeSock instead of sock โ sendMessage is now protected
|
|
33
|
+
await safeSock.sendMessage(jid, { text: 'Hello!' });
|
|
34
|
+
|
|
35
|
+
// Check health anytime
|
|
36
|
+
console.log(safeSock.antiban.getStats());
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Option 2: Manual control
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { AntiBan } from 'baileys-antiban';
|
|
43
|
+
|
|
44
|
+
const antiban = new AntiBan();
|
|
45
|
+
|
|
46
|
+
// Before every message:
|
|
47
|
+
const decision = await antiban.beforeSend(recipient, content);
|
|
48
|
+
|
|
49
|
+
if (decision.allowed) {
|
|
50
|
+
// Wait the recommended delay
|
|
51
|
+
await new Promise(r => setTimeout(r, decision.delayMs));
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
await sock.sendMessage(recipient, { text: content });
|
|
55
|
+
antiban.afterSend(recipient, content);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
antiban.afterSendFailed(err.message);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
console.log('Blocked:', decision.reason);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// In your connection.update handler:
|
|
64
|
+
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
65
|
+
if (connection === 'close') {
|
|
66
|
+
antiban.onDisconnect(lastDisconnect?.error?.output?.statusCode);
|
|
67
|
+
}
|
|
68
|
+
if (connection === 'open') {
|
|
69
|
+
antiban.onReconnect();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const antiban = new AntiBan({
|
|
78
|
+
rateLimiter: {
|
|
79
|
+
maxPerMinute: 8, // Max messages per minute (default: 8)
|
|
80
|
+
maxPerHour: 200, // Max messages per hour (default: 200)
|
|
81
|
+
maxPerDay: 1500, // Max messages per day (default: 1500)
|
|
82
|
+
minDelayMs: 1500, // Min delay between messages (default: 1500ms)
|
|
83
|
+
maxDelayMs: 5000, // Max delay between messages (default: 5000ms)
|
|
84
|
+
newChatDelayMs: 3000, // Extra delay for first message to new chat
|
|
85
|
+
maxIdenticalMessages: 3, // Block after 3 identical messages
|
|
86
|
+
burstAllowance: 3, // Fast messages before rate limiting kicks in
|
|
87
|
+
},
|
|
88
|
+
warmUp: {
|
|
89
|
+
warmUpDays: 7, // Days to full capacity (default: 7)
|
|
90
|
+
day1Limit: 20, // Messages allowed on day 1 (default: 20)
|
|
91
|
+
growthFactor: 1.8, // Daily limit multiplier (~doubles each day)
|
|
92
|
+
inactivityThresholdHours: 72, // Re-enter warm-up after 3 days inactive
|
|
93
|
+
},
|
|
94
|
+
health: {
|
|
95
|
+
disconnectWarningThreshold: 3, // Disconnects/hour before warning
|
|
96
|
+
disconnectCriticalThreshold: 5, // Disconnects/hour before critical
|
|
97
|
+
failedMessageThreshold: 5, // Failed messages/hour before warning
|
|
98
|
+
autoPauseAt: 'high', // Auto-pause at this risk level
|
|
99
|
+
onRiskChange: (status) => {
|
|
100
|
+
// Custom handler โ send alert, log, etc.
|
|
101
|
+
console.log(`Risk: ${status.risk}`, status.recommendation);
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
logging: true, // Console logging (default: true)
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Health Monitor
|
|
109
|
+
|
|
110
|
+
The health monitor tracks ban warning signs:
|
|
111
|
+
|
|
112
|
+
| Signal | Risk Score | What It Means |
|
|
113
|
+
|--------|-----------|---------------|
|
|
114
|
+
| Frequent disconnects | +15 to +30 | WhatsApp dropping your connection |
|
|
115
|
+
| 403 Forbidden | +40 per event | WhatsApp actively blocking you |
|
|
116
|
+
| 401 Logged Out | +60 | Possible temporary ban |
|
|
117
|
+
| Failed messages | +20 | Messages not going through |
|
|
118
|
+
|
|
119
|
+
Risk levels:
|
|
120
|
+
- ๐ข **Low** (0-29): Operating normally
|
|
121
|
+
- ๐ก **Medium** (30-59): Reduce messaging rate by 50%
|
|
122
|
+
- ๐ **High** (60-84): Reduce by 80%, consider pausing
|
|
123
|
+
- ๐ด **Critical** (85-100): **STOP IMMEDIATELY**
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const status = antiban.getStats().health;
|
|
127
|
+
console.log(status.risk); // 'low' | 'medium' | 'high' | 'critical'
|
|
128
|
+
console.log(status.score); // 0-100
|
|
129
|
+
console.log(status.recommendation); // Human-readable advice
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Warm-Up Schedule
|
|
133
|
+
|
|
134
|
+
New numbers ramp up gradually:
|
|
135
|
+
|
|
136
|
+
| Day | Message Limit |
|
|
137
|
+
|-----|--------------|
|
|
138
|
+
| 1 | 20 |
|
|
139
|
+
| 2 | 36 |
|
|
140
|
+
| 3 | 65 |
|
|
141
|
+
| 4 | 117 |
|
|
142
|
+
| 5 | 210 |
|
|
143
|
+
| 6 | 378 |
|
|
144
|
+
| 7 | 680 |
|
|
145
|
+
| 8+ | Unlimited |
|
|
146
|
+
|
|
147
|
+
Persist warm-up state between restarts:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// Save state
|
|
151
|
+
const state = antiban.exportWarmUpState();
|
|
152
|
+
fs.writeFileSync('warmup.json', JSON.stringify(state));
|
|
153
|
+
|
|
154
|
+
// Restore state
|
|
155
|
+
const saved = JSON.parse(fs.readFileSync('warmup.json', 'utf-8'));
|
|
156
|
+
const antiban = new AntiBan(config, saved);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Rate Limiter Details
|
|
160
|
+
|
|
161
|
+
The rate limiter mimics human behavior:
|
|
162
|
+
|
|
163
|
+
- **Gaussian jitter**: Delays clustered around the middle of the range, not uniform random
|
|
164
|
+
- **Typing simulation**: Longer messages get longer delays (~30ms per character)
|
|
165
|
+
- **New chat penalty**: First message to an unknown recipient gets extra delay
|
|
166
|
+
- **Burst allowance**: First 3 messages are faster (humans do this too)
|
|
167
|
+
- **Identical message detection**: Blocks sending the same text more than 3 times
|
|
168
|
+
- **Time-of-day awareness**: Built-in support for custom schedules
|
|
169
|
+
|
|
170
|
+
## Message Queue
|
|
171
|
+
|
|
172
|
+
Queue messages for safe, paced delivery with auto-retry:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { MessageQueue } from 'baileys-antiban';
|
|
176
|
+
|
|
177
|
+
const queue = new MessageQueue({ maxAttempts: 3 });
|
|
178
|
+
queue.setSendFunction(async (jid, content) => {
|
|
179
|
+
await safeSock.sendMessage(jid, content);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Queue messages
|
|
183
|
+
queue.add('group@g.us', { text: 'Hello!' });
|
|
184
|
+
queue.add('group@g.us', { text: 'Important!' }, { priority: 'high' });
|
|
185
|
+
queue.addBulk(['user1@s.whatsapp.net', 'user2@s.whatsapp.net'], { text: 'Broadcast' });
|
|
186
|
+
|
|
187
|
+
// Start processing
|
|
188
|
+
queue.start();
|
|
189
|
+
|
|
190
|
+
// Events
|
|
191
|
+
queue.on('sent', (msg) => console.log('Sent:', msg.id));
|
|
192
|
+
queue.on('failed', (msg, err) => console.log('Failed:', msg.id, err));
|
|
193
|
+
queue.on('retry', (msg, attempt, delay) => console.log(`Retry #${attempt} in ${delay}ms`));
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Content Variator
|
|
197
|
+
|
|
198
|
+
Auto-vary messages to avoid identical message detection:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { ContentVariator } from 'baileys-antiban';
|
|
202
|
+
|
|
203
|
+
const variator = new ContentVariator({
|
|
204
|
+
zeroWidthChars: true, // Invisible character variations
|
|
205
|
+
punctuationVariation: true, // Subtle punctuation changes
|
|
206
|
+
synonyms: true, // Replace common words with synonyms
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Each call returns a unique variation
|
|
210
|
+
const msg1 = variator.vary('Check out our auction today!');
|
|
211
|
+
const msg2 = variator.vary('Check out our auction today!');
|
|
212
|
+
// msg1 !== msg2 (technically different, looks the same to humans)
|
|
213
|
+
|
|
214
|
+
// Generate bulk variations for broadcast
|
|
215
|
+
const variations = variator.varyBulk('Hello everyone!', 50);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Smart Scheduler
|
|
219
|
+
|
|
220
|
+
Send during safe hours with realistic daily patterns:
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { Scheduler } from 'baileys-antiban';
|
|
224
|
+
|
|
225
|
+
const scheduler = new Scheduler({
|
|
226
|
+
timezone: 'Africa/Johannesburg',
|
|
227
|
+
activeHours: [8, 21], // 8 AM to 9 PM
|
|
228
|
+
weekendFactor: 0.5, // Half speed on weekends
|
|
229
|
+
peakHours: [10, 14], // Faster during business hours
|
|
230
|
+
lunchBreak: [12, 13], // Slow down at lunch
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (scheduler.isActiveTime()) {
|
|
234
|
+
const adjustedDelay = scheduler.adjustDelay(baseDelay);
|
|
235
|
+
// Send with adjusted timing
|
|
236
|
+
} else {
|
|
237
|
+
console.log(`Next active in ${scheduler.msUntilActive()}ms`);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Webhook Alerts
|
|
242
|
+
|
|
243
|
+
Get notified when risk level changes:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { WebhookAlerts } from 'baileys-antiban';
|
|
247
|
+
|
|
248
|
+
const alerts = new WebhookAlerts({
|
|
249
|
+
// Telegram alerts
|
|
250
|
+
telegram: { botToken: 'BOT_TOKEN', chatId: 'CHAT_ID' },
|
|
251
|
+
// Discord alerts
|
|
252
|
+
discord: { webhookUrl: 'https://discord.com/api/webhooks/...' },
|
|
253
|
+
// Generic webhooks
|
|
254
|
+
urls: ['https://your-server.com/webhook'],
|
|
255
|
+
minRiskLevel: 'medium',
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Wire into health monitor
|
|
259
|
+
const antiban = new AntiBan({
|
|
260
|
+
health: {
|
|
261
|
+
onRiskChange: (status) => alerts.alert(status),
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Emergency Controls
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
// Manually pause all sending
|
|
270
|
+
antiban.pause();
|
|
271
|
+
|
|
272
|
+
// Resume
|
|
273
|
+
antiban.resume();
|
|
274
|
+
|
|
275
|
+
// Nuclear reset (use after serving a ban period)
|
|
276
|
+
antiban.reset();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Best Practices
|
|
280
|
+
|
|
281
|
+
1. **Always warm up new numbers** โ Don't send 1000 messages on day 1
|
|
282
|
+
2. **Use a real phone number** โ Virtual/VOIP numbers get banned faster
|
|
283
|
+
3. **Don't send identical messages** โ Vary your content even slightly
|
|
284
|
+
4. **Respect the health monitor** โ When it says stop, STOP
|
|
285
|
+
5. **Persist warm-up state** โ Don't lose progress on restart
|
|
286
|
+
6. **Monitor your stats** โ Check `getStats()` regularly
|
|
287
|
+
7. **Have a backup number** โ Bans happen despite best efforts
|
|
288
|
+
|
|
289
|
+
## License
|
|
290
|
+
|
|
291
|
+
MIT โ Built by [WhatsAuction](https://whatsauction.co.za) ๐ฟ๐ฆ
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "baileys-antiban",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Anti-ban middleware for Baileys โ human-like messaging patterns to protect your WhatsApp number",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "jest"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"baileys",
|
|
14
|
+
"whatsapp",
|
|
15
|
+
"anti-ban",
|
|
16
|
+
"rate-limit",
|
|
17
|
+
"middleware"
|
|
18
|
+
],
|
|
19
|
+
"author": "Kobus Wentzel <kobie@pop.co.za>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/kobie3717/baileys-antiban"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"baileys": ">=6.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"typescript": "^5.0.0",
|
|
30
|
+
"@types/node": "^20.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/stress-test.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stress Test โ Send 1000 messages as fast as safely possible
|
|
3
|
+
*
|
|
4
|
+
* This pushes the anti-ban to its limits while keeping your number alive.
|
|
5
|
+
* Run: AUTH_DIR=<path> npx tsx stress-test.ts <group-jid>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import makeWASocket, { useMultiFileAuthState, DisconnectReason } from 'baileys';
|
|
9
|
+
import pino from 'pino';
|
|
10
|
+
import { AntiBan } from './src/antiban.js';
|
|
11
|
+
import { ContentVariator } from './src/contentVariator.js';
|
|
12
|
+
|
|
13
|
+
const AUTH_DIR = process.env.AUTH_DIR || './test-auth';
|
|
14
|
+
const GROUP_JID = process.argv[2];
|
|
15
|
+
const TARGET = parseInt(process.argv[3] || '1000');
|
|
16
|
+
|
|
17
|
+
if (!GROUP_JID) {
|
|
18
|
+
console.log('Usage: AUTH_DIR=<path> npx tsx stress-test.ts <group-jid> [count]');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Aggressive but safe config
|
|
23
|
+
const antiban = new AntiBan({
|
|
24
|
+
rateLimiter: {
|
|
25
|
+
maxPerMinute: 12, // Push it โ 12/min
|
|
26
|
+
maxPerHour: 500, // 500/hr
|
|
27
|
+
maxPerDay: 2000, // 2000/day
|
|
28
|
+
minDelayMs: 800, // Fast but not instant
|
|
29
|
+
maxDelayMs: 3000, // Cap delay at 3s
|
|
30
|
+
burstAllowance: 5, // Allow 5 fast messages
|
|
31
|
+
maxIdenticalMessages: 50, // High โ we use variator
|
|
32
|
+
newChatDelayMs: 500, // Low โ same group every time
|
|
33
|
+
},
|
|
34
|
+
warmUp: { warmUpDays: 0 }, // Skip warm-up for stress test
|
|
35
|
+
health: {
|
|
36
|
+
autoPauseAt: 'high',
|
|
37
|
+
onRiskChange: (status) => {
|
|
38
|
+
console.log(`\nโ ๏ธ RISK: ${status.risk.toUpperCase()} (score: ${status.score}) โ ${status.recommendation}\n`);
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
logging: false,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const variator = new ContentVariator({
|
|
45
|
+
zeroWidthChars: true,
|
|
46
|
+
punctuationVariation: true,
|
|
47
|
+
emojiPadding: false,
|
|
48
|
+
synonyms: false,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Message templates
|
|
52
|
+
const templates = [
|
|
53
|
+
'๐ก๏ธ Stress test #{n}/1000 โ anti-ban holding strong',
|
|
54
|
+
'โก Message #{n} โ speed run in progress',
|
|
55
|
+
'๐งช #{n} โ pushing the limits safely',
|
|
56
|
+
'๐ #{n}/1000 โ rate limiter active',
|
|
57
|
+
'๐ฅ #{n} โ no ban, no problem',
|
|
58
|
+
'โ
#{n} sent โ number still alive',
|
|
59
|
+
'๐๏ธ Fast message #{n} โ jitter applied',
|
|
60
|
+
'๐ช #{n}/1000 โ endurance test',
|
|
61
|
+
'๐ฏ #{n} โ precision timing',
|
|
62
|
+
'๐ Wave #{n} โ steady flow',
|
|
63
|
+
'โฑ๏ธ #{n} โ clock is ticking',
|
|
64
|
+
'๐ #{n}/1000 โ full throttle safe mode',
|
|
65
|
+
'๐ #{n} โ stats looking good',
|
|
66
|
+
'๐ #{n} โ protected by baileys-antiban',
|
|
67
|
+
'๐ #{n} โ race to 1000',
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
console.log('๐๏ธ baileys-antiban STRESS TEST');
|
|
71
|
+
console.log('='.repeat(60));
|
|
72
|
+
console.log(`Target: ${GROUP_JID}`);
|
|
73
|
+
console.log(`Messages: ${TARGET}`);
|
|
74
|
+
console.log(`Config: 12/min, 500/hr, 800-3000ms delay`);
|
|
75
|
+
console.log(`Estimated time: ${Math.round(TARGET * 2 / 60)} - ${Math.round(TARGET * 4 / 60)} minutes`);
|
|
76
|
+
console.log('='.repeat(60));
|
|
77
|
+
|
|
78
|
+
async function main() {
|
|
79
|
+
const { state, saveCreds } = await useMultiFileAuthState(AUTH_DIR);
|
|
80
|
+
|
|
81
|
+
const sock = makeWASocket({
|
|
82
|
+
auth: state,
|
|
83
|
+
printQRInTerminal: false,
|
|
84
|
+
logger: pino({ level: 'silent' }),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
sock.ev.on('creds.update', saveCreds);
|
|
88
|
+
|
|
89
|
+
// Monitor disconnects
|
|
90
|
+
sock.ev.on('connection.update', async (update) => {
|
|
91
|
+
if (update.connection === 'close') {
|
|
92
|
+
const reason = (update.lastDisconnect?.error as any)?.output?.statusCode;
|
|
93
|
+
antiban.onDisconnect(reason || 'unknown');
|
|
94
|
+
console.log(`\nโ DISCONNECTED: ${DisconnectReason[reason] || reason}`);
|
|
95
|
+
|
|
96
|
+
if (reason === 403) {
|
|
97
|
+
console.log('๐ด FORBIDDEN โ WhatsApp is blocking us. Test stopped.');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
if (reason === 401) {
|
|
101
|
+
console.log('๐ด LOGGED OUT โ Possible ban. Test stopped.');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return new Promise<void>((resolve) => {
|
|
108
|
+
sock.ev.on('connection.update', async (update) => {
|
|
109
|
+
if (update.connection !== 'open') return;
|
|
110
|
+
|
|
111
|
+
console.log('\nโ
Connected! Starting stress test...\n');
|
|
112
|
+
antiban.onReconnect();
|
|
113
|
+
|
|
114
|
+
let sent = 0;
|
|
115
|
+
let blocked = 0;
|
|
116
|
+
let errors = 0;
|
|
117
|
+
const startTime = Date.now();
|
|
118
|
+
const milestones = new Set([10, 50, 100, 250, 500, 750, 1000]);
|
|
119
|
+
|
|
120
|
+
for (let i = 1; i <= TARGET; i++) {
|
|
121
|
+
const template = templates[(i - 1) % templates.length];
|
|
122
|
+
const baseMsg = template.replace(/#{n}/g, String(i));
|
|
123
|
+
const message = variator.vary(baseMsg);
|
|
124
|
+
|
|
125
|
+
const decision = await antiban.beforeSend(GROUP_JID, message);
|
|
126
|
+
|
|
127
|
+
if (!decision.allowed) {
|
|
128
|
+
blocked++;
|
|
129
|
+
// Wait a bit and retry once
|
|
130
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
131
|
+
const retry = await antiban.beforeSend(GROUP_JID, message);
|
|
132
|
+
if (!retry.allowed) {
|
|
133
|
+
if (i % 50 === 0) console.log(`โ ${i}: Still blocked โ ${retry.reason}`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
await new Promise(r => setTimeout(r, retry.delayMs));
|
|
137
|
+
} else {
|
|
138
|
+
await new Promise(r => setTimeout(r, decision.delayMs));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
await sock.sendMessage(GROUP_JID, { text: message });
|
|
143
|
+
antiban.afterSend(GROUP_JID, message);
|
|
144
|
+
sent++;
|
|
145
|
+
|
|
146
|
+
// Progress indicator
|
|
147
|
+
if (sent % 10 === 0) {
|
|
148
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(0);
|
|
149
|
+
const rate = (sent / (parseInt(elapsed) || 1) * 60).toFixed(1);
|
|
150
|
+
const eta = Math.round((TARGET - sent) / (parseFloat(rate) / 60));
|
|
151
|
+
process.stdout.write(`\r๐ ${sent}/${TARGET} sent | ${blocked} blocked | ${rate}/min | ${elapsed}s elapsed | ETA: ${eta}s `);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (milestones.has(sent)) {
|
|
155
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
156
|
+
const stats = antiban.getStats();
|
|
157
|
+
console.log(`\n\n๐ MILESTONE: ${sent} messages sent!`);
|
|
158
|
+
console.log(` Time: ${elapsed}s | Health: ${stats.health.risk} (score: ${stats.health.score})`);
|
|
159
|
+
console.log(` Rate: ${stats.rateLimiter.lastMinute}/min, ${stats.rateLimiter.lastHour}/hr`);
|
|
160
|
+
console.log(` Avg delay: ${(stats.totalDelayMs / sent / 1000).toFixed(1)}s per message\n`);
|
|
161
|
+
}
|
|
162
|
+
} catch (err: any) {
|
|
163
|
+
errors++;
|
|
164
|
+
antiban.afterSendFailed(err.message);
|
|
165
|
+
if (errors > 10) {
|
|
166
|
+
console.log(`\n\n๐ด Too many errors (${errors}). Stopping.`);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
173
|
+
const finalStats = antiban.getStats();
|
|
174
|
+
|
|
175
|
+
console.log(`\n\n${'='.repeat(60)}`);
|
|
176
|
+
console.log(`๐ STRESS TEST COMPLETE`);
|
|
177
|
+
console.log(`${'='.repeat(60)}`);
|
|
178
|
+
console.log(`โ
Sent: ${sent}/${TARGET}`);
|
|
179
|
+
console.log(`โ Blocked: ${blocked}`);
|
|
180
|
+
console.log(`โ Errors: ${errors}`);
|
|
181
|
+
console.log(`โฑ๏ธ Total time: ${totalTime}s (${(parseFloat(totalTime) / 60).toFixed(1)} min)`);
|
|
182
|
+
console.log(`๐ Average rate: ${(sent / parseFloat(totalTime) * 60).toFixed(1)} msgs/min`);
|
|
183
|
+
console.log(`โณ Average delay: ${(finalStats.totalDelayMs / sent / 1000).toFixed(1)}s per message`);
|
|
184
|
+
console.log(`๐ฅ Final health: ${finalStats.health.risk} (score: ${finalStats.health.score})`);
|
|
185
|
+
console.log(`${sent === TARGET ? '๐ ALL MESSAGES DELIVERED โ NUMBER IS SAFE!' : 'โ ๏ธ Did not complete all messages'}`);
|
|
186
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
187
|
+
|
|
188
|
+
setTimeout(() => {
|
|
189
|
+
sock.end(undefined);
|
|
190
|
+
resolve();
|
|
191
|
+
}, 2000);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
main().then(() => process.exit(0)).catch(err => {
|
|
197
|
+
console.error('Fatal:', err);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
});
|