dankgrinder 8.90.0 → 8.92.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/grinder.js +95 -16
- package/lib/rawLogger.js +6 -1
- package/lib/ui.js +7 -5
- package/package.json +1 -1
package/lib/grinder.js
CHANGED
|
@@ -665,6 +665,12 @@ class AccountWorker {
|
|
|
665
665
|
this._sellRunning = false;
|
|
666
666
|
this.failStreak = 0;
|
|
667
667
|
this.globalCooldownUntil = 0;
|
|
668
|
+
// Level quest system — blocks grinding until quests complete
|
|
669
|
+
this._level = 1;
|
|
670
|
+
this._levelQuestActive = false;
|
|
671
|
+
this._levelQuestQueue = [];
|
|
672
|
+
this._levelQuestDone = new Set();
|
|
673
|
+
this._questBetOverride = null;
|
|
668
674
|
this.commandQueue = null;
|
|
669
675
|
this.lastHealthCheck = Date.now();
|
|
670
676
|
this.doneToday = new Map();
|
|
@@ -1479,7 +1485,7 @@ class AccountWorker {
|
|
|
1479
1485
|
client: this.client,
|
|
1480
1486
|
safeAnswers: cmdName === 'search' ? safeParseJSON(this.account.search_answers, []) :
|
|
1481
1487
|
cmdName === 'crime' ? safeParseJSON(this.account.crime_answers, []) : [],
|
|
1482
|
-
betAmount: ['blackjack'].includes(cmdName) ? bjBet : gambBet,
|
|
1488
|
+
betAmount: this._questBetOverride || (['blackjack'].includes(cmdName) ? bjBet : gambBet),
|
|
1483
1489
|
accountId: this.account.id,
|
|
1484
1490
|
redis,
|
|
1485
1491
|
};
|
|
@@ -1538,6 +1544,16 @@ class AccountWorker {
|
|
|
1538
1544
|
return;
|
|
1539
1545
|
}
|
|
1540
1546
|
|
|
1547
|
+
// Level-locked detection — "not unlocked" means this command needs quests
|
|
1548
|
+
if (resultLower.includes('not unlocked') || resultLower.includes('complete tasks to unlock')) {
|
|
1549
|
+
const lvMatch = result.match(/level\s*(\d+)/i);
|
|
1550
|
+
if (lvMatch) {
|
|
1551
|
+
const targetLv = parseInt(lvMatch[1]);
|
|
1552
|
+
cmdResult.levelLocked = targetLv;
|
|
1553
|
+
this.log('warn', `Command /${cmdName} requires Level ${targetLv} quests`);
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1541
1557
|
// Captcha/verification detection — Aho-Corasick O(n) single-pass match
|
|
1542
1558
|
// Instead of 8 separate .includes() calls (each O(n)), one automaton pass
|
|
1543
1559
|
if (captchaDetector.hasAny(resultLower)) {
|
|
@@ -1708,6 +1724,14 @@ class AccountWorker {
|
|
|
1708
1724
|
this.globalCooldownUntil = Math.max(this.globalCooldownUntil, Date.now() + holdSec * 1000);
|
|
1709
1725
|
}
|
|
1710
1726
|
|
|
1727
|
+
// Detect level-locked command → start quest mode
|
|
1728
|
+
if (cmdResult.levelLocked) {
|
|
1729
|
+
const targetLv = cmdResult.levelLocked;
|
|
1730
|
+
if (this._startLevelQuests(targetLv)) {
|
|
1731
|
+
this.log('info', `[QUEST] Detected locked command — target Level ${targetLv}`);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1711
1735
|
this.stats.successes++;
|
|
1712
1736
|
// Format result with clean command name and colored earnings
|
|
1713
1737
|
const formattedResult = formatCommandResult(cmdName, result, earned, spent);
|
|
@@ -1856,6 +1880,35 @@ class AccountWorker {
|
|
|
1856
1880
|
// Alert is NOT scheduled — it's reactive (listener-based, see grindLoop)
|
|
1857
1881
|
].map(Object.freeze);
|
|
1858
1882
|
|
|
1883
|
+
// ── Level Quest System — blocks grinding until quests complete ──
|
|
1884
|
+
static LEVEL_QUESTS = {
|
|
1885
|
+
1: [
|
|
1886
|
+
{ cmd: 'beg', times: 2 },
|
|
1887
|
+
{ cmd: 'search', times: 2 },
|
|
1888
|
+
{ cmd: 'tidy', times: 2 },
|
|
1889
|
+
],
|
|
1890
|
+
3: [
|
|
1891
|
+
{ cmd: 'work shift', times: 1 },
|
|
1892
|
+
{ cmd: 'shop sell common coin 1', times: 2 },
|
|
1893
|
+
],
|
|
1894
|
+
5: [
|
|
1895
|
+
{ cmd: 'slots', times: 1, bet: 50000 },
|
|
1896
|
+
{ cmd: 'cointoss', times: 1, bet: 50000 },
|
|
1897
|
+
{ cmd: 'snakeeyes', times: 1, bet: 50000 },
|
|
1898
|
+
],
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1901
|
+
_startLevelQuests(targetLevel) {
|
|
1902
|
+
if (this._levelQuestDone.has(targetLevel)) return false;
|
|
1903
|
+
const quests = AccountWorker.LEVEL_QUESTS[targetLevel];
|
|
1904
|
+
if (!quests || quests.length === 0) return false;
|
|
1905
|
+
this._levelQuestQueue = quests.map(q => ({ ...q, level: targetLevel }));
|
|
1906
|
+
this._levelQuestActive = true;
|
|
1907
|
+
this._questBetOverride = null;
|
|
1908
|
+
this.log('info', `[QUEST] Level ${targetLevel} quests started — grinding PAUSED`);
|
|
1909
|
+
return true;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1859
1912
|
async buildCommandQueue() {
|
|
1860
1913
|
const heap = new MinHeap();
|
|
1861
1914
|
const now = Date.now();
|
|
@@ -2106,6 +2159,27 @@ class AccountWorker {
|
|
|
2106
2159
|
|
|
2107
2160
|
// ── Main Non-Blocking Grind Scheduler ───────────────────────
|
|
2108
2161
|
async tick() {
|
|
2162
|
+
// BLOCK: quest mode active — run quests only, no normal grinding
|
|
2163
|
+
if (this._levelQuestActive && this._levelQuestQueue.length > 0) {
|
|
2164
|
+
const quest = this._levelQuestQueue[0];
|
|
2165
|
+
this._questBetOverride = quest.bet || null;
|
|
2166
|
+
const prefix = this.account.use_slash ? '/' : 'pls';
|
|
2167
|
+
this.setStatus(`[L${quest.level}] QUEST ${quest.cmd} (${quest.times}x left)`);
|
|
2168
|
+
this.lastStatus = `[L${quest.level}] ${quest.cmd}`;
|
|
2169
|
+
await this.runCommand(quest.cmd, prefix);
|
|
2170
|
+
this._questBetOverride = null;
|
|
2171
|
+
quest.times--;
|
|
2172
|
+
if (quest.times <= 0) this._levelQuestQueue.shift();
|
|
2173
|
+
if (this._levelQuestQueue.length === 0) {
|
|
2174
|
+
this._levelQuestActive = false;
|
|
2175
|
+
this._levelQuestDone.add(quest.level);
|
|
2176
|
+
this.log('success', `[QUEST] Level ${quest.level} quests DONE — resuming grinding`);
|
|
2177
|
+
this.setStatus('idle');
|
|
2178
|
+
}
|
|
2179
|
+
this.tickTimeout = setTimeout(() => this.tick(), 2500);
|
|
2180
|
+
return;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2109
2183
|
if (!this.running || shutdownCalled) return;
|
|
2110
2184
|
if (this.paused) {
|
|
2111
2185
|
this.setStatus('PAUSED (captcha)');
|
|
@@ -3042,6 +3116,7 @@ async function start(apiKey, apiUrl, opts = {}) {
|
|
|
3042
3116
|
let shutdownInProgress = false;
|
|
3043
3117
|
|
|
3044
3118
|
async function gracefulShutdown(signal) {
|
|
3119
|
+
const chalk = require('chalk');
|
|
3045
3120
|
if (shutdownInProgress) return;
|
|
3046
3121
|
shutdownInProgress = true;
|
|
3047
3122
|
shutdownCalled = true;
|
|
@@ -3057,42 +3132,46 @@ async function start(apiKey, apiUrl, opts = {}) {
|
|
|
3057
3132
|
let finalCoins = 0;
|
|
3058
3133
|
let finalCmds = 0;
|
|
3059
3134
|
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
table
|
|
3135
|
+
const figlet = require('figlet');
|
|
3136
|
+
|
|
3137
|
+
let table = '';
|
|
3138
|
+
|
|
3139
|
+
|
|
3140
|
+
table += ' ' + chalk.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n\n';
|
|
3141
|
+
table += ' ' + chalk.dim('ACCOUNT'.padEnd(25)) + chalk.dim('GAINED'.padEnd(15)) + chalk.dim('COMMANDS'.padEnd(10)) + chalk.dim('OK %') + '\n\n';
|
|
3063
3142
|
|
|
3064
3143
|
for (const wk of workers) {
|
|
3065
3144
|
const rate = wk.stats.commands > 0 ? ((wk.stats.successes / wk.stats.commands) * 100).toFixed(0) : 0;
|
|
3066
3145
|
finalCoins += wk.stats.coins || 0;
|
|
3067
3146
|
finalCmds += wk.stats.commands || 0;
|
|
3068
3147
|
|
|
3069
|
-
const unRaw = (wk.username || '?').substring(0,
|
|
3148
|
+
const unRaw = (wk.username || '?').substring(0, 23);
|
|
3070
3149
|
const coinsRaw = '+⏣' + (wk.stats.coins || 0).toLocaleString();
|
|
3071
3150
|
const cmdsRaw = String(wk.stats.commands || 0);
|
|
3072
3151
|
const okRaw = rate + '%';
|
|
3073
3152
|
|
|
3074
|
-
const un = unRaw.padEnd(
|
|
3075
|
-
const coins = coinsRaw.substring(0, 14).padEnd(
|
|
3076
|
-
const cmds = cmdsRaw.substring(0, 8).padEnd(
|
|
3153
|
+
const un = unRaw.padEnd(25);
|
|
3154
|
+
const coins = coinsRaw.substring(0, 14).padEnd(15);
|
|
3155
|
+
const cmds = cmdsRaw.substring(0, 8).padEnd(10);
|
|
3077
3156
|
const ok = okRaw.substring(0, 6).padEnd(6);
|
|
3078
3157
|
|
|
3079
|
-
table += `
|
|
3158
|
+
table += ` ${c.cyan}${un}${c.reset} ${c.green}${coins}${c.reset} ${cmds} ${ok}\n`;
|
|
3080
3159
|
}
|
|
3081
3160
|
|
|
3082
3161
|
if (workers.length === 0) {
|
|
3083
|
-
table += `
|
|
3162
|
+
table += ` ${c.dim}${'No workers...'.padEnd(25)}${c.reset} ${''.padEnd(15)} ${''.padEnd(10)} ${''.padEnd(6)}\n`;
|
|
3084
3163
|
}
|
|
3085
3164
|
|
|
3086
|
-
table +=
|
|
3165
|
+
table += '\n ' + chalk.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n';
|
|
3087
3166
|
process.stdout.write(table);
|
|
3088
3167
|
|
|
3089
3168
|
const memFinal = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
|
|
3090
3169
|
const cpm = globalCmdRate.getRate().toFixed(1);
|
|
3091
3170
|
|
|
3092
|
-
console.log(
|
|
3093
|
-
console.log(
|
|
3094
|
-
console.log(
|
|
3095
|
-
|
|
3171
|
+
console.log('\n ' + chalk.magenta('◆') + ' ' + chalk.bold('TOTAL') + ' ' + chalk.green('+⏣' + finalCoins.toLocaleString()) + ' ' + chalk.dim('in ' + ui.formatUptime()));
|
|
3172
|
+
console.log(' ' + chalk.cyan('◆') + ' ' + chalk.bold('STATS') + ' ' + finalCmds + chalk.dim(' cmds │ ') + '~' + cpm + chalk.dim(' cmd/m │ ') + memFinal + chalk.dim('MB RAM'));
|
|
3173
|
+
console.log('\n');
|
|
3174
|
+
|
|
3096
3175
|
|
|
3097
3176
|
// Stop workers immediately (don't wait) — instant shutdown
|
|
3098
3177
|
for (const wk of workers) {
|
|
@@ -3123,7 +3202,7 @@ async function start(apiKey, apiUrl, opts = {}) {
|
|
|
3123
3202
|
|
|
3124
3203
|
const disResult = redis?.disconnect?.();
|
|
3125
3204
|
if (disResult && typeof disResult.catch === 'function') disResult.catch(() => {});
|
|
3126
|
-
console.log(
|
|
3205
|
+
console.log(chalk.magenta + figlet.textSync('GOODBYE!', { font: 'ANSI Shadow' }) + c.reset + '\n');
|
|
3127
3206
|
// Force exit so Ctrl+C always terminates immediately
|
|
3128
3207
|
setTimeout(() => process.exit(0), 2000);
|
|
3129
3208
|
process.exit(0);
|
package/lib/rawLogger.js
CHANGED
|
@@ -294,7 +294,11 @@ function detectCommand(d) {
|
|
|
294
294
|
if (anyOf(embedText, ['fishing', 'fish'])) return 'fish';
|
|
295
295
|
if (has(embedText, 'hold tight')) return 'holdtight';
|
|
296
296
|
|
|
297
|
-
//
|
|
297
|
+
// Level-gated command lock
|
|
298
|
+
if (anyOf(allText, ['not unlocked', 'complete tasks to unlock'])) {
|
|
299
|
+
const lvMatch = allText.match(/level\s*(\d+)/i);
|
|
300
|
+
return lvMatch ? 'level_locked' : 'unknown';
|
|
301
|
+
}
|
|
298
302
|
if (anyOf(allText, ['hunting', 'went hunting', 'hunting rifle', 'your aim was so bad', 'animals laughed', 'animals attacked', 'barely escaped', 'fell asleep in a tree', 'caught nothing', 'brought back literally nothing', 'rifle broke', 'imagine going into the woods'])) return 'hunt';
|
|
299
303
|
if (anyOf(allText, ['digging', 'you dug', 'dug in the dirt', 'found nothing while', 'what are the odds lol'])) return 'dig';
|
|
300
304
|
if (anyOf(allText, ['brought back']) && anyOf(allText, ['ant', 'worm', 'stickbug', 'ladybug'])) return 'dig';
|
|
@@ -712,6 +716,7 @@ module.exports = {
|
|
|
712
716
|
onDmEvent,
|
|
713
717
|
onNextEphemeral,
|
|
714
718
|
setVerbose,
|
|
719
|
+
store,
|
|
715
720
|
// Memory reads
|
|
716
721
|
getRawMessage,
|
|
717
722
|
getLastRaw,
|
package/lib/ui.js
CHANGED
|
@@ -156,11 +156,13 @@ function render() {
|
|
|
156
156
|
// 2. MINIMALIST STATS + TREND SPARKLINE
|
|
157
157
|
const profStr = totalCoins > 0 ? '+' + formatBal(totalCoins) : '+0';
|
|
158
158
|
const graph = getSparkline();
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
|
|
160
|
+
const secOnline = chalk.dim('Online') + ' ' + chalk.bold.cyan(`${onlineCount}/${_workers.length}`);
|
|
161
|
+
const secUptime = chalk.dim('Uptime') + ' ' + chalk.bold.blue(fmtUptime());
|
|
162
|
+
const secBal = chalk.dim('Bank') + ' ' + chalk.bold.green(`⏣ ${formatBal(totalBal)}`) + chalk.greenBright(` (${profStr})`);
|
|
163
|
+
const secTrend = chalk.dim('Trend') + ' ' + chalk.magenta(graph);
|
|
164
|
+
|
|
165
|
+
out += ` ${secOnline} ${chalk.dim('│')} ${secUptime} ${chalk.dim('│')} ${secBal} ${chalk.dim('│')} ${secTrend}\n`;
|
|
164
166
|
|
|
165
167
|
// Divider
|
|
166
168
|
const divWidth = Math.min(C - 4, 100);
|