dankgrinder 5.24.0 → 5.260.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/lib/antiDetect.js +211 -0
- package/lib/commands/adventure.js +36 -25
- package/lib/commands/dig.js +80 -15
- package/lib/commands/farm.js +14 -3
- package/lib/commands/stream.js +33 -3
- package/lib/commands/utils.js +20 -2
- package/lib/commands/work.js +37 -13
- package/lib/cooldownManager.js +347 -0
- package/lib/grinder.js +159 -55
- package/package.json +1 -1
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced Cooldown Manager with Smart CD Calculations
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - EMA-based cooldown prediction (learns from history)
|
|
6
|
+
* - Command chaining with optimal timing
|
|
7
|
+
* - Priority queue for scheduling
|
|
8
|
+
* - Redis-backed shared state (cluster mode)
|
|
9
|
+
* - Predictive readiness estimation
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const { LRUCache, MinHeap } = require('./structures');
|
|
15
|
+
const { calcSmartCooldown } = require('./antiDetect');
|
|
16
|
+
|
|
17
|
+
// ── Command Cooldown Config ───────────────────────────────────
|
|
18
|
+
const COMMAND_CONFIG = Object.freeze({
|
|
19
|
+
// Base cooldowns in seconds
|
|
20
|
+
beg: { base: 120, priority: 3, category: 'income' },
|
|
21
|
+
crime: { base: 1200, priority: 2, category: 'income' },
|
|
22
|
+
search: { base: 150, priority: 3, category: 'income' },
|
|
23
|
+
work: { base: 3600, priority: 1, category: 'income' },
|
|
24
|
+
dig: { base: 600, priority: 2, category: 'income' },
|
|
25
|
+
fish: { base: 900, priority: 2, category: 'income' },
|
|
26
|
+
hunt: { base: 120, priority: 3, category: 'income' },
|
|
27
|
+
farm: { base: 300, priority: 2, category: 'income' },
|
|
28
|
+
stream: { base: 120, priority: 3, category: 'income' },
|
|
29
|
+
trivia: { base: 180, priority: 2, category: 'income' },
|
|
30
|
+
highlow: { base: 120, priority: 2, category: 'income' },
|
|
31
|
+
gamble: { base: 30, priority: 1, category: 'gambling' },
|
|
32
|
+
blackjack: { base: 30, priority: 1, category: 'gambling' },
|
|
33
|
+
roulette: { base: 30, priority: 1, category: 'gambling' },
|
|
34
|
+
slots: { base: 30, priority: 1, category: 'gambling' },
|
|
35
|
+
adventure: { base: 1800, priority: 1, category: 'special' },
|
|
36
|
+
postmeme: { base: 300, priority: 2, category: 'social' },
|
|
37
|
+
meme: { base: 300, priority: 2, category: 'social' },
|
|
38
|
+
vote: { base: 7200, priority: 1, category: 'special' },
|
|
39
|
+
daily: { base: 86400, priority: 1, category: 'special' },
|
|
40
|
+
weekly: { base: 604800, priority: 1, category: 'special' },
|
|
41
|
+
monthly: { base: 2592000, priority: 1, category: 'special' },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ── EMA Calculator ────────────────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Exponential Moving Average for cooldown prediction.
|
|
47
|
+
* Gives more weight to recent observations.
|
|
48
|
+
*/
|
|
49
|
+
class EMACalculator {
|
|
50
|
+
constructor(alpha = 0.3) {
|
|
51
|
+
this.alpha = alpha; // Smoothing factor (0-1)
|
|
52
|
+
this.ema = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
update(value) {
|
|
56
|
+
if (this.ema === null) {
|
|
57
|
+
this.ema = value;
|
|
58
|
+
} else {
|
|
59
|
+
this.ema = this.alpha * value + (1 - this.alpha) * this.ema;
|
|
60
|
+
}
|
|
61
|
+
return this.ema;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getPrediction() {
|
|
65
|
+
return this.ema;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
reset() {
|
|
69
|
+
this.ema = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Cooldown Tracker ──────────────────────────────────────────
|
|
74
|
+
class CooldownTracker {
|
|
75
|
+
constructor(userId, redis = null, clusterEnabled = false) {
|
|
76
|
+
this.userId = userId;
|
|
77
|
+
this.redis = redis;
|
|
78
|
+
this.clusterEnabled = clusterEnabled;
|
|
79
|
+
|
|
80
|
+
// Local caches
|
|
81
|
+
this.cooldowns = new Map(); // command -> readyAt (timestamp)
|
|
82
|
+
this.emaTrackers = new Map(); // command -> EMACalculator
|
|
83
|
+
this.historyCache = new LRUCache(50); // Last 50 cooldown observations per command
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Record a cooldown for a command.
|
|
88
|
+
* @param {string} command - Command name
|
|
89
|
+
* @param {number} cooldownSec - Observed cooldown in seconds
|
|
90
|
+
*/
|
|
91
|
+
recordCooldown(command, cooldownSec) {
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
const readyAt = now + (cooldownSec * 1000);
|
|
94
|
+
|
|
95
|
+
// Store in local map
|
|
96
|
+
this.cooldowns.set(command, readyAt);
|
|
97
|
+
|
|
98
|
+
// Update EMA prediction
|
|
99
|
+
if (!this.emaTrackers.has(command)) {
|
|
100
|
+
this.emaTrackers.set(command, new EMACalculator(0.3));
|
|
101
|
+
}
|
|
102
|
+
const ema = this.emaTrackers.get(command);
|
|
103
|
+
ema.update(cooldownSec);
|
|
104
|
+
|
|
105
|
+
// Store in Redis for cluster mode
|
|
106
|
+
if (this.redis && this.clusterEnabled) {
|
|
107
|
+
const key = `dkg:cd:${this.userId}:${command}`;
|
|
108
|
+
this.redis.setex(key, cooldownSec + 60, JSON.stringify({
|
|
109
|
+
readyAt,
|
|
110
|
+
ema: ema.getPrediction(),
|
|
111
|
+
recorded: now,
|
|
112
|
+
})).catch(() => {});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Cache history
|
|
116
|
+
const history = this.historyCache.get(command) || [];
|
|
117
|
+
history.push({ cooldownSec, timestamp: now });
|
|
118
|
+
if (history.length > 50) history.shift();
|
|
119
|
+
this.historyCache.set(command, history);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a command is ready.
|
|
124
|
+
* @param {string} command - Command name
|
|
125
|
+
* @returns {Object} - { ready: boolean, waitMs: number, predicted: number|null }
|
|
126
|
+
*/
|
|
127
|
+
isReady(command) {
|
|
128
|
+
const readyAt = this.cooldowns.get(command);
|
|
129
|
+
if (!readyAt) {
|
|
130
|
+
return { ready: true, waitMs: 0, predicted: null };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const waitMs = Math.max(0, readyAt - now);
|
|
135
|
+
|
|
136
|
+
// Add smart buffer
|
|
137
|
+
const config = COMMAND_CONFIG[command];
|
|
138
|
+
if (config) {
|
|
139
|
+
const buffered = calcSmartCooldown(waitMs / 1000, { addBuffer: true, addVariance: false });
|
|
140
|
+
return {
|
|
141
|
+
ready: waitMs <= 0,
|
|
142
|
+
waitMs: waitMs > 0 ? buffered * 1000 : 0,
|
|
143
|
+
predicted: this.emaTrackers.get(command)?.getPrediction() || null,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
ready: waitMs <= 0,
|
|
149
|
+
waitMs,
|
|
150
|
+
predicted: this.emaTrackers.get(command)?.getPrediction() || null,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get predicted cooldown for a command.
|
|
156
|
+
* @param {string} command - Command name
|
|
157
|
+
* @returns {number|null} - Predicted cooldown in seconds
|
|
158
|
+
*/
|
|
159
|
+
getPredictedCooldown(command) {
|
|
160
|
+
return this.emaTrackers.get(command)?.getPrediction() || null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get optimal command order based on cooldowns.
|
|
165
|
+
* @param {string[]} commands - List of commands to consider
|
|
166
|
+
* @returns {Array} - Sorted commands with timing info
|
|
167
|
+
*/
|
|
168
|
+
getOptimalOrder(commands) {
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
const order = commands.map(cmd => {
|
|
171
|
+
const config = COMMAND_CONFIG[cmd] || { priority: 5 };
|
|
172
|
+
const status = this.isReady(cmd);
|
|
173
|
+
return {
|
|
174
|
+
command: cmd,
|
|
175
|
+
ready: status.ready,
|
|
176
|
+
waitMs: status.waitMs,
|
|
177
|
+
priority: config.priority,
|
|
178
|
+
category: config.category,
|
|
179
|
+
predicted: status.predicted,
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Sort: ready commands first (by priority), then by wait time
|
|
184
|
+
order.sort((a, b) => {
|
|
185
|
+
if (a.ready && !b.ready) return -1;
|
|
186
|
+
if (!a.ready && b.ready) return 1;
|
|
187
|
+
if (a.ready && b.ready) return a.priority - b.priority;
|
|
188
|
+
return a.waitMs - b.waitMs;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return order;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get all commands that will be ready within a time window.
|
|
196
|
+
* @param {number} windowMs - Time window in milliseconds
|
|
197
|
+
* @returns {Array} - Commands ready within window
|
|
198
|
+
*/
|
|
199
|
+
getReadyInWindow(windowMs) {
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
const ready = [];
|
|
202
|
+
|
|
203
|
+
for (const [command, readyAt] of this.cooldowns.entries()) {
|
|
204
|
+
const waitMs = readyAt - now;
|
|
205
|
+
if (waitMs <= windowMs) {
|
|
206
|
+
ready.push({
|
|
207
|
+
command,
|
|
208
|
+
waitMs: Math.max(0, waitMs),
|
|
209
|
+
readyAt,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
ready.sort((a, b) => a.waitMs - b.waitMs);
|
|
215
|
+
return ready;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Clear cooldown for a command.
|
|
220
|
+
* @param {string} command - Command name
|
|
221
|
+
*/
|
|
222
|
+
clearCooldown(command) {
|
|
223
|
+
this.cooldowns.delete(command);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Clear all cooldowns.
|
|
228
|
+
*/
|
|
229
|
+
clearAll() {
|
|
230
|
+
this.cooldowns.clear();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ── Command Scheduler (Priority Queue) ────────────────────────
|
|
235
|
+
class CommandScheduler {
|
|
236
|
+
constructor(tracker) {
|
|
237
|
+
this.tracker = tracker;
|
|
238
|
+
this.queue = new MinHeap((a, b) => a.executeAt - b.executeAt);
|
|
239
|
+
this.pending = new Map(); // command -> node reference
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Schedule a command for execution.
|
|
244
|
+
* @param {string} command - Command name
|
|
245
|
+
* @param {Function} executeFn - Function to execute
|
|
246
|
+
* @param {number} executeAt - Timestamp to execute at
|
|
247
|
+
*/
|
|
248
|
+
schedule(command, executeFn, executeAt) {
|
|
249
|
+
// Remove existing scheduled command if any
|
|
250
|
+
this.unschedule(command);
|
|
251
|
+
|
|
252
|
+
const node = {
|
|
253
|
+
command,
|
|
254
|
+
executeFn,
|
|
255
|
+
executeAt,
|
|
256
|
+
added: Date.now(),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
this.queue.push(node);
|
|
260
|
+
this.pending.set(command, node);
|
|
261
|
+
|
|
262
|
+
return node;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Cancel a scheduled command.
|
|
267
|
+
* @param {string} command - Command name
|
|
268
|
+
*/
|
|
269
|
+
unschedule(command) {
|
|
270
|
+
const node = this.pending.get(command);
|
|
271
|
+
if (node) {
|
|
272
|
+
this.queue.remove(node);
|
|
273
|
+
this.pending.delete(command);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get next command to execute.
|
|
279
|
+
* @returns {Object|null} - Next command node or null
|
|
280
|
+
*/
|
|
281
|
+
peekNext() {
|
|
282
|
+
return this.queue.peek();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Execute next command if it's time.
|
|
287
|
+
* @returns {Object|null} - Executed command node or null
|
|
288
|
+
*/
|
|
289
|
+
async executeNext() {
|
|
290
|
+
const node = this.queue.peek();
|
|
291
|
+
if (!node) return null;
|
|
292
|
+
|
|
293
|
+
const now = Date.now();
|
|
294
|
+
if (node.executeAt <= now) {
|
|
295
|
+
this.queue.pop();
|
|
296
|
+
this.pending.delete(node.command);
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
await node.executeFn();
|
|
300
|
+
return node;
|
|
301
|
+
} catch (e) {
|
|
302
|
+
console.error(`[scheduler] Error executing ${node.command}:`, e);
|
|
303
|
+
throw e;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get queue size.
|
|
312
|
+
*/
|
|
313
|
+
size() {
|
|
314
|
+
return this.queue.size();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ── Factory Function ──────────────────────────────────────────
|
|
319
|
+
/**
|
|
320
|
+
* Create a cooldown manager for a user.
|
|
321
|
+
* @param {string} userId - User ID
|
|
322
|
+
* @param {Object} options
|
|
323
|
+
* @param {Object} options.redis - Redis client (optional)
|
|
324
|
+
* @param {boolean} options.clusterEnabled - Cluster mode flag
|
|
325
|
+
* @returns {Object} - { tracker, scheduler, COMMAND_CONFIG }
|
|
326
|
+
*/
|
|
327
|
+
function createCooldownManager(userId, options = {}) {
|
|
328
|
+
const { redis = null, clusterEnabled = false } = options;
|
|
329
|
+
|
|
330
|
+
const tracker = new CooldownTracker(userId, redis, clusterEnabled);
|
|
331
|
+
const scheduler = new CommandScheduler(tracker);
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
tracker,
|
|
335
|
+
scheduler,
|
|
336
|
+
COMMAND_CONFIG,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ── Exports ───────────────────────────────────────────────────
|
|
341
|
+
module.exports = {
|
|
342
|
+
EMACalculator,
|
|
343
|
+
CooldownTracker,
|
|
344
|
+
CommandScheduler,
|
|
345
|
+
createCooldownManager,
|
|
346
|
+
COMMAND_CONFIG,
|
|
347
|
+
};
|