dankgrinder 8.91.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 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;
@@ -3062,8 +3137,8 @@ const figlet = require('figlet');
3062
3137
  let table = '';
3063
3138
 
3064
3139
 
3065
- table += ' ' + c.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n\n';
3066
- table += ' ' + c.dim('ACCOUNT'.padEnd(25)) + c.dim('GAINED'.padEnd(15)) + c.dim('COMMANDS'.padEnd(10)) + c.dim('OK %') + '\n\n';
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';
3067
3142
 
3068
3143
  for (const wk of workers) {
3069
3144
  const rate = wk.stats.commands > 0 ? ((wk.stats.successes / wk.stats.commands) * 100).toFixed(0) : 0;
@@ -3087,14 +3162,14 @@ const figlet = require('figlet');
3087
3162
  table += ` ${c.dim}${'No workers...'.padEnd(25)}${c.reset} ${''.padEnd(15)} ${''.padEnd(10)} ${''.padEnd(6)}\n`;
3088
3163
  }
3089
3164
 
3090
- table += '\n ' + c.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n';
3165
+ table += '\n ' + chalk.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n';
3091
3166
  process.stdout.write(table);
3092
3167
 
3093
3168
  const memFinal = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
3094
3169
  const cpm = globalCmdRate.getRate().toFixed(1);
3095
3170
 
3096
- console.log('\n ' + c.magenta('◆') + ' ' + c.bold('TOTAL') + ' ' + c.green('+⏣' + finalCoins.toLocaleString()) + ' ' + c.dim('in ' + ui.formatUptime()));
3097
- console.log(' ' + c.cyan('◆') + ' ' + c.bold('STATS') + ' ' + finalCmds + c.dim(' cmds │ ') + '~' + cpm + c.dim(' cmd/m │ ') + memFinal + c.dim('MB RAM'));
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'));
3098
3173
  console.log('\n');
3099
3174
 
3100
3175
 
@@ -3127,7 +3202,7 @@ const figlet = require('figlet');
3127
3202
 
3128
3203
  const disResult = redis?.disconnect?.();
3129
3204
  if (disResult && typeof disResult.catch === 'function') disResult.catch(() => {});
3130
- console.log(c.magenta + figlet.textSync('GOODBYE!', { font: 'ANSI Shadow' }) + c.reset + '\n');
3205
+ console.log(chalk.magenta + figlet.textSync('GOODBYE!', { font: 'ANSI Shadow' }) + c.reset + '\n');
3131
3206
  // Force exit so Ctrl+C always terminates immediately
3132
3207
  setTimeout(() => process.exit(0), 2000);
3133
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
- // ── allText fallback — catches CV2 messages where d.components is a JSON string ──
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "8.91.0",
3
+ "version": "8.92.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"