@prototypevip/baileys 0.0.9 → 0.1.1
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/lib/Socket/index.js +12 -135
- package/lib/Socket/messages-send.js +27 -44
- package/package.json +2 -2
- package/lib/AntiBan/antiban.js +0 -922
- package/lib/AntiBan/banRecoveryOrchestrator.js +0 -284
- package/lib/AntiBan/cli.js +0 -177
- package/lib/AntiBan/contactGraph.js +0 -236
- package/lib/AntiBan/contentVariator.js +0 -150
- package/lib/AntiBan/credsSnapshot.js +0 -121
- package/lib/AntiBan/deliveryTracker.js +0 -107
- package/lib/AntiBan/deviceFingerprint.js +0 -106
- package/lib/AntiBan/fleetEventStore.js +0 -157
- package/lib/AntiBan/groupOperationGuard.js +0 -116
- package/lib/AntiBan/health.js +0 -207
- package/lib/AntiBan/humanEntropy.js +0 -337
- package/lib/AntiBan/index.js +0 -77
- package/lib/AntiBan/instanceCoordinator.js +0 -175
- package/lib/AntiBan/jidCanonicalizer.js +0 -256
- package/lib/AntiBan/jidCircuitBreaker.js +0 -186
- package/lib/AntiBan/legitimacySignalInjector.js +0 -231
- package/lib/AntiBan/lidFirstResolver.js +0 -174
- package/lib/AntiBan/lidResolver.js +0 -324
- package/lib/AntiBan/messageQueue.js +0 -187
- package/lib/AntiBan/messageRecovery.js +0 -283
- package/lib/AntiBan/messageTypeRegistry.js +0 -369
- package/lib/AntiBan/observability.js +0 -145
- package/lib/AntiBan/patch.js +0 -302
- package/lib/AntiBan/persist.js +0 -87
- package/lib/AntiBan/presenceChoreographer.js +0 -430
- package/lib/AntiBan/presets.js +0 -105
- package/lib/AntiBan/profiles.js +0 -31
- package/lib/AntiBan/proxyRotator.js +0 -306
- package/lib/AntiBan/rateLimiter.js +0 -261
- package/lib/AntiBan/readReceiptVariance.js +0 -88
- package/lib/AntiBan/reconnectThrottle.js +0 -180
- package/lib/AntiBan/replyRatio.js +0 -161
- package/lib/AntiBan/reputationVoucher.js +0 -373
- package/lib/AntiBan/retryReason.js +0 -91
- package/lib/AntiBan/retryTracker.js +0 -172
- package/lib/AntiBan/scheduler.js +0 -111
- package/lib/AntiBan/sessionFingerprint.js +0 -248
- package/lib/AntiBan/sessionStability.js +0 -330
- package/lib/AntiBan/stateAdapter.js +0 -73
- package/lib/AntiBan/stateExport.js +0 -152
- package/lib/AntiBan/stealthConnect.js +0 -130
- package/lib/AntiBan/timelockGuard.js +0 -181
- package/lib/AntiBan/topologyThrottler.js +0 -339
- package/lib/AntiBan/warmup.js +0 -140
- package/lib/AntiBan/webhooks.js +0 -80
- package/lib/AntiBan/wrapper.js +0 -456
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Presence Choreographer — Circadian rhythm, distraction pauses, realistic read-receipts
|
|
3
|
-
*
|
|
4
|
-
* WhatsApp's ML models detect accounts with perfect, robotic timing patterns.
|
|
5
|
-
* This module adds realistic temporal variations:
|
|
6
|
-
* - Circadian rhythm: slower at night, faster during business hours
|
|
7
|
-
* - Distraction pauses: random 5-20min pauses (phone put down)
|
|
8
|
-
* - Offline gaps: occasional 5-15min offline periods
|
|
9
|
-
* - Read receipt timing: 3-45s delay, 15% chance to skip
|
|
10
|
-
*
|
|
11
|
-
* Research: 2025 ban analysis showed accounts with <10% timing variance were
|
|
12
|
-
* flagged at 3x rate vs accounts with circadian patterns. Human users have
|
|
13
|
-
* 40-60% variance in hourly activity.
|
|
14
|
-
*/
|
|
15
|
-
const DEFAULT_CONFIG = {
|
|
16
|
-
enabled: false,
|
|
17
|
-
enableCircadianRhythm: true,
|
|
18
|
-
timezone: 'UTC',
|
|
19
|
-
activityCurve: 'office',
|
|
20
|
-
circadian: {
|
|
21
|
-
enabled: true,
|
|
22
|
-
profile: 'default',
|
|
23
|
-
timezone: 'UTC',
|
|
24
|
-
},
|
|
25
|
-
distractionPauseProbability: 0.05,
|
|
26
|
-
distractionPauseMinMs: 300000,
|
|
27
|
-
distractionPauseMaxMs: 1200000,
|
|
28
|
-
readReceiptDelayMinMs: 3000,
|
|
29
|
-
readReceiptDelayMaxMs: 45000,
|
|
30
|
-
readReceiptSkipProbability: 0.15,
|
|
31
|
-
offlineGapProbability: 0.03,
|
|
32
|
-
offlineGapMinMs: 300000,
|
|
33
|
-
offlineGapMaxMs: 900000,
|
|
34
|
-
enableTypingModel: true,
|
|
35
|
-
typingWPM: 45,
|
|
36
|
-
typingWPMStdDev: 15,
|
|
37
|
-
thinkPauseProbability: 0.08,
|
|
38
|
-
thinkPauseMinMs: 800,
|
|
39
|
-
thinkPauseMaxMs: 3500,
|
|
40
|
-
intermittentPausedProbability: 0.4,
|
|
41
|
-
typingMaxMs: 90000,
|
|
42
|
-
typingMinMs: 600,
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* Activity curves (0.1 to 1.0 multipliers by hour)
|
|
46
|
-
* Values are inverted later: higher activity = shorter delays
|
|
47
|
-
*/
|
|
48
|
-
const ACTIVITY_CURVES = {
|
|
49
|
-
office: [
|
|
50
|
-
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 0-7: night quiet
|
|
51
|
-
0.5, 0.5, // 8-9: morning ramp
|
|
52
|
-
0.95, 0.95, // 10-11: morning peak
|
|
53
|
-
0.6, // 12: lunch dip
|
|
54
|
-
0.9, 0.9, 0.9, 0.9, // 13-16: afternoon
|
|
55
|
-
0.6, 0.6, // 17-18: wind-down
|
|
56
|
-
0.4, 0.4, // 19-20: evening
|
|
57
|
-
0.2, 0.2, 0.2, 0.2, // 21-24: taper
|
|
58
|
-
],
|
|
59
|
-
social: [
|
|
60
|
-
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 0-7: night quiet
|
|
61
|
-
0.3, 0.4, // 8-9: slow start
|
|
62
|
-
0.7, 0.8, // 10-11: ramp up
|
|
63
|
-
0.5, // 12: lunch
|
|
64
|
-
0.7, 0.7, // 13-14: afternoon
|
|
65
|
-
0.4, // 15: tea time dip
|
|
66
|
-
0.8, 0.9, 0.9, // 16-18: active
|
|
67
|
-
0.6, // 19: dinner dip
|
|
68
|
-
0.8, 0.85, 0.9, 0.95, 1.0, // 20-24: evening peak
|
|
69
|
-
],
|
|
70
|
-
global: [
|
|
71
|
-
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, // 0-5: night
|
|
72
|
-
0.4, 0.4, // 6-7: dawn dip
|
|
73
|
-
0.6, 0.7, 0.8, 0.8, // 8-11: morning
|
|
74
|
-
0.6, // 12: lunch
|
|
75
|
-
0.8, 0.8, 0.8, 0.8, // 13-16: afternoon
|
|
76
|
-
0.7, 0.7, // 17-18: evening
|
|
77
|
-
0.6, 0.5, 0.5, 0.5, 0.5, 0.5, // 19-24: night taper
|
|
78
|
-
],
|
|
79
|
-
};
|
|
80
|
-
/**
|
|
81
|
-
* Get circadian delay multiplier based on hour of day.
|
|
82
|
-
* Returns a multiplier to apply to base delays (typing, presence, etc.).
|
|
83
|
-
*
|
|
84
|
-
* Multiplier ranges:
|
|
85
|
-
* - Awake hours (09:00-22:00): ~0.8-1.2 (near baseline)
|
|
86
|
-
* - Evening (22:00-00:00): 1.2 → 2.5
|
|
87
|
-
* - Late night (00:00-02:00): 2.5 → 4.0
|
|
88
|
-
* - Dead zone (02:00-06:00): 4.0-6.0 (peak slow)
|
|
89
|
-
* - Early morning (06:00-09:00): 4.0 → 1.0
|
|
90
|
-
*
|
|
91
|
-
* Uses cosine-based smooth transitions (not stepped).
|
|
92
|
-
*
|
|
93
|
-
* @param date - Date to check (uses hour from this)
|
|
94
|
-
* @param profile - Circadian profile ('default' | 'nightOwl' | 'earlyBird' | 'always_on')
|
|
95
|
-
* @param timezone - IANA timezone (optional, defaults to local)
|
|
96
|
-
* @returns Delay multiplier (0.5 = 2x faster, 2.0 = 2x slower, 5.0 = 5x slower)
|
|
97
|
-
*/
|
|
98
|
-
export function getCircadianMultiplier(date = new Date(), profile = 'default', timezone) {
|
|
99
|
-
// always_on profile returns flat 1.0
|
|
100
|
-
if (profile === 'always_on') {
|
|
101
|
-
return 1.0;
|
|
102
|
-
}
|
|
103
|
-
// Get hour in specified timezone
|
|
104
|
-
let hour;
|
|
105
|
-
if (timezone) {
|
|
106
|
-
try {
|
|
107
|
-
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
108
|
-
timeZone: timezone,
|
|
109
|
-
hour: 'numeric',
|
|
110
|
-
hour12: false,
|
|
111
|
-
});
|
|
112
|
-
const parts = formatter.formatToParts(date);
|
|
113
|
-
const hourPart = parts.find(p => p.type === 'hour');
|
|
114
|
-
hour = hourPart ? parseInt(hourPart.value, 10) : date.getHours();
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
hour = date.getHours();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
hour = date.getHours();
|
|
122
|
-
}
|
|
123
|
-
// Apply profile shift
|
|
124
|
-
let shiftedHour = hour;
|
|
125
|
-
if (profile === 'nightOwl') {
|
|
126
|
-
shiftedHour = (hour - 3 + 24) % 24; // shift +3hr (active until 02:00, dead 04:00-09:00)
|
|
127
|
-
}
|
|
128
|
-
else if (profile === 'earlyBird') {
|
|
129
|
-
shiftedHour = (hour + 2) % 24; // shift -2hr (active 06:00-20:00, dead 23:00-04:00)
|
|
130
|
-
}
|
|
131
|
-
// Cosine-based smooth curve
|
|
132
|
-
// Model: slow at night (02:00-06:00), fast during day (09:00-22:00)
|
|
133
|
-
// Use piecewise cosine for smooth transitions
|
|
134
|
-
if (shiftedHour >= 9 && shiftedHour < 22) {
|
|
135
|
-
// Awake hours: 09:00-22:00 — baseline ~0.8-1.2
|
|
136
|
-
// Add slight variance: cosine wave with period 13 hours
|
|
137
|
-
const t = (shiftedHour - 9) / 13;
|
|
138
|
-
return 1.0 + 0.2 * Math.cos(2 * Math.PI * t);
|
|
139
|
-
}
|
|
140
|
-
else if (shiftedHour >= 22 && shiftedHour < 24) {
|
|
141
|
-
// Evening: 22:00-00:00 — ramp 1.2 → 2.5
|
|
142
|
-
const t = (shiftedHour - 22) / 2;
|
|
143
|
-
return 1.2 + 1.3 * t;
|
|
144
|
-
}
|
|
145
|
-
else if (shiftedHour >= 0 && shiftedHour < 2) {
|
|
146
|
-
// Late night: 00:00-02:00 — ramp 2.5 → 4.0
|
|
147
|
-
const t = shiftedHour / 2;
|
|
148
|
-
return 2.5 + 1.5 * t;
|
|
149
|
-
}
|
|
150
|
-
else if (shiftedHour >= 2 && shiftedHour < 6) {
|
|
151
|
-
// Dead zone: 02:00-06:00 — peak slow 4.0-6.0
|
|
152
|
-
// Use cosine for smooth peak
|
|
153
|
-
const t = (shiftedHour - 2) / 4;
|
|
154
|
-
return 5.0 + 1.0 * Math.cos(Math.PI * t); // peaks at 6.0 around 04:00
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
// Early morning: 06:00-09:00 — ramp 4.0 → 1.0
|
|
158
|
-
const t = (shiftedHour - 6) / 3;
|
|
159
|
-
return 4.0 - 3.0 * t;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
export class PresenceChoreographer {
|
|
163
|
-
config;
|
|
164
|
-
stats = {
|
|
165
|
-
distractionPausesInjected: 0,
|
|
166
|
-
offlineGapsInjected: 0,
|
|
167
|
-
readReceiptsDelayed: 0,
|
|
168
|
-
readReceiptsSkipped: 0,
|
|
169
|
-
typingPlansComputed: 0,
|
|
170
|
-
typingPlansExecuted: 0,
|
|
171
|
-
totalTypingTimeMs: 0,
|
|
172
|
-
};
|
|
173
|
-
constructor(config = {}) {
|
|
174
|
-
this.config = {
|
|
175
|
-
...DEFAULT_CONFIG,
|
|
176
|
-
...config,
|
|
177
|
-
circadian: {
|
|
178
|
-
...DEFAULT_CONFIG.circadian,
|
|
179
|
-
...(config.circadian || {}),
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Get current activity factor (0.1 to 1.0).
|
|
185
|
-
* Higher = more active = shorter delays.
|
|
186
|
-
* If circadian disabled, returns 1.0.
|
|
187
|
-
*/
|
|
188
|
-
getCurrentActivityFactor() {
|
|
189
|
-
if (!this.config.enabled || !this.config.enableCircadianRhythm) {
|
|
190
|
-
return 1.0;
|
|
191
|
-
}
|
|
192
|
-
const hour = this.getLocalHour();
|
|
193
|
-
const curve = ACTIVITY_CURVES[this.config.activityCurve];
|
|
194
|
-
return curve[hour] || 0.5;
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Check if should pause for distraction.
|
|
198
|
-
* Returns { pause: true, durationMs: 600000 } if probability check passes.
|
|
199
|
-
*/
|
|
200
|
-
shouldPauseForDistraction() {
|
|
201
|
-
if (!this.config.enabled) {
|
|
202
|
-
return { pause: false, durationMs: 0 };
|
|
203
|
-
}
|
|
204
|
-
if (Math.random() < this.config.distractionPauseProbability) {
|
|
205
|
-
const durationMs = this.randomBetween(this.config.distractionPauseMinMs, this.config.distractionPauseMaxMs);
|
|
206
|
-
this.stats.distractionPausesInjected++;
|
|
207
|
-
return { pause: true, durationMs };
|
|
208
|
-
}
|
|
209
|
-
return { pause: false, durationMs: 0 };
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Check if should take offline gap.
|
|
213
|
-
* Returns { offline: true, durationMs: 600000 } if probability check passes.
|
|
214
|
-
*/
|
|
215
|
-
shouldTakeOfflineGap() {
|
|
216
|
-
if (!this.config.enabled) {
|
|
217
|
-
return { offline: false, durationMs: 0 };
|
|
218
|
-
}
|
|
219
|
-
if (Math.random() < this.config.offlineGapProbability) {
|
|
220
|
-
const durationMs = this.randomBetween(this.config.offlineGapMinMs, this.config.offlineGapMaxMs);
|
|
221
|
-
this.stats.offlineGapsInjected++;
|
|
222
|
-
return { offline: true, durationMs };
|
|
223
|
-
}
|
|
224
|
-
return { offline: false, durationMs: 0 };
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Check if should mark message as read.
|
|
228
|
-
* Returns { mark: false } if skip probability hit.
|
|
229
|
-
* Returns { mark: true, delayMs: 5000 } otherwise.
|
|
230
|
-
* Applies circadian multiplier to delay.
|
|
231
|
-
*/
|
|
232
|
-
shouldMarkRead() {
|
|
233
|
-
if (!this.config.enabled) {
|
|
234
|
-
return { mark: true, delayMs: 0 };
|
|
235
|
-
}
|
|
236
|
-
// Skip read receipt?
|
|
237
|
-
if (Math.random() < this.config.readReceiptSkipProbability) {
|
|
238
|
-
this.stats.readReceiptsSkipped++;
|
|
239
|
-
return { mark: false, delayMs: 0 };
|
|
240
|
-
}
|
|
241
|
-
// Delayed read receipt
|
|
242
|
-
const baseDelayMs = this.randomBetween(this.config.readReceiptDelayMinMs, this.config.readReceiptDelayMaxMs);
|
|
243
|
-
// Apply circadian multiplier if enabled
|
|
244
|
-
let delayMs = baseDelayMs;
|
|
245
|
-
if (this.config.circadian.enabled) {
|
|
246
|
-
const circadianMultiplier = getCircadianMultiplier(new Date(), this.config.circadian.profile, this.config.circadian.timezone);
|
|
247
|
-
delayMs = Math.floor(baseDelayMs * circadianMultiplier);
|
|
248
|
-
}
|
|
249
|
-
this.stats.readReceiptsDelayed++;
|
|
250
|
-
return { mark: true, delayMs };
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Compute realistic typing duration for a message of given length.
|
|
254
|
-
* Includes Gaussian WPM variance + think-pause injection + circadian timing multiplier.
|
|
255
|
-
* Returns a "typing plan": array of { state, durationMs } steps the caller should execute sequentially.
|
|
256
|
-
*
|
|
257
|
-
* plan = [
|
|
258
|
-
* { state: 'composing', durationMs: 4200 },
|
|
259
|
-
* { state: 'paused', durationMs: 950 }, // think pause
|
|
260
|
-
* { state: 'composing', durationMs: 6800 },
|
|
261
|
-
* { state: 'paused', durationMs: 600 }, // brief stop before send
|
|
262
|
-
* ]
|
|
263
|
-
*/
|
|
264
|
-
computeTypingPlan(messageLength) {
|
|
265
|
-
if (!this.config.enabled || !this.config.enableTypingModel) {
|
|
266
|
-
return [{ state: 'composing', durationMs: this.config.typingMinMs }];
|
|
267
|
-
}
|
|
268
|
-
this.stats.typingPlansComputed++;
|
|
269
|
-
// Handle empty message
|
|
270
|
-
if (messageLength === 0) {
|
|
271
|
-
return [{ state: 'composing', durationMs: this.config.typingMinMs }];
|
|
272
|
-
}
|
|
273
|
-
// 1. Sample WPM from Gaussian distribution
|
|
274
|
-
const wpmSample = this.clamp(this.gaussianSample(this.config.typingWPM, this.config.typingWPMStdDev), 10, 120);
|
|
275
|
-
// 2. Convert to chars/sec (WPM standard: 5 chars/word)
|
|
276
|
-
const cps = (wpmSample * 5) / 60;
|
|
277
|
-
// 3. Base typing time
|
|
278
|
-
const baseMs = (messageLength / cps) * 1000;
|
|
279
|
-
// 4. Apply circadian multiplier if enabled
|
|
280
|
-
let circadianMultiplier = 1.0;
|
|
281
|
-
if (this.config.circadian.enabled) {
|
|
282
|
-
circadianMultiplier = getCircadianMultiplier(new Date(), this.config.circadian.profile, this.config.circadian.timezone);
|
|
283
|
-
}
|
|
284
|
-
// 5. Clamp to min/max
|
|
285
|
-
const targetMs = this.clamp(baseMs * circadianMultiplier, this.config.typingMinMs, this.config.typingMaxMs);
|
|
286
|
-
// 6. Build plan with think pauses
|
|
287
|
-
const plan = [];
|
|
288
|
-
let remainingBudget = targetMs;
|
|
289
|
-
let position = 0;
|
|
290
|
-
// Walk through message in chunks of 10 chars
|
|
291
|
-
const chunkSize = 10;
|
|
292
|
-
const numChunks = Math.max(1, Math.ceil(messageLength / chunkSize));
|
|
293
|
-
for (let i = 0; i < numChunks && remainingBudget > 0; i++) {
|
|
294
|
-
const charsInChunk = Math.min(chunkSize, messageLength - position);
|
|
295
|
-
// Distribute remaining budget proportionally across remaining chunks
|
|
296
|
-
const remainingChunks = numChunks - i;
|
|
297
|
-
const chunkBudget = remainingBudget / remainingChunks;
|
|
298
|
-
const chunkTypingMs = Math.floor(Math.min(chunkBudget, remainingBudget));
|
|
299
|
-
if (chunkTypingMs <= 0)
|
|
300
|
-
break;
|
|
301
|
-
// Should we inject a think pause after this chunk?
|
|
302
|
-
if (i > 0 && i < numChunks - 1 && Math.random() < this.config.thinkPauseProbability) {
|
|
303
|
-
// Add composing step
|
|
304
|
-
plan.push({ state: 'composing', durationMs: chunkTypingMs });
|
|
305
|
-
remainingBudget -= chunkTypingMs;
|
|
306
|
-
// Add think pause (apply circadian multiplier to pause durations too)
|
|
307
|
-
const basePauseMs = this.randomBetween(this.config.thinkPauseMinMs, this.config.thinkPauseMaxMs);
|
|
308
|
-
const pauseMs = Math.floor(basePauseMs * circadianMultiplier);
|
|
309
|
-
plan.push({ state: 'paused', durationMs: pauseMs });
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
// Accumulate typing time
|
|
313
|
-
if (plan.length === 0 || plan[plan.length - 1].state === 'paused') {
|
|
314
|
-
plan.push({ state: 'composing', durationMs: chunkTypingMs });
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
plan[plan.length - 1].durationMs += chunkTypingMs;
|
|
318
|
-
}
|
|
319
|
-
remainingBudget -= chunkTypingMs;
|
|
320
|
-
}
|
|
321
|
-
position += charsInChunk;
|
|
322
|
-
}
|
|
323
|
-
// 7. Optional final pause before send (apply circadian multiplier)
|
|
324
|
-
if (Math.random() < this.config.intermittentPausedProbability) {
|
|
325
|
-
const baseFinalPauseMs = this.randomBetween(200, 800);
|
|
326
|
-
const finalPauseMs = Math.floor(baseFinalPauseMs * circadianMultiplier);
|
|
327
|
-
plan.push({ state: 'paused', durationMs: finalPauseMs });
|
|
328
|
-
}
|
|
329
|
-
// 8. Ensure we have at least one composing step
|
|
330
|
-
if (plan.length === 0 || !plan.some(step => step.state === 'composing')) {
|
|
331
|
-
return [{ state: 'composing', durationMs: this.config.typingMinMs }];
|
|
332
|
-
}
|
|
333
|
-
return plan;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Execute a typing plan against a Baileys-shaped sock with sendPresenceUpdate(state, jid).
|
|
337
|
-
* Awaits each step's duration. Updates stats.
|
|
338
|
-
*
|
|
339
|
-
* await choreo.executeTypingPlan(sock, jid, plan);
|
|
340
|
-
* await sock.sendMessage(jid, content);
|
|
341
|
-
*/
|
|
342
|
-
async executeTypingPlan(sock, jid, plan, options) {
|
|
343
|
-
this.stats.typingPlansExecuted++;
|
|
344
|
-
for (const step of plan) {
|
|
345
|
-
// Check abort signal
|
|
346
|
-
if (options?.signal?.aborted) {
|
|
347
|
-
// Restore presence to paused before throwing
|
|
348
|
-
await Promise.resolve(sock.sendPresenceUpdate('paused', jid));
|
|
349
|
-
throw new Error('Typing plan aborted');
|
|
350
|
-
}
|
|
351
|
-
// Update presence
|
|
352
|
-
await Promise.resolve(sock.sendPresenceUpdate(step.state, jid));
|
|
353
|
-
// Sleep for duration
|
|
354
|
-
await this.sleep(step.durationMs);
|
|
355
|
-
// Track total typing time
|
|
356
|
-
this.stats.totalTypingTimeMs += step.durationMs;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Get statistics.
|
|
361
|
-
*/
|
|
362
|
-
getStats() {
|
|
363
|
-
return {
|
|
364
|
-
currentActivityFactor: this.getCurrentActivityFactor(),
|
|
365
|
-
distractionPausesInjected: this.stats.distractionPausesInjected,
|
|
366
|
-
offlineGapsInjected: this.stats.offlineGapsInjected,
|
|
367
|
-
readReceiptsDelayed: this.stats.readReceiptsDelayed,
|
|
368
|
-
readReceiptsSkipped: this.stats.readReceiptsSkipped,
|
|
369
|
-
currentHourLocal: this.getLocalHour(),
|
|
370
|
-
typingPlansComputed: this.stats.typingPlansComputed,
|
|
371
|
-
typingPlansExecuted: this.stats.typingPlansExecuted,
|
|
372
|
-
totalTypingTimeMs: this.stats.totalTypingTimeMs,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Reset statistics.
|
|
377
|
-
*/
|
|
378
|
-
reset() {
|
|
379
|
-
this.stats = {
|
|
380
|
-
distractionPausesInjected: 0,
|
|
381
|
-
offlineGapsInjected: 0,
|
|
382
|
-
readReceiptsDelayed: 0,
|
|
383
|
-
readReceiptsSkipped: 0,
|
|
384
|
-
typingPlansComputed: 0,
|
|
385
|
-
typingPlansExecuted: 0,
|
|
386
|
-
totalTypingTimeMs: 0,
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
// Private helpers
|
|
390
|
-
getLocalHour() {
|
|
391
|
-
try {
|
|
392
|
-
// Use Intl.DateTimeFormat to get local hour in specified timezone
|
|
393
|
-
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
394
|
-
timeZone: this.config.timezone,
|
|
395
|
-
hour: 'numeric',
|
|
396
|
-
hour12: false,
|
|
397
|
-
});
|
|
398
|
-
const parts = formatter.formatToParts(new Date());
|
|
399
|
-
const hourPart = parts.find(p => p.type === 'hour');
|
|
400
|
-
if (hourPart) {
|
|
401
|
-
return parseInt(hourPart.value, 10);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
catch (error) {
|
|
405
|
-
// Timezone not supported — fall back to UTC
|
|
406
|
-
}
|
|
407
|
-
// Fallback to UTC hour
|
|
408
|
-
return new Date().getUTCHours();
|
|
409
|
-
}
|
|
410
|
-
randomBetween(min, max) {
|
|
411
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
412
|
-
}
|
|
413
|
-
clamp(value, min, max) {
|
|
414
|
-
return Math.max(min, Math.min(max, value));
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Generate Gaussian sample using Box-Muller transform.
|
|
418
|
-
* Returns a sample from N(mean, stdDev).
|
|
419
|
-
*/
|
|
420
|
-
gaussianSample(mean, stdDev) {
|
|
421
|
-
// Box-Muller transform
|
|
422
|
-
const u1 = Math.random();
|
|
423
|
-
const u2 = Math.random();
|
|
424
|
-
const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
425
|
-
return mean + z0 * stdDev;
|
|
426
|
-
}
|
|
427
|
-
sleep(ms) {
|
|
428
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
429
|
-
}
|
|
430
|
-
}
|
package/lib/AntiBan/presets.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
export const PRESETS = {
|
|
2
|
-
conservative: {
|
|
3
|
-
maxPerMinute: 5,
|
|
4
|
-
maxPerHour: 100,
|
|
5
|
-
maxPerDay: 800,
|
|
6
|
-
minDelayMs: 2500,
|
|
7
|
-
maxDelayMs: 7000,
|
|
8
|
-
newChatDelayMs: 4000,
|
|
9
|
-
maxIdenticalMessages: 3,
|
|
10
|
-
identicalMessageWindowMs: 3600000,
|
|
11
|
-
burstAllowance: 3,
|
|
12
|
-
warmupDays: 10,
|
|
13
|
-
day1Limit: 15,
|
|
14
|
-
growthFactor: 1.8,
|
|
15
|
-
inactivityThresholdHours: 72,
|
|
16
|
-
autoPauseAt: 'medium',
|
|
17
|
-
groupMultiplier: 0.5,
|
|
18
|
-
groupProfiles: true,
|
|
19
|
-
logging: true,
|
|
20
|
-
},
|
|
21
|
-
moderate: {
|
|
22
|
-
maxPerMinute: 10,
|
|
23
|
-
maxPerHour: 300,
|
|
24
|
-
maxPerDay: 1500,
|
|
25
|
-
minDelayMs: 1500,
|
|
26
|
-
maxDelayMs: 5000,
|
|
27
|
-
newChatDelayMs: 3000,
|
|
28
|
-
maxIdenticalMessages: 5,
|
|
29
|
-
identicalMessageWindowMs: 3600000,
|
|
30
|
-
burstAllowance: 5,
|
|
31
|
-
warmupDays: 7,
|
|
32
|
-
day1Limit: 20,
|
|
33
|
-
growthFactor: 1.8,
|
|
34
|
-
inactivityThresholdHours: 72,
|
|
35
|
-
autoPauseAt: 'high',
|
|
36
|
-
groupMultiplier: 0.7,
|
|
37
|
-
groupProfiles: true,
|
|
38
|
-
logging: true,
|
|
39
|
-
},
|
|
40
|
-
aggressive: {
|
|
41
|
-
maxPerMinute: 20,
|
|
42
|
-
maxPerHour: 800,
|
|
43
|
-
maxPerDay: 4000,
|
|
44
|
-
minDelayMs: 800,
|
|
45
|
-
maxDelayMs: 3000,
|
|
46
|
-
newChatDelayMs: 2000,
|
|
47
|
-
maxIdenticalMessages: 10,
|
|
48
|
-
identicalMessageWindowMs: 3600000,
|
|
49
|
-
burstAllowance: 8,
|
|
50
|
-
warmupDays: 4,
|
|
51
|
-
day1Limit: 35,
|
|
52
|
-
growthFactor: 2.0,
|
|
53
|
-
inactivityThresholdHours: 48,
|
|
54
|
-
autoPauseAt: 'high',
|
|
55
|
-
groupMultiplier: 0.9,
|
|
56
|
-
groupProfiles: true,
|
|
57
|
-
logging: true,
|
|
58
|
-
},
|
|
59
|
-
// For established, fully-warmed accounts running enterprise-scale operations.
|
|
60
|
-
// Only use on accounts with 6+ months history and no prior bans.
|
|
61
|
-
'high-volume': {
|
|
62
|
-
maxPerMinute: 40,
|
|
63
|
-
maxPerHour: 1500,
|
|
64
|
-
maxPerDay: 8000,
|
|
65
|
-
minDelayMs: 400,
|
|
66
|
-
maxDelayMs: 1800,
|
|
67
|
-
newChatDelayMs: 1200,
|
|
68
|
-
maxIdenticalMessages: 20,
|
|
69
|
-
identicalMessageWindowMs: 3600000,
|
|
70
|
-
burstAllowance: 15,
|
|
71
|
-
warmupDays: 3,
|
|
72
|
-
day1Limit: 60,
|
|
73
|
-
growthFactor: 2.5,
|
|
74
|
-
inactivityThresholdHours: 24,
|
|
75
|
-
autoPauseAt: 'high', // Pause at HIGH not CRITICAL (too late)
|
|
76
|
-
groupMultiplier: 0.95,
|
|
77
|
-
groupProfiles: true,
|
|
78
|
-
logging: true,
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
export function resolveConfig(input) {
|
|
82
|
-
if (input === undefined) {
|
|
83
|
-
return { ...PRESETS.conservative };
|
|
84
|
-
}
|
|
85
|
-
if (typeof input === 'string') {
|
|
86
|
-
if (!(input in PRESETS)) {
|
|
87
|
-
throw new Error(`Unknown preset "${input}". Valid: conservative, moderate, aggressive, high-volume`);
|
|
88
|
-
}
|
|
89
|
-
// Warn if using high-volume preset
|
|
90
|
-
if (input === 'high-volume') {
|
|
91
|
-
console.warn('[baileys-antiban] WARNING: high-volume preset requires an established account (6+ months). Using on new accounts will result in immediate bans. Set accountAgeDays in config to suppress this warning.');
|
|
92
|
-
}
|
|
93
|
-
return { ...PRESETS[input] };
|
|
94
|
-
}
|
|
95
|
-
// Object form — extract preset base, merge overrides
|
|
96
|
-
const { preset = 'conservative', ...overrides } = input;
|
|
97
|
-
if (!(preset in PRESETS)) {
|
|
98
|
-
throw new Error(`Unknown preset "${preset}". Valid: conservative, moderate, aggressive, high-volume`);
|
|
99
|
-
}
|
|
100
|
-
// Warn if using high-volume preset (unless accountAgeDays is set)
|
|
101
|
-
if (preset === 'high-volume' && !('accountAgeDays' in overrides)) {
|
|
102
|
-
console.warn('[baileys-antiban] WARNING: high-volume preset requires an established account (6+ months). Using on new accounts will result in immediate bans. Set accountAgeDays in config to suppress this warning.');
|
|
103
|
-
}
|
|
104
|
-
return { ...PRESETS[preset], ...overrides };
|
|
105
|
-
}
|
package/lib/AntiBan/profiles.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/** @g.us = WhatsApp group */
|
|
2
|
-
export function isGroup(jid) {
|
|
3
|
-
return jid.endsWith('@g.us');
|
|
4
|
-
}
|
|
5
|
-
/** @newsletter = WhatsApp newsletter/channel */
|
|
6
|
-
export function isNewsletter(jid) {
|
|
7
|
-
return jid.endsWith('@newsletter');
|
|
8
|
-
}
|
|
9
|
-
/** status@broadcast = broadcast list */
|
|
10
|
-
export function isBroadcast(jid) {
|
|
11
|
-
return jid === 'status@broadcast' || jid.endsWith('@broadcast');
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Returns true if the JID should use stricter (group) rate limits.
|
|
15
|
-
* Groups and newsletters both get the group multiplier in v3.
|
|
16
|
-
* v4: separate newsletter profile.
|
|
17
|
-
*/
|
|
18
|
-
export function shouldUseGroupProfile(jid) {
|
|
19
|
-
return isGroup(jid) || isNewsletter(jid);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Scale rate limits by multiplier for group/newsletter JIDs.
|
|
23
|
-
* Floors to integer, minimum 1 per limit.
|
|
24
|
-
*/
|
|
25
|
-
export function applyGroupMultiplier(limits, multiplier) {
|
|
26
|
-
return {
|
|
27
|
-
maxPerMinute: Math.max(1, Math.floor(limits.maxPerMinute * multiplier)),
|
|
28
|
-
maxPerHour: Math.max(1, Math.floor(limits.maxPerHour * multiplier)),
|
|
29
|
-
maxPerDay: Math.max(1, Math.floor(limits.maxPerDay * multiplier)),
|
|
30
|
-
};
|
|
31
|
-
}
|