dankgrinder 5.260.0 → 5.281.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/afkMode.js +349 -0
- package/lib/antiDetect.js +51 -1
- package/lib/grinder.js +92 -19
- package/package.json +5 -1
package/lib/afkMode.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AFK Mode - Smart Command Queuing
|
|
3
|
+
*
|
|
4
|
+
* Users want to run the bot and forget about it. This module provides:
|
|
5
|
+
* - Smart prioritization based on cooldowns and earnings
|
|
6
|
+
* - Auto-deposit when wallet is full
|
|
7
|
+
* - Auto-sell junk items
|
|
8
|
+
* - Sleep mode (pause during night hours)
|
|
9
|
+
* - Smart bank management
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
// ── Configuration ─────────────────────────────────────────────
|
|
15
|
+
const AFK_CONFIG = Object.freeze({
|
|
16
|
+
// Wallet management
|
|
17
|
+
AUTO_DEPOSIT_THRESHOLD: 100000, // Deposit when wallet > 100k
|
|
18
|
+
AUTO_DEPOSIT_KEEP: 20000, // Keep 20k in wallet after deposit
|
|
19
|
+
|
|
20
|
+
// Inventory management
|
|
21
|
+
AUTO_SELL_THRESHOLD: 0.85, // Sell when inventory > 85% full
|
|
22
|
+
JUNK_ITEMS: new Set([ // Always sell these
|
|
23
|
+
'trash', 'junk', 'rock', 'stick', 'bottle', 'can', 'old shoe',
|
|
24
|
+
'broken compass', 'rusty key', 'tattered flag', 'empty chest',
|
|
25
|
+
]),
|
|
26
|
+
VALUABLE_ITEMS: new Set([ // Never sell these
|
|
27
|
+
'diamond', 'ruby', 'emerald', 'sapphire', 'gold bar', 'coin',
|
|
28
|
+
'legendary', 'mythical', 'artifact', 'treasure',
|
|
29
|
+
]),
|
|
30
|
+
|
|
31
|
+
// Sleep mode (optional - pause during these hours)
|
|
32
|
+
SLEEP_MODE_ENABLED: false,
|
|
33
|
+
SLEEP_START_HOUR: 2, // 2 AM
|
|
34
|
+
SLEEP_END_HOUR: 6, // 6 AM
|
|
35
|
+
|
|
36
|
+
// Command prioritization
|
|
37
|
+
HIGH_VALUE_COMMANDS: new Set(['daily', 'weekly', 'monthly', 'work', 'stream']),
|
|
38
|
+
INCOME_COMMANDS: new Set(['beg', 'search', 'crime', 'hunt', 'dig', 'fish']),
|
|
39
|
+
GAMBLE_COMMANDS: new Set(['blackjack', 'roulette', 'slots', 'snakeeyes', 'cointoss']),
|
|
40
|
+
|
|
41
|
+
// Gambling limits in AFK mode
|
|
42
|
+
GAMBLE_MAX_LOSSES: 10, // Stop after 10 losses
|
|
43
|
+
GAMBLE_MAX_LOSS_AMOUNT: 50000, // Stop after losing 50k
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ── Command Priority Calculator ───────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Calculate priority score for a command.
|
|
49
|
+
* Higher score = should run sooner.
|
|
50
|
+
*/
|
|
51
|
+
function calculateCommandPriority(cmdName, cooldownMs, account) {
|
|
52
|
+
let score = 0;
|
|
53
|
+
|
|
54
|
+
// Ready commands get highest priority
|
|
55
|
+
if (cooldownMs <= 0) {
|
|
56
|
+
score += 1000;
|
|
57
|
+
} else {
|
|
58
|
+
// Penalize by wait time (in minutes)
|
|
59
|
+
score -= Math.floor(cooldownMs / 60000);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// High-value commands get bonus
|
|
63
|
+
if (AFK_CONFIG.HIGH_VALUE_COMMANDS.has(cmdName)) {
|
|
64
|
+
score += 500;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Income commands are important
|
|
68
|
+
if (AFK_CONFIG.INCOME_COMMANDS.has(cmdName)) {
|
|
69
|
+
score += 200;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Gambling commands are lower priority in AFK mode
|
|
73
|
+
if (AFK_CONFIG.GAMBLE_COMMANDS.has(cmdName)) {
|
|
74
|
+
score -= 100;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Periodic commands (daily/weekly/monthly) get huge bonus when ready
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
const dailyCd = account?.doneToday?.get('daily') || 0;
|
|
80
|
+
const weeklyCd = account?.doneToday?.get('weekly') || 0;
|
|
81
|
+
const monthlyCd = account?.doneToday?.get('monthly') || 0;
|
|
82
|
+
|
|
83
|
+
if (cmdName === 'daily' && dailyCd <= now) score += 800;
|
|
84
|
+
if (cmdName === 'weekly' && weeklyCd <= now) score += 900;
|
|
85
|
+
if (cmdName === 'monthly' && monthlyCd <= now) score += 1000;
|
|
86
|
+
|
|
87
|
+
return score;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Smart Command Queue Builder ───────────────────────────────
|
|
91
|
+
/**
|
|
92
|
+
* Build optimized command queue based on cooldowns and priorities.
|
|
93
|
+
*/
|
|
94
|
+
function buildSmartQueue(account, cooldowns) {
|
|
95
|
+
const enabledCmds = getEnabledCommands(account);
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
|
|
98
|
+
// Calculate priority for each command
|
|
99
|
+
const priorities = enabledCmds.map(cmd => {
|
|
100
|
+
const cdUntil = cooldowns.get(cmd) || 0;
|
|
101
|
+
const cooldownMs = Math.max(0, cdUntil - now);
|
|
102
|
+
const priority = calculateCommandPriority(cmd, cooldownMs, account);
|
|
103
|
+
|
|
104
|
+
return { cmd, priority, cooldownMs };
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Sort by priority (highest first)
|
|
108
|
+
priorities.sort((a, b) => b.priority - a.priority);
|
|
109
|
+
|
|
110
|
+
// Return queue of command names
|
|
111
|
+
return priorities.map(p => p.cmd);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── Get Enabled Commands ──────────────────────────────────────
|
|
115
|
+
function getEnabledCommands(account) {
|
|
116
|
+
const cmdMap = [
|
|
117
|
+
{ key: 'cmd_beg', name: 'beg' },
|
|
118
|
+
{ key: 'cmd_search', name: 'search' },
|
|
119
|
+
{ key: 'cmd_crime', name: 'crime' },
|
|
120
|
+
{ key: 'cmd_hunt', name: 'hunt' },
|
|
121
|
+
{ key: 'cmd_dig', name: 'dig' },
|
|
122
|
+
{ key: 'cmd_fish', name: 'fish' },
|
|
123
|
+
{ key: 'cmd_farm', name: 'farm' },
|
|
124
|
+
{ key: 'cmd_work', name: 'work' },
|
|
125
|
+
{ key: 'cmd_stream', name: 'stream' },
|
|
126
|
+
{ key: 'cmd_daily', name: 'daily' },
|
|
127
|
+
{ key: 'cmd_weekly', name: 'weekly' },
|
|
128
|
+
{ key: 'cmd_monthly', name: 'monthly' },
|
|
129
|
+
{ key: 'cmd_highlow', name: 'hl' },
|
|
130
|
+
{ key: 'cmd_blackjack', name: 'blackjack' },
|
|
131
|
+
{ key: 'cmd_roulette', name: 'roulette' },
|
|
132
|
+
{ key: 'cmd_slots', name: 'slots' },
|
|
133
|
+
{ key: 'cmd_snakeeyes', name: 'snakeeyes' },
|
|
134
|
+
{ key: 'cmd_trivia', name: 'trivia' },
|
|
135
|
+
{ key: 'cmd_scratch', name: 'scratch' },
|
|
136
|
+
{ key: 'cmd_adventure', name: 'adventure' },
|
|
137
|
+
{ key: 'cmd_postmemes', name: 'postmemes' },
|
|
138
|
+
{ key: 'cmd_tidy', name: 'tidy' },
|
|
139
|
+
{ key: 'cmd_use', name: 'use' },
|
|
140
|
+
{ key: 'cmd_deposit', name: 'deposit' },
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
return cmdMap
|
|
144
|
+
.filter(c => account[c.key] === true)
|
|
145
|
+
.map(c => c.name);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ── Auto-Deposit Check ────────────────────────────────────────
|
|
149
|
+
/**
|
|
150
|
+
* Check if auto-deposit should run.
|
|
151
|
+
*/
|
|
152
|
+
function shouldAutoDeposit(walletBalance, account) {
|
|
153
|
+
if (!account.cmd_deposit) return false;
|
|
154
|
+
|
|
155
|
+
const threshold = account.afk_deposit_threshold || AFK_CONFIG.AUTO_DEPOSIT_THRESHOLD;
|
|
156
|
+
return walletBalance > threshold;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Auto-Sell Check ───────────────────────────────────────────
|
|
160
|
+
/**
|
|
161
|
+
* Check if inventory auto-sell should run.
|
|
162
|
+
*/
|
|
163
|
+
function shouldAutoSell(inventoryCount, maxInventory, account) {
|
|
164
|
+
if (!account.cmd_use) return false;
|
|
165
|
+
|
|
166
|
+
const threshold = account.afk_sell_threshold || AFK_CONFIG.AUTO_SELL_THRESHOLD;
|
|
167
|
+
return inventoryCount / maxInventory >= threshold;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Get Items To Sell ─────────────────────────────────────────
|
|
171
|
+
/**
|
|
172
|
+
* Determine which items to auto-sell.
|
|
173
|
+
*/
|
|
174
|
+
function getItemsToSell(inventory) {
|
|
175
|
+
const toSell = [];
|
|
176
|
+
|
|
177
|
+
for (const item of inventory) {
|
|
178
|
+
const itemName = (item.name || '').toLowerCase();
|
|
179
|
+
|
|
180
|
+
// Always sell junk
|
|
181
|
+
if (AFK_CONFIG.JUNK_ITEMS.some(junk => itemName.includes(junk))) {
|
|
182
|
+
toSell.push(item.name);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Never sell valuables
|
|
187
|
+
if (AFK_CONFIG.VALUABLE_ITEMS.some(val => itemName.includes(val))) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Sell common items (value < 1000)
|
|
192
|
+
if (item.value && item.value < 1000) {
|
|
193
|
+
toSell.push(item.name);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return toSell;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ── Sleep Mode Check ──────────────────────────────────────────
|
|
201
|
+
/**
|
|
202
|
+
* Check if sleep mode is active.
|
|
203
|
+
*/
|
|
204
|
+
function isSleepMode() {
|
|
205
|
+
if (!AFK_CONFIG.SLEEP_MODE_ENABLED) return false;
|
|
206
|
+
|
|
207
|
+
const hour = new Date().getHours();
|
|
208
|
+
const start = AFK_CONFIG.SLEEP_START_HOUR;
|
|
209
|
+
const end = AFK_CONFIG.SLEEP_END_HOUR;
|
|
210
|
+
|
|
211
|
+
if (start < end) {
|
|
212
|
+
return hour >= start && hour < end;
|
|
213
|
+
}
|
|
214
|
+
// Handles overnight ranges (e.g., 22:00 - 06:00)
|
|
215
|
+
return hour >= start || hour < end;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── AFK Mode Presets ──────────────────────────────────────────
|
|
219
|
+
const AFK_PRESETS = {
|
|
220
|
+
// Casual: Run every 2 hours, low priority commands only
|
|
221
|
+
casual: {
|
|
222
|
+
commands: ['beg', 'search', 'daily', 'weekly', 'monthly'],
|
|
223
|
+
deposit_threshold: 50000,
|
|
224
|
+
sell_threshold: 0.9,
|
|
225
|
+
gamble_enabled: false,
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
// Grinder: Run all commands ASAP, maximize earnings
|
|
229
|
+
grinder: {
|
|
230
|
+
commands: 'all',
|
|
231
|
+
deposit_threshold: 100000,
|
|
232
|
+
sell_threshold: 0.8,
|
|
233
|
+
gamble_enabled: true,
|
|
234
|
+
gamble_limit: 20,
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
// Smart: AI-driven prioritization based on cooldowns
|
|
238
|
+
smart: {
|
|
239
|
+
commands: 'all',
|
|
240
|
+
deposit_threshold: 100000,
|
|
241
|
+
sell_threshold: 0.85,
|
|
242
|
+
gamble_enabled: true,
|
|
243
|
+
gamble_limit: 10,
|
|
244
|
+
use_priority_queue: true,
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// Sleep: Only run long cooldown commands
|
|
248
|
+
sleep: {
|
|
249
|
+
commands: ['daily', 'weekly', 'monthly', 'work'],
|
|
250
|
+
deposit_threshold: 200000,
|
|
251
|
+
sell_threshold: 0.95,
|
|
252
|
+
gamble_enabled: false,
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
// Safe: No gambling, conservative play
|
|
256
|
+
safe: {
|
|
257
|
+
commands: 'all',
|
|
258
|
+
deposit_threshold: 100000,
|
|
259
|
+
sell_threshold: 0.85,
|
|
260
|
+
gamble_enabled: false,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// ── Apply Preset ──────────────────────────────────────────────
|
|
265
|
+
/**
|
|
266
|
+
* Apply an AFK preset to an account.
|
|
267
|
+
*/
|
|
268
|
+
function applyPreset(account, presetName) {
|
|
269
|
+
const preset = AFK_PRESETS[presetName];
|
|
270
|
+
if (!preset) return false;
|
|
271
|
+
|
|
272
|
+
// Set commands
|
|
273
|
+
if (preset.commands === 'all') {
|
|
274
|
+
// Enable all commands
|
|
275
|
+
account.cmd_beg = true;
|
|
276
|
+
account.cmd_search = true;
|
|
277
|
+
account.cmd_crime = true;
|
|
278
|
+
account.cmd_hunt = true;
|
|
279
|
+
account.cmd_dig = true;
|
|
280
|
+
account.cmd_fish = true;
|
|
281
|
+
account.cmd_farm = true;
|
|
282
|
+
account.cmd_work = true;
|
|
283
|
+
account.cmd_stream = true;
|
|
284
|
+
account.cmd_daily = true;
|
|
285
|
+
account.cmd_weekly = true;
|
|
286
|
+
account.cmd_monthly = true;
|
|
287
|
+
account.cmd_highlow = true;
|
|
288
|
+
account.cmd_blackjack = preset.gamble_enabled;
|
|
289
|
+
account.cmd_roulette = preset.gamble_enabled;
|
|
290
|
+
account.cmd_slots = preset.gamble_enabled;
|
|
291
|
+
account.cmd_snakeeyes = preset.gamble_enabled;
|
|
292
|
+
account.cmd_trivia = true;
|
|
293
|
+
account.cmd_scratch = true;
|
|
294
|
+
account.cmd_adventure = true;
|
|
295
|
+
account.cmd_postmemes = true;
|
|
296
|
+
account.cmd_tidy = true;
|
|
297
|
+
account.cmd_use = true;
|
|
298
|
+
account.cmd_deposit = true;
|
|
299
|
+
} else {
|
|
300
|
+
// Enable only specified commands
|
|
301
|
+
const cmdKeys = {
|
|
302
|
+
beg: 'cmd_beg', search: 'cmd_search', crime: 'cmd_crime',
|
|
303
|
+
hunt: 'cmd_hunt', dig: 'cmd_dig', fish: 'cmd_fish',
|
|
304
|
+
farm: 'cmd_farm', work: 'cmd_work', stream: 'cmd_stream',
|
|
305
|
+
daily: 'cmd_daily', weekly: 'cmd_weekly', monthly: 'cmd_monthly',
|
|
306
|
+
hl: 'cmd_highlow', blackjack: 'cmd_blackjack', roulette: 'cmd_roulette',
|
|
307
|
+
slots: 'cmd_slots', snakeeyes: 'cmd_snakeeyes', trivia: 'cmd_trivia',
|
|
308
|
+
scratch: 'cmd_scratch', adventure: 'cmd_adventure',
|
|
309
|
+
postmemes: 'cmd_postmemes', tidy: 'cmd_tidy',
|
|
310
|
+
use: 'cmd_use', deposit: 'cmd_deposit',
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// Disable all first
|
|
314
|
+
Object.values(cmdKeys).forEach(key => { account[key] = false; });
|
|
315
|
+
|
|
316
|
+
// Enable selected
|
|
317
|
+
preset.commands.forEach(cmd => {
|
|
318
|
+
const key = cmdKeys[cmd];
|
|
319
|
+
if (key) account[key] = true;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Set thresholds
|
|
324
|
+
account.afk_deposit_threshold = preset.deposit_threshold;
|
|
325
|
+
account.afk_sell_threshold = preset.sell_threshold;
|
|
326
|
+
account.afk_gamble_enabled = preset.gamble_enabled;
|
|
327
|
+
account.afk_gamble_limit = preset.gamble_limit || 10;
|
|
328
|
+
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── Exports ───────────────────────────────────────────────────
|
|
333
|
+
module.exports = {
|
|
334
|
+
// Main functions
|
|
335
|
+
buildSmartQueue,
|
|
336
|
+
calculateCommandPriority,
|
|
337
|
+
shouldAutoDeposit,
|
|
338
|
+
shouldAutoSell,
|
|
339
|
+
getItemsToSell,
|
|
340
|
+
isSleepMode,
|
|
341
|
+
applyPreset,
|
|
342
|
+
|
|
343
|
+
// Getters
|
|
344
|
+
getEnabledCommands,
|
|
345
|
+
|
|
346
|
+
// Constants
|
|
347
|
+
AFK_CONFIG,
|
|
348
|
+
AFK_PRESETS,
|
|
349
|
+
};
|
package/lib/antiDetect.js
CHANGED
|
@@ -34,6 +34,16 @@ const CONFIG = Object.freeze({
|
|
|
34
34
|
|
|
35
35
|
// Pattern randomization
|
|
36
36
|
PATTERN_RESET_CHANCE: 0.2, // 20% chance to reset timing pattern
|
|
37
|
+
|
|
38
|
+
// Session-based behavior (humans get tired/faster over time)
|
|
39
|
+
SESSION_TIRE_FACTOR: 0.001, // Get 0.1% slower per action
|
|
40
|
+
SESSION_MAX_TIRED: 0.15, // Max 15% slower when tired
|
|
41
|
+
|
|
42
|
+
// Time-of-day awareness (humans are slower at night)
|
|
43
|
+
TIME_DAWN: { hour: 6, multiplier: 0.9 }, // Faster in morning
|
|
44
|
+
TIME_DAY: { hour: 12, multiplier: 1.0 }, // Normal during day
|
|
45
|
+
TIME_EVENING: { hour: 18, multiplier: 1.05 }, // Slightly slower evening
|
|
46
|
+
TIME_NIGHT: { hour: 23, multiplier: 1.15 }, // Slower at night
|
|
37
47
|
});
|
|
38
48
|
|
|
39
49
|
// ── Session State ─────────────────────────────────────────────
|
|
@@ -42,7 +52,9 @@ const sessionState = {
|
|
|
42
52
|
lastActionTime: 0,
|
|
43
53
|
currentDrift: 0,
|
|
44
54
|
patternSeed: Math.random(),
|
|
45
|
-
activityLevel: 1.0,
|
|
55
|
+
activityLevel: 1.0, // Starts neutral
|
|
56
|
+
tiredness: 0, // Increases with actions
|
|
57
|
+
sessionStart: Date.now(),
|
|
46
58
|
};
|
|
47
59
|
|
|
48
60
|
// ── Seeded Random for Reproducible Patterns ──────────────────
|
|
@@ -51,6 +63,30 @@ function seededRandom(seed) {
|
|
|
51
63
|
return x - Math.floor(x);
|
|
52
64
|
}
|
|
53
65
|
|
|
66
|
+
// ── Time-of-Day Multiplier ────────────────────────────────────
|
|
67
|
+
/**
|
|
68
|
+
* Get delay multiplier based on current hour.
|
|
69
|
+
* Humans are slower at night, faster in the morning.
|
|
70
|
+
*/
|
|
71
|
+
function getTimeOfDayMultiplier() {
|
|
72
|
+
const hour = new Date().getHours();
|
|
73
|
+
|
|
74
|
+
// Simple interpolation between time periods
|
|
75
|
+
if (hour >= 6 && hour < 12) {
|
|
76
|
+
// Morning: faster
|
|
77
|
+
return 0.9 + (hour - 6) * 0.016; // 0.9 → 1.0
|
|
78
|
+
} else if (hour >= 12 && hour < 18) {
|
|
79
|
+
// Day: normal
|
|
80
|
+
return 1.0 + (hour - 12) * 0.008; // 1.0 → 1.05
|
|
81
|
+
} else if (hour >= 18 && hour < 23) {
|
|
82
|
+
// Evening: slightly slower
|
|
83
|
+
return 1.05 + (hour - 18) * 0.02; // 1.05 → 1.15
|
|
84
|
+
} else {
|
|
85
|
+
// Night: slowest
|
|
86
|
+
return 1.15;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
54
90
|
// ── Adaptive Delay Calculation ────────────────────────────────
|
|
55
91
|
/**
|
|
56
92
|
* Calculate human-like delay based on context.
|
|
@@ -101,6 +137,14 @@ async function calcAdaptiveDelay(options = {}) {
|
|
|
101
137
|
);
|
|
102
138
|
multiplier *= (1 + sessionState.currentDrift);
|
|
103
139
|
|
|
140
|
+
// Apply tiredness factor (humans get slower over long sessions)
|
|
141
|
+
sessionState.tiredness += CONFIG.SESSION_TIRE_FACTOR;
|
|
142
|
+
sessionState.tiredness = Math.min(CONFIG.SESSION_MAX_TIRED, sessionState.tiredness);
|
|
143
|
+
multiplier *= (1 + sessionState.tiredness);
|
|
144
|
+
|
|
145
|
+
// Apply time-of-day multiplier
|
|
146
|
+
multiplier *= getTimeOfDayMultiplier();
|
|
147
|
+
|
|
104
148
|
// Pattern reset for unpredictability
|
|
105
149
|
if (Math.random() < CONFIG.PATTERN_RESET_CHANCE) {
|
|
106
150
|
sessionState.patternSeed = Math.random();
|
|
@@ -146,14 +190,20 @@ function resetSession() {
|
|
|
146
190
|
sessionState.currentDrift = 0;
|
|
147
191
|
sessionState.patternSeed = Math.random();
|
|
148
192
|
sessionState.activityLevel = 1.0;
|
|
193
|
+
sessionState.tiredness = 0;
|
|
194
|
+
sessionState.sessionStart = Date.now();
|
|
149
195
|
}
|
|
150
196
|
|
|
151
197
|
// ── Get Session Stats (for debugging) ─────────────────────────
|
|
152
198
|
function getSessionStats() {
|
|
199
|
+
const elapsed = Date.now() - sessionState.sessionStart;
|
|
153
200
|
return {
|
|
154
201
|
actionCount: sessionState.actionCount,
|
|
155
202
|
currentDrift: sessionState.currentDrift,
|
|
203
|
+
tiredness: sessionState.tiredness,
|
|
156
204
|
timeSinceLast: Date.now() - sessionState.lastActionTime,
|
|
205
|
+
sessionDuration: Math.round(elapsed / 60000), // minutes
|
|
206
|
+
timeMultiplier: getTimeOfDayMultiplier(),
|
|
157
207
|
};
|
|
158
208
|
}
|
|
159
209
|
|
package/lib/grinder.js
CHANGED
|
@@ -1184,10 +1184,9 @@ class AccountWorker {
|
|
|
1184
1184
|
accountId: this.account.id,
|
|
1185
1185
|
redis,
|
|
1186
1186
|
onPageProgress: ({ page, total }) => {
|
|
1187
|
-
//
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
process.stdout.write(`${erase}${this.color}${progress}${c.reset}`);
|
|
1187
|
+
// Minimal progress update on same line
|
|
1188
|
+
const erase = '\x1b[2K\r';
|
|
1189
|
+
process.stdout.write(`${erase}${this.color}[inv] ${page}/${total}${c.reset}`);
|
|
1191
1190
|
},
|
|
1192
1191
|
});
|
|
1193
1192
|
|
|
@@ -2561,25 +2560,25 @@ async function start(apiKey, apiUrl) {
|
|
|
2561
2560
|
hintGC();
|
|
2562
2561
|
}
|
|
2563
2562
|
|
|
2564
|
-
// Phase 2: Run inventory on ALL accounts (must complete before
|
|
2565
|
-
log('info', `${c.dim}Checking inventory for
|
|
2563
|
+
// Phase 2: Run inventory on ALL accounts in parallel (must complete before grinding)
|
|
2564
|
+
log('info', `${c.dim}Checking inventory for ${workers.length} accounts...${c.reset}`);
|
|
2565
|
+
|
|
2566
|
+
// Parallel inventory checks with single-line progress
|
|
2566
2567
|
let invDone = 0;
|
|
2567
2568
|
let invFailed = 0;
|
|
2569
|
+
const total = workers.length;
|
|
2568
2570
|
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
for (let i = 0; i < workers.length; i++) {
|
|
2572
|
-
const w = workers[i];
|
|
2573
|
-
const label = w?.username || w?.account?.label || w?.account?.id || `account-${i + 1}`;
|
|
2571
|
+
await Promise.all(workers.map(async (w, i) => {
|
|
2572
|
+
const label = w?.username || w?.account?.label || 'account';
|
|
2574
2573
|
|
|
2575
|
-
// Update
|
|
2576
|
-
const
|
|
2577
|
-
process.stdout.write(
|
|
2574
|
+
// Update progress on same line
|
|
2575
|
+
const progress = `[inv] ${invDone + invFailed + 1}/${total}: ${label}`;
|
|
2576
|
+
process.stdout.write(`\x1b[2K\r${c.dim}${progress}${c.reset}`);
|
|
2578
2577
|
|
|
2579
2578
|
try {
|
|
2580
2579
|
const invRes = await w.checkInventory({
|
|
2581
2580
|
force: true,
|
|
2582
|
-
startupProgress: { current: i + 1, total
|
|
2581
|
+
startupProgress: { current: i + 1, total },
|
|
2583
2582
|
requireComplete: true,
|
|
2584
2583
|
maxAttempts: 3,
|
|
2585
2584
|
});
|
|
@@ -2588,11 +2587,11 @@ async function start(apiKey, apiUrl) {
|
|
|
2588
2587
|
} catch {
|
|
2589
2588
|
invFailed++;
|
|
2590
2589
|
}
|
|
2591
|
-
}
|
|
2590
|
+
}));
|
|
2592
2591
|
|
|
2593
|
-
//
|
|
2592
|
+
// Final newline and summary
|
|
2594
2593
|
process.stdout.write('\n');
|
|
2595
|
-
log('success',
|
|
2594
|
+
log('success', `Inventory: ${invDone}/${total} done${invFailed > 0 ? `, ${c.yellow}${invFailed} failed${c.reset}` : ''}`);
|
|
2596
2595
|
|
|
2597
2596
|
if (invFailed > 0) {
|
|
2598
2597
|
log('error', `${c.red}Inventory phase incomplete: ${invDone}/${workers.length} complete, ${invFailed} failed/incomplete. Not starting grind loops.${c.reset}`);
|
|
@@ -2610,6 +2609,8 @@ async function start(apiKey, apiUrl) {
|
|
|
2610
2609
|
startTime = Date.now();
|
|
2611
2610
|
dashboardStarted = true;
|
|
2612
2611
|
setDashboardActive(true);
|
|
2612
|
+
// Setup keyboard shortcuts
|
|
2613
|
+
setupKeyboardShortcuts();
|
|
2613
2614
|
// Clear entire screen so startup logs don't create ghost bars
|
|
2614
2615
|
process.stdout.write('\x1b[2J\x1b[H');
|
|
2615
2616
|
process.stdout.write(c.hide);
|
|
@@ -2753,4 +2754,76 @@ async function start(apiKey, apiUrl) {
|
|
|
2753
2754
|
});
|
|
2754
2755
|
}
|
|
2755
2756
|
|
|
2756
|
-
|
|
2757
|
+
// ══════════════════════════════════════════════════════════════
|
|
2758
|
+
// ═ Keyboard Shortcuts (Quality of Life)
|
|
2759
|
+
// ══════════════════════════════════════════════════════════════
|
|
2760
|
+
// Single-key shortcuts for common actions
|
|
2761
|
+
function setupKeyboardShortcuts() {
|
|
2762
|
+
if (process.stdin.isTTY) {
|
|
2763
|
+
process.stdin.setRawMode(true);
|
|
2764
|
+
process.stdin.resume();
|
|
2765
|
+
process.stdin.setEncoding('utf8');
|
|
2766
|
+
|
|
2767
|
+
console.log(`\n ${c.dim}Keyboard shortcuts: ${c.reset}p=pause/resume all ${c.dim}·${c.reset} r=resume all ${c.dim}·${c.reset} s=status ${c.dim}·${c.reset} q=quit ${c.dim}·${c.reset} 1-9=toggle account`);
|
|
2768
|
+
|
|
2769
|
+
process.stdin.on('data', (key) => {
|
|
2770
|
+
const k = key.toString().toLowerCase();
|
|
2771
|
+
|
|
2772
|
+
// Ctrl+C or q = quit
|
|
2773
|
+
if (k === '\u0003' || k === 'q') {
|
|
2774
|
+
console.log(`\n\n ${c.yellow}Shutting down gracefully...${c.reset}`);
|
|
2775
|
+
process.emit('SIGINT');
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
// p = pause all accounts
|
|
2780
|
+
if (k === 'p') {
|
|
2781
|
+
let paused = 0;
|
|
2782
|
+
workers.forEach(w => { if (w.running && !w.paused) { w.paused = true; paused++; } });
|
|
2783
|
+
console.log(`\n ${c.yellow}Paused ${paused} accounts${c.reset}`);
|
|
2784
|
+
return;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
// r = resume all accounts
|
|
2788
|
+
if (k === 'r') {
|
|
2789
|
+
let resumed = 0;
|
|
2790
|
+
workers.forEach(w => { if (w.paused) { w.paused = false; resumed++; } });
|
|
2791
|
+
console.log(`\n ${c.green}Resumed ${resumed} accounts${c.reset}`);
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
// s = show status summary
|
|
2796
|
+
if (k === 's') {
|
|
2797
|
+
console.log(`\n ${c.bold}Status Summary:${c.reset}`);
|
|
2798
|
+
const active = workers.filter(w => w.running && !w.paused).length;
|
|
2799
|
+
const paused = workers.filter(w => w.paused).length;
|
|
2800
|
+
const offline = workers.filter(w => !w.running).length;
|
|
2801
|
+
const recovering = workers.filter(w => w._recoveryAttempts > 0 && w._errorCooldownUntil > Date.now()).length;
|
|
2802
|
+
console.log(` ${c.green}● ${active} active${c.reset} ${c.yellow}⏸ ${paused} paused${c.reset} ${c.red}○ ${offline} offline${c.reset} ${c.yellow}↻ ${recovering} recovering${c.reset}`);
|
|
2803
|
+
console.log(` ${c.dim}Total earnings: ⏣ ${workers.reduce((s, w) => s + (w.stats.coins || 0), 0).toLocaleString()}${c.reset}`);
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// 1-9 = toggle specific account (for small account counts)
|
|
2808
|
+
const num = parseInt(k, 10);
|
|
2809
|
+
if (num >= 1 && num <= 9 && workers[num - 1]) {
|
|
2810
|
+
const w = workers[num - 1];
|
|
2811
|
+
w.paused = !w.paused;
|
|
2812
|
+
console.log(`\n ${w.color}${w.username}${c.reset} ${w.paused ? c.yellow + 'paused' : c.green + 'resumed'}${c.reset}`);
|
|
2813
|
+
return;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
// ? = show help
|
|
2817
|
+
if (k === '?' || k === 'h') {
|
|
2818
|
+
console.log(`\n ${c.bold}Keyboard Shortcuts:${c.reset}`);
|
|
2819
|
+
console.log(` ${c.white}p${c.reset} Pause all accounts`);
|
|
2820
|
+
console.log(` ${c.white}r${c.reset} Resume all accounts`);
|
|
2821
|
+
console.log(` ${c.white}s${c.reset} Show status summary`);
|
|
2822
|
+
console.log(` ${c.white}q${c.reset} Quit gracefully`);
|
|
2823
|
+
console.log(` ${c.white}1-9${c.reset} Toggle account N`);
|
|
2824
|
+
console.log(` ${c.white}?${c.reset} Show this help`);
|
|
2825
|
+
return;
|
|
2826
|
+
}
|
|
2827
|
+
});
|
|
2828
|
+
}
|
|
2829
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dankgrinder",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.281.0",
|
|
4
4
|
"description": "Dank Memer automation engine — grind coins while you sleep",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dankgrinder": "bin/dankgrinder.js"
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
],
|
|
20
20
|
"author": "DankGrinder",
|
|
21
21
|
"license": "MIT",
|
|
22
|
+
"scripts": {
|
|
23
|
+
"start": "node bin/dankgrinder.js",
|
|
24
|
+
"test": "node --test test/**/*.test.js"
|
|
25
|
+
},
|
|
22
26
|
"dependencies": {
|
|
23
27
|
"debug": "^4.4.0",
|
|
24
28
|
"discord.js-selfbot-v13": "3.5.0",
|