@prototypevip/baileys 0.1.0 → 0.1.2
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/Defaults/index.js +3 -3
- package/lib/Defaults/index.js.bak-1788476907801 +187 -0
- package/lib/Defaults/index.js.bak-1788479166288 +187 -0
- package/lib/Socket/groups.js.bak-1788476973954 +475 -0
- package/lib/Socket/index.js +42 -126
- package/lib/Socket/index.js.bak-1788479281092 +38 -0
- package/lib/Socket/messages-send.js +88 -51
- package/lib/Store/prototype-store.js +34 -22
- package/lib/Store/prototype-store.js.bak-1788477006895 +983 -0
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/integrity-guard.js +281 -0
- package/lib/Utils/interactive-message-handler.js +28 -8
- package/lib/Utils/interactive-message-handler.js.bak-1788476898661 +478 -0
- package/lib/Utils/socket-guard.js +154 -0
- 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,339 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Topology Throttler — Network topology-based anti-ban enforcement
|
|
3
|
-
*
|
|
4
|
-
* WhatsApp bans based on NETWORK TOPOLOGY, not just message timing:
|
|
5
|
-
* - How fast you expand your contact graph
|
|
6
|
-
* - Cold-contact ratio (strangers vs known contacts)
|
|
7
|
-
* - Reply reciprocity
|
|
8
|
-
* - Group-source clustering (mass-DMing group members)
|
|
9
|
-
*
|
|
10
|
-
* This module enforces graph expansion limits and scores contact risk
|
|
11
|
-
* before each send, acting as the primary enforcement layer for high-risk
|
|
12
|
-
* cold outreach.
|
|
13
|
-
*
|
|
14
|
-
* Key insight: A 30% reply rate is the minimum to unlock more cold sends.
|
|
15
|
-
* Below that, WhatsApp's ML models flag you as a spammer.
|
|
16
|
-
*/
|
|
17
|
-
const TIME_CONSTANTS = {
|
|
18
|
-
MS_PER_HOUR: 3600000,
|
|
19
|
-
MS_PER_DAY: 86400000,
|
|
20
|
-
MS_PER_24H: 86400000,
|
|
21
|
-
REPLY_WINDOW_DAYS: 7,
|
|
22
|
-
};
|
|
23
|
-
const DEFAULT_CONFIG = {
|
|
24
|
-
maxNewContactsPerHour: 5,
|
|
25
|
-
maxNewContactsPerDay: 20,
|
|
26
|
-
minReplyRatioForNewContacts: 0.3,
|
|
27
|
-
maxSameGroupContacts: 10,
|
|
28
|
-
maxContactsFromSameSource: 8,
|
|
29
|
-
blockOnLimitReached: true,
|
|
30
|
-
cooldownMs: TIME_CONSTANTS.MS_PER_HOUR,
|
|
31
|
-
riskConfig: {
|
|
32
|
-
firstContactPenalty: 40,
|
|
33
|
-
noReplyPenalty: 20,
|
|
34
|
-
noMutualGroupsPenalty: 15,
|
|
35
|
-
recentContactBonus: -20,
|
|
36
|
-
repliedBeforeBonus: -30,
|
|
37
|
-
delayThreshold: 40,
|
|
38
|
-
abortThreshold: 75,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
const DEFAULT_RISK_CONFIG = {
|
|
42
|
-
firstContactPenalty: 40,
|
|
43
|
-
noReplyPenalty: 20,
|
|
44
|
-
noMutualGroupsPenalty: 15,
|
|
45
|
-
recentContactBonus: -20,
|
|
46
|
-
repliedBeforeBonus: -30,
|
|
47
|
-
delayThreshold: 40,
|
|
48
|
-
abortThreshold: 75,
|
|
49
|
-
};
|
|
50
|
-
export class TopologyThrottler {
|
|
51
|
-
config;
|
|
52
|
-
riskConfig;
|
|
53
|
-
contacts = new Map();
|
|
54
|
-
limits;
|
|
55
|
-
sourceGroupCounts = new Map();
|
|
56
|
-
constructor(config) {
|
|
57
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
58
|
-
this.riskConfig = { ...DEFAULT_RISK_CONFIG, ...this.config.riskConfig };
|
|
59
|
-
this.limits = {
|
|
60
|
-
newContactsThisHour: 0,
|
|
61
|
-
newContactsToday: 0,
|
|
62
|
-
lastHourResetAt: Date.now(),
|
|
63
|
-
lastDayResetAt: Date.now(),
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Assess contact risk before sending.
|
|
68
|
-
* This is the main check — call before every send to a new/unknown contact.
|
|
69
|
-
*/
|
|
70
|
-
assessContact(jid, context) {
|
|
71
|
-
this.resetLimitsIfNeeded();
|
|
72
|
-
const record = this.contacts.get(jid);
|
|
73
|
-
const now = Date.now();
|
|
74
|
-
let score = 0;
|
|
75
|
-
const reasons = [];
|
|
76
|
-
// First contact penalty
|
|
77
|
-
if (!record) {
|
|
78
|
-
score += this.riskConfig.firstContactPenalty;
|
|
79
|
-
reasons.push('first_contact');
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
// Has record — check reply history
|
|
83
|
-
if (record.replyTimestamps.length === 0 && record.sendTimestamps.length > 0) {
|
|
84
|
-
score += this.riskConfig.noReplyPenalty;
|
|
85
|
-
reasons.push('no_reply_history');
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// No mutual groups penalty
|
|
89
|
-
if (!context.knownGroups || context.knownGroups.length === 0) {
|
|
90
|
-
score += this.riskConfig.noMutualGroupsPenalty;
|
|
91
|
-
reasons.push('no_mutual_groups');
|
|
92
|
-
}
|
|
93
|
-
// Recent contact bonus
|
|
94
|
-
if (context.lastContactAt && (now - context.lastContactAt) < TIME_CONSTANTS.MS_PER_24H) {
|
|
95
|
-
score += this.riskConfig.recentContactBonus;
|
|
96
|
-
reasons.push('recent_contact');
|
|
97
|
-
}
|
|
98
|
-
// Replied before bonus
|
|
99
|
-
if (context.hasReplied || (record && record.replyTimestamps.length > 0)) {
|
|
100
|
-
score += this.riskConfig.repliedBeforeBonus;
|
|
101
|
-
reasons.push('has_replied');
|
|
102
|
-
}
|
|
103
|
-
// Clamp score to 0-100
|
|
104
|
-
score = Math.max(0, Math.min(100, score));
|
|
105
|
-
// Determine risk level
|
|
106
|
-
let risk;
|
|
107
|
-
let recommendation;
|
|
108
|
-
let suggestedDelayMs;
|
|
109
|
-
if (score >= this.riskConfig.abortThreshold) {
|
|
110
|
-
risk = 'CRITICAL';
|
|
111
|
-
recommendation = 'abort';
|
|
112
|
-
reasons.push('risk_too_high');
|
|
113
|
-
}
|
|
114
|
-
else if (score >= this.riskConfig.delayThreshold) {
|
|
115
|
-
risk = score >= 60 ? 'HIGH' : 'MEDIUM';
|
|
116
|
-
recommendation = 'delay';
|
|
117
|
-
// Exponential delay based on score
|
|
118
|
-
const delayMinutes = Math.floor((score - this.riskConfig.delayThreshold) / 10);
|
|
119
|
-
suggestedDelayMs = delayMinutes * 60000;
|
|
120
|
-
reasons.push('recommend_delay');
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
risk = score >= 30 ? 'MEDIUM' : 'LOW';
|
|
124
|
-
recommendation = 'send';
|
|
125
|
-
}
|
|
126
|
-
return {
|
|
127
|
-
jid,
|
|
128
|
-
risk,
|
|
129
|
-
score,
|
|
130
|
-
reasons,
|
|
131
|
-
recommendation,
|
|
132
|
-
suggestedDelayMs,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Record a sent message to this contact.
|
|
137
|
-
*/
|
|
138
|
-
recordSent(jid, sourceGroup) {
|
|
139
|
-
this.resetLimitsIfNeeded();
|
|
140
|
-
const now = Date.now();
|
|
141
|
-
let record = this.contacts.get(jid);
|
|
142
|
-
if (!record) {
|
|
143
|
-
// New contact — increment limits
|
|
144
|
-
record = {
|
|
145
|
-
firstContactAt: now,
|
|
146
|
-
sendTimestamps: [],
|
|
147
|
-
replyTimestamps: [],
|
|
148
|
-
blocked: false,
|
|
149
|
-
sourceGroup,
|
|
150
|
-
};
|
|
151
|
-
this.contacts.set(jid, record);
|
|
152
|
-
this.limits.newContactsThisHour++;
|
|
153
|
-
this.limits.newContactsToday++;
|
|
154
|
-
// Track source group
|
|
155
|
-
if (sourceGroup) {
|
|
156
|
-
const count = this.sourceGroupCounts.get(sourceGroup) || 0;
|
|
157
|
-
this.sourceGroupCounts.set(sourceGroup, count + 1);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// Add send timestamp (keep sliding window)
|
|
161
|
-
record.sendTimestamps.push(now);
|
|
162
|
-
this.cleanupTimestamps(record.sendTimestamps, TIME_CONSTANTS.REPLY_WINDOW_DAYS * TIME_CONSTANTS.MS_PER_DAY);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Record a reply from this contact.
|
|
166
|
-
*/
|
|
167
|
-
recordReplied(jid) {
|
|
168
|
-
const record = this.contacts.get(jid);
|
|
169
|
-
if (!record)
|
|
170
|
-
return;
|
|
171
|
-
const now = Date.now();
|
|
172
|
-
record.replyTimestamps.push(now);
|
|
173
|
-
this.cleanupTimestamps(record.replyTimestamps, TIME_CONSTANTS.REPLY_WINDOW_DAYS * TIME_CONSTANTS.MS_PER_DAY);
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Record that this contact blocked you.
|
|
177
|
-
*/
|
|
178
|
-
recordBlocked(jid) {
|
|
179
|
-
const record = this.contacts.get(jid);
|
|
180
|
-
if (!record)
|
|
181
|
-
return;
|
|
182
|
-
record.blocked = true;
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Check if topology limits allow sending to a new contact.
|
|
186
|
-
* Returns whether allowed and reason/retry time if blocked.
|
|
187
|
-
*/
|
|
188
|
-
canSendToNewContact() {
|
|
189
|
-
this.resetLimitsIfNeeded();
|
|
190
|
-
const now = Date.now();
|
|
191
|
-
// Check cooldown
|
|
192
|
-
if (this.limits.limitHitAt) {
|
|
193
|
-
const cooldownEndsAt = this.limits.limitHitAt + this.config.cooldownMs;
|
|
194
|
-
if (now < cooldownEndsAt) {
|
|
195
|
-
const retryAfterMs = cooldownEndsAt - now;
|
|
196
|
-
return {
|
|
197
|
-
allowed: false,
|
|
198
|
-
reason: `Cooldown active — limit hit recently`,
|
|
199
|
-
retryAfterMs,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
// Cooldown expired
|
|
204
|
-
delete this.limits.limitHitAt;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
// Check hourly limit
|
|
208
|
-
if (this.limits.newContactsThisHour >= this.config.maxNewContactsPerHour) {
|
|
209
|
-
this.limits.limitHitAt = now;
|
|
210
|
-
const retryAfterMs = (this.limits.lastHourResetAt + TIME_CONSTANTS.MS_PER_HOUR) - now;
|
|
211
|
-
return {
|
|
212
|
-
allowed: false,
|
|
213
|
-
reason: `Hourly new contact limit reached (${this.config.maxNewContactsPerHour})`,
|
|
214
|
-
retryAfterMs: Math.max(0, retryAfterMs),
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
// Check daily limit
|
|
218
|
-
if (this.limits.newContactsToday >= this.config.maxNewContactsPerDay) {
|
|
219
|
-
this.limits.limitHitAt = now;
|
|
220
|
-
const retryAfterMs = (this.limits.lastDayResetAt + TIME_CONSTANTS.MS_PER_DAY) - now;
|
|
221
|
-
return {
|
|
222
|
-
allowed: false,
|
|
223
|
-
reason: `Daily new contact limit reached (${this.config.maxNewContactsPerDay})`,
|
|
224
|
-
retryAfterMs: Math.max(0, retryAfterMs),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
// Check reply ratio requirement
|
|
228
|
-
const replyRatio = this.calculateReplyRatio();
|
|
229
|
-
if (replyRatio !== null && replyRatio < this.config.minReplyRatioForNewContacts) {
|
|
230
|
-
// Poor reply ratio — need to improve engagement before more cold sends
|
|
231
|
-
return {
|
|
232
|
-
allowed: false,
|
|
233
|
-
reason: `Reply ratio too low (${Math.round(replyRatio * 100)}% < ${Math.round(this.config.minReplyRatioForNewContacts * 100)}%)`,
|
|
234
|
-
retryAfterMs: TIME_CONSTANTS.MS_PER_HOUR, // arbitrary — user needs to improve engagement
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
return { allowed: true };
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Get topology statistics.
|
|
241
|
-
*/
|
|
242
|
-
getTopologyStats() {
|
|
243
|
-
this.resetLimitsIfNeeded();
|
|
244
|
-
const replyRatio = this.calculateReplyRatio();
|
|
245
|
-
const blockedRatio = this.calculateBlockedRatio();
|
|
246
|
-
// Get top 5 source group hotspots
|
|
247
|
-
const hotspots = Array.from(this.sourceGroupCounts.entries())
|
|
248
|
-
.map(([sourceGroup, count]) => ({ sourceGroup, count }))
|
|
249
|
-
.sort((a, b) => b.count - a.count)
|
|
250
|
-
.slice(0, 5);
|
|
251
|
-
return {
|
|
252
|
-
newContactsThisHour: this.limits.newContactsThisHour,
|
|
253
|
-
newContactsToday: this.limits.newContactsToday,
|
|
254
|
-
replyRatio,
|
|
255
|
-
blockedRatio,
|
|
256
|
-
hotspots,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Export state for persistence.
|
|
261
|
-
*/
|
|
262
|
-
exportState() {
|
|
263
|
-
return {
|
|
264
|
-
contacts: Array.from(this.contacts.entries()),
|
|
265
|
-
limits: { ...this.limits },
|
|
266
|
-
sourceGroupCounts: Array.from(this.sourceGroupCounts.entries()),
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Import state from persistence.
|
|
271
|
-
*/
|
|
272
|
-
importState(state) {
|
|
273
|
-
if (state.contacts) {
|
|
274
|
-
this.contacts = new Map(state.contacts);
|
|
275
|
-
}
|
|
276
|
-
if (state.limits) {
|
|
277
|
-
this.limits = { ...state.limits };
|
|
278
|
-
}
|
|
279
|
-
if (state.sourceGroupCounts) {
|
|
280
|
-
this.sourceGroupCounts = new Map(state.sourceGroupCounts);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
// Private helpers
|
|
284
|
-
resetLimitsIfNeeded() {
|
|
285
|
-
const now = Date.now();
|
|
286
|
-
// Reset hourly counter
|
|
287
|
-
if (now - this.limits.lastHourResetAt >= TIME_CONSTANTS.MS_PER_HOUR) {
|
|
288
|
-
this.limits.newContactsThisHour = 0;
|
|
289
|
-
this.limits.lastHourResetAt = now;
|
|
290
|
-
}
|
|
291
|
-
// Reset daily counter
|
|
292
|
-
if (now - this.limits.lastDayResetAt >= TIME_CONSTANTS.MS_PER_DAY) {
|
|
293
|
-
this.limits.newContactsToday = 0;
|
|
294
|
-
this.limits.lastDayResetAt = now;
|
|
295
|
-
// Clear source group counts daily
|
|
296
|
-
this.sourceGroupCounts.clear();
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
calculateReplyRatio() {
|
|
300
|
-
const now = Date.now();
|
|
301
|
-
const windowMs = TIME_CONSTANTS.REPLY_WINDOW_DAYS * TIME_CONSTANTS.MS_PER_DAY;
|
|
302
|
-
let totalSent = 0;
|
|
303
|
-
let totalReplies = 0;
|
|
304
|
-
for (const record of this.contacts.values()) {
|
|
305
|
-
// Count sends in window
|
|
306
|
-
const recentSends = record.sendTimestamps.filter(t => now - t < windowMs);
|
|
307
|
-
totalSent += recentSends.length;
|
|
308
|
-
// Count replies in window
|
|
309
|
-
const recentReplies = record.replyTimestamps.filter(t => now - t < windowMs);
|
|
310
|
-
totalReplies += recentReplies.length;
|
|
311
|
-
}
|
|
312
|
-
if (totalSent === 0)
|
|
313
|
-
return null; // No data yet
|
|
314
|
-
return totalReplies / totalSent;
|
|
315
|
-
}
|
|
316
|
-
calculateBlockedRatio() {
|
|
317
|
-
const totalContacts = this.contacts.size;
|
|
318
|
-
if (totalContacts === 0)
|
|
319
|
-
return null;
|
|
320
|
-
let blockedCount = 0;
|
|
321
|
-
for (const record of this.contacts.values()) {
|
|
322
|
-
if (record.blocked)
|
|
323
|
-
blockedCount++;
|
|
324
|
-
}
|
|
325
|
-
return blockedCount / totalContacts;
|
|
326
|
-
}
|
|
327
|
-
cleanupTimestamps(timestamps, maxAgeMs) {
|
|
328
|
-
const now = Date.now();
|
|
329
|
-
const cutoff = now - maxAgeMs;
|
|
330
|
-
// Remove old timestamps (in-place filter)
|
|
331
|
-
let writeIdx = 0;
|
|
332
|
-
for (let readIdx = 0; readIdx < timestamps.length; readIdx++) {
|
|
333
|
-
if (timestamps[readIdx] >= cutoff) {
|
|
334
|
-
timestamps[writeIdx++] = timestamps[readIdx];
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
timestamps.length = writeIdx;
|
|
338
|
-
}
|
|
339
|
-
}
|
package/lib/AntiBan/warmup.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WarmUp — Gradual activity increase for new/reconnected numbers
|
|
3
|
-
*
|
|
4
|
-
* New numbers or numbers reconnecting after a break are under extra scrutiny.
|
|
5
|
-
* This module enforces a gradual ramp-up of messaging activity.
|
|
6
|
-
*
|
|
7
|
-
* WhatsApp flags:
|
|
8
|
-
* - New numbers sending many messages immediately
|
|
9
|
-
* - Numbers going from 0 to 100 messages/day overnight
|
|
10
|
-
* - Sudden pattern changes after period of inactivity
|
|
11
|
-
*/
|
|
12
|
-
/** Generate a random growth factor in [1.5, 2.2] to avoid cross-account clustering */
|
|
13
|
-
function randomGrowthFactor() {
|
|
14
|
-
return Math.round((1.5 + Math.random() * 0.7) * 100) / 100;
|
|
15
|
-
}
|
|
16
|
-
const DEFAULT_CONFIG = {
|
|
17
|
-
warmUpDays: 7,
|
|
18
|
-
day1Limit: 20,
|
|
19
|
-
growthFactor: randomGrowthFactor(), // intentionally non-deterministic
|
|
20
|
-
inactivityThresholdHours: 72,
|
|
21
|
-
};
|
|
22
|
-
export class WarmUp {
|
|
23
|
-
config;
|
|
24
|
-
state;
|
|
25
|
-
constructor(config = {}, existingState) {
|
|
26
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
27
|
-
this.state = existingState || this.freshState();
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Get the current daily message limit based on warm-up phase
|
|
31
|
-
*/
|
|
32
|
-
getDailyLimit() {
|
|
33
|
-
if (this.state.graduated)
|
|
34
|
-
return Infinity;
|
|
35
|
-
const day = this.getCurrentDay();
|
|
36
|
-
if (day >= this.config.warmUpDays) {
|
|
37
|
-
this.state.graduated = true;
|
|
38
|
-
return Infinity;
|
|
39
|
-
}
|
|
40
|
-
return Math.round(this.config.day1Limit * Math.pow(this.config.growthFactor, day));
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Check if a message can be sent (within warm-up limits)
|
|
44
|
-
*/
|
|
45
|
-
canSend() {
|
|
46
|
-
this.checkInactivity();
|
|
47
|
-
if (this.state.graduated)
|
|
48
|
-
return true;
|
|
49
|
-
const day = this.getCurrentDay();
|
|
50
|
-
const todayCount = this.state.dailyCounts[day] || 0;
|
|
51
|
-
return todayCount < this.getDailyLimit();
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Record a sent message
|
|
55
|
-
*/
|
|
56
|
-
record() {
|
|
57
|
-
const now = Date.now();
|
|
58
|
-
const day = this.getCurrentDay();
|
|
59
|
-
while (this.state.dailyCounts.length <= day) {
|
|
60
|
-
this.state.dailyCounts.push(0);
|
|
61
|
-
}
|
|
62
|
-
this.state.dailyCounts[day]++;
|
|
63
|
-
this.state.lastActiveAt = now;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Get current warm-up status
|
|
67
|
-
*/
|
|
68
|
-
getStatus() {
|
|
69
|
-
const day = this.getCurrentDay();
|
|
70
|
-
const todaySent = this.state.dailyCounts[day] || 0;
|
|
71
|
-
const limit = this.getDailyLimit();
|
|
72
|
-
return {
|
|
73
|
-
phase: this.state.graduated ? 'graduated' : 'warming',
|
|
74
|
-
day: Math.min(day + 1, this.config.warmUpDays),
|
|
75
|
-
totalDays: this.config.warmUpDays,
|
|
76
|
-
todayLimit: limit === Infinity ? -1 : limit,
|
|
77
|
-
todaySent,
|
|
78
|
-
progress: this.state.graduated ? 100 : Math.round((day / this.config.warmUpDays) * 100),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Export state for persistence
|
|
83
|
-
*/
|
|
84
|
-
exportState() {
|
|
85
|
-
const day = this.getCurrentDay();
|
|
86
|
-
const todaySent = this.state.dailyCounts[day] || 0;
|
|
87
|
-
const todayDate = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
|
|
88
|
-
return {
|
|
89
|
-
...this.state,
|
|
90
|
-
todaySentCount: todaySent,
|
|
91
|
-
todayDate,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Import state from persistence
|
|
96
|
-
*/
|
|
97
|
-
importState(state) {
|
|
98
|
-
this.state = { ...state };
|
|
99
|
-
// BUG FIX 1: Restore today's send count from persisted state
|
|
100
|
-
// If todayDate matches current date, restore the count to prevent
|
|
101
|
-
// post-crash burst (issue: warmup day counter survives but send history lost)
|
|
102
|
-
if (state.todayDate && state.todaySentCount !== undefined) {
|
|
103
|
-
const todayDate = new Date().toISOString().split('T')[0];
|
|
104
|
-
if (state.todayDate === todayDate) {
|
|
105
|
-
const day = this.getCurrentDay();
|
|
106
|
-
// Restore the persisted count if higher than current
|
|
107
|
-
while (this.state.dailyCounts.length <= day) {
|
|
108
|
-
this.state.dailyCounts.push(0);
|
|
109
|
-
}
|
|
110
|
-
this.state.dailyCounts[day] = Math.max(this.state.dailyCounts[day] || 0, state.todaySentCount);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Reset warm-up (e.g., after detected ban risk)
|
|
116
|
-
*/
|
|
117
|
-
reset() {
|
|
118
|
-
this.state = this.freshState();
|
|
119
|
-
}
|
|
120
|
-
getCurrentDay() {
|
|
121
|
-
return Math.floor((Date.now() - this.state.startedAt) / 86400000);
|
|
122
|
-
}
|
|
123
|
-
checkInactivity() {
|
|
124
|
-
const hoursSinceActive = (Date.now() - this.state.lastActiveAt) / 3600000;
|
|
125
|
-
if (hoursSinceActive > this.config.inactivityThresholdHours && this.state.graduated) {
|
|
126
|
-
// Re-enter warm-up after extended inactivity
|
|
127
|
-
this.state = this.freshState();
|
|
128
|
-
this.state.graduated = false;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
freshState() {
|
|
132
|
-
const now = Date.now();
|
|
133
|
-
return {
|
|
134
|
-
startedAt: now,
|
|
135
|
-
lastActiveAt: now,
|
|
136
|
-
dailyCounts: [],
|
|
137
|
-
graduated: false,
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
}
|
package/lib/AntiBan/webhooks.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Webhook Alerts — Get notified on Telegram/Discord/Slack when risk changes
|
|
3
|
-
*/
|
|
4
|
-
const DEFAULT_CONFIG = {
|
|
5
|
-
urls: [],
|
|
6
|
-
minRiskLevel: 'medium',
|
|
7
|
-
cooldownMs: 300000,
|
|
8
|
-
includeStats: true,
|
|
9
|
-
};
|
|
10
|
-
export class WebhookAlerts {
|
|
11
|
-
config;
|
|
12
|
-
lastAlertTime = 0;
|
|
13
|
-
constructor(config = {}) {
|
|
14
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Send alert if risk level warrants it
|
|
18
|
-
*/
|
|
19
|
-
async alert(data) {
|
|
20
|
-
const riskOrder = ['low', 'medium', 'high', 'critical'];
|
|
21
|
-
if (riskOrder.indexOf(data.risk) < riskOrder.indexOf(this.config.minRiskLevel)) {
|
|
22
|
-
return; // Below threshold
|
|
23
|
-
}
|
|
24
|
-
const now = Date.now();
|
|
25
|
-
if (now - this.lastAlertTime < this.config.cooldownMs) {
|
|
26
|
-
return; // Cooldown active
|
|
27
|
-
}
|
|
28
|
-
this.lastAlertTime = now;
|
|
29
|
-
const payload = {
|
|
30
|
-
source: 'baileys-antiban',
|
|
31
|
-
timestamp: new Date().toISOString(),
|
|
32
|
-
...data,
|
|
33
|
-
};
|
|
34
|
-
// Send to generic webhook URLs
|
|
35
|
-
for (const url of this.config.urls) {
|
|
36
|
-
this.postWebhook(url, payload).catch(() => { });
|
|
37
|
-
}
|
|
38
|
-
// Telegram
|
|
39
|
-
if (this.config.telegram) {
|
|
40
|
-
const emoji = { low: '🟢', medium: '🟡', high: '🟠', critical: '🔴' }[data.risk] || '⚪';
|
|
41
|
-
const text = `${emoji} *baileys-antiban Alert*\n\nRisk: *${data.risk.toUpperCase()}* (score: ${data.score})\n${data.recommendation}\n\nReasons:\n${data.reasons.map(r => `• ${r}`).join('\n')}`;
|
|
42
|
-
this.postWebhook(`https://api.telegram.org/bot${this.config.telegram.botToken}/sendMessage`, { chat_id: this.config.telegram.chatId, text, parse_mode: 'Markdown' }).catch(() => { });
|
|
43
|
-
}
|
|
44
|
-
// Discord
|
|
45
|
-
if (this.config.discord) {
|
|
46
|
-
const color = { low: 0x00ff00, medium: 0xffff00, high: 0xff8800, critical: 0xff0000 }[data.risk] || 0;
|
|
47
|
-
this.postWebhook(this.config.discord.webhookUrl, {
|
|
48
|
-
embeds: [{
|
|
49
|
-
title: '🛡️ baileys-antiban Alert',
|
|
50
|
-
color,
|
|
51
|
-
fields: [
|
|
52
|
-
{ name: 'Risk', value: data.risk.toUpperCase(), inline: true },
|
|
53
|
-
{ name: 'Score', value: String(data.score), inline: true },
|
|
54
|
-
{ name: 'Recommendation', value: data.recommendation },
|
|
55
|
-
{ name: 'Reasons', value: data.reasons.join('\n') },
|
|
56
|
-
],
|
|
57
|
-
timestamp: new Date().toISOString(),
|
|
58
|
-
}],
|
|
59
|
-
}).catch(() => { });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
async postWebhook(url, payload) {
|
|
63
|
-
try {
|
|
64
|
-
const response = await fetch(url, {
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
...this.config.headers,
|
|
69
|
-
},
|
|
70
|
-
body: JSON.stringify(payload),
|
|
71
|
-
});
|
|
72
|
-
if (!response.ok) {
|
|
73
|
-
console.error(`[baileys-antiban] Webhook failed: ${response.status}`);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
console.error(`[baileys-antiban] Webhook error:`, err);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|