dankgrinder 8.91.0 → 8.93.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,18 @@ class AccountWorker {
1538
1544
  return;
1539
1545
  }
1540
1546
 
1547
+ // Level-locked detection — check rawLogger (CV2/embed text) NOT just content
1548
+ const raw = rawLogger.getLastRaw(this.channel?.id);
1549
+ const rawAllText = (raw?.content || '') + '\n' + (raw?.cv2Text || '') + '\n' + (raw?.embedText || '');
1550
+ if (rawAllText.toLowerCase().includes('not unlocked')) {
1551
+ const lvMatch = rawAllText.match(/level\s*(\d+)/i);
1552
+ if (lvMatch) {
1553
+ const targetLv = parseInt(lvMatch[1]);
1554
+ cmdResult.levelLocked = targetLv;
1555
+ this.log('warn', `Command /${cmdName} requires Level ${targetLv} quests`);
1556
+ }
1557
+ }
1558
+
1541
1559
  // Captcha/verification detection — Aho-Corasick O(n) single-pass match
1542
1560
  // Instead of 8 separate .includes() calls (each O(n)), one automaton pass
1543
1561
  if (captchaDetector.hasAny(resultLower)) {
@@ -1708,6 +1726,14 @@ class AccountWorker {
1708
1726
  this.globalCooldownUntil = Math.max(this.globalCooldownUntil, Date.now() + holdSec * 1000);
1709
1727
  }
1710
1728
 
1729
+ // Detect level-locked command → start quest mode
1730
+ if (cmdResult.levelLocked) {
1731
+ const targetLv = cmdResult.levelLocked;
1732
+ if (this._startLevelQuests(targetLv)) {
1733
+ this.log('info', `[QUEST] Detected locked command — target Level ${targetLv}`);
1734
+ }
1735
+ }
1736
+
1711
1737
  this.stats.successes++;
1712
1738
  // Format result with clean command name and colored earnings
1713
1739
  const formattedResult = formatCommandResult(cmdName, result, earned, spent);
@@ -1856,6 +1882,36 @@ class AccountWorker {
1856
1882
  // Alert is NOT scheduled — it's reactive (listener-based, see grindLoop)
1857
1883
  ].map(Object.freeze);
1858
1884
 
1885
+ // ── Level Quest System — blocks grinding until quests complete ──
1886
+ static LEVEL_QUESTS = {
1887
+ 1: [
1888
+ { cmd: 'beg', times: 2 },
1889
+ { cmd: 'search', times: 2 },
1890
+ { cmd: 'tidy', times: 2 },
1891
+ ],
1892
+ 3: [
1893
+ { cmd: 'work apply', times: 1 },
1894
+ { cmd: 'work shift', times: 1 },
1895
+ { cmd: 'shop sell common coin 1', times: 2 },
1896
+ ],
1897
+ 5: [
1898
+ { cmd: 'slots', times: 1, bet: 50000 },
1899
+ { cmd: 'cointoss', times: 1, bet: 50000 },
1900
+ { cmd: 'snakeeyes', times: 1, bet: 50000 },
1901
+ ],
1902
+ };
1903
+
1904
+ _startLevelQuests(targetLevel) {
1905
+ if (this._levelQuestDone.has(targetLevel)) return false;
1906
+ const quests = AccountWorker.LEVEL_QUESTS[targetLevel];
1907
+ if (!quests || quests.length === 0) return false;
1908
+ this._levelQuestQueue = quests.map(q => ({ ...q, level: targetLevel }));
1909
+ this._levelQuestActive = true;
1910
+ this._questBetOverride = null;
1911
+ this.log('info', `[QUEST] Level ${targetLevel} quests started — grinding PAUSED`);
1912
+ return true;
1913
+ }
1914
+
1859
1915
  async buildCommandQueue() {
1860
1916
  const heap = new MinHeap();
1861
1917
  const now = Date.now();
@@ -2106,6 +2162,27 @@ class AccountWorker {
2106
2162
 
2107
2163
  // ── Main Non-Blocking Grind Scheduler ───────────────────────
2108
2164
  async tick() {
2165
+ // BLOCK: quest mode active — run quests only, no normal grinding
2166
+ if (this._levelQuestActive && this._levelQuestQueue.length > 0) {
2167
+ const quest = this._levelQuestQueue[0];
2168
+ this._questBetOverride = quest.bet || null;
2169
+ const prefix = this.account.use_slash ? '/' : 'pls';
2170
+ this.setStatus(`[L${quest.level}] QUEST ${quest.cmd} (${quest.times}x left)`);
2171
+ this.lastStatus = `[L${quest.level}] ${quest.cmd}`;
2172
+ await this.runCommand(quest.cmd, prefix);
2173
+ this._questBetOverride = null;
2174
+ quest.times--;
2175
+ if (quest.times <= 0) this._levelQuestQueue.shift();
2176
+ if (this._levelQuestQueue.length === 0) {
2177
+ this._levelQuestActive = false;
2178
+ this._levelQuestDone.add(quest.level);
2179
+ this.log('success', `[QUEST] Level ${quest.level} quests DONE — resuming grinding`);
2180
+ this.setStatus('idle');
2181
+ }
2182
+ this.tickTimeout = setTimeout(() => this.tick(), 2500);
2183
+ return;
2184
+ }
2185
+
2109
2186
  if (!this.running || shutdownCalled) return;
2110
2187
  if (this.paused) {
2111
2188
  this.setStatus('PAUSED (captcha)');
@@ -3042,6 +3119,7 @@ async function start(apiKey, apiUrl, opts = {}) {
3042
3119
  let shutdownInProgress = false;
3043
3120
 
3044
3121
  async function gracefulShutdown(signal) {
3122
+ const chalk = require('chalk');
3045
3123
  if (shutdownInProgress) return;
3046
3124
  shutdownInProgress = true;
3047
3125
  shutdownCalled = true;
@@ -3062,8 +3140,8 @@ const figlet = require('figlet');
3062
3140
  let table = '';
3063
3141
 
3064
3142
 
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';
3143
+ table += ' ' + chalk.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n\n';
3144
+ table += ' ' + chalk.dim('ACCOUNT'.padEnd(25)) + chalk.dim('GAINED'.padEnd(15)) + chalk.dim('COMMANDS'.padEnd(10)) + chalk.dim('OK %') + '\n\n';
3067
3145
 
3068
3146
  for (const wk of workers) {
3069
3147
  const rate = wk.stats.commands > 0 ? ((wk.stats.successes / wk.stats.commands) * 100).toFixed(0) : 0;
@@ -3087,14 +3165,14 @@ const figlet = require('figlet');
3087
3165
  table += ` ${c.dim}${'No workers...'.padEnd(25)}${c.reset} ${''.padEnd(15)} ${''.padEnd(10)} ${''.padEnd(6)}\n`;
3088
3166
  }
3089
3167
 
3090
- table += '\n ' + c.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n';
3168
+ table += '\n ' + chalk.dim('────────────────────────────────────────────────────────────────────────────────────────────────') + '\n';
3091
3169
  process.stdout.write(table);
3092
3170
 
3093
3171
  const memFinal = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
3094
3172
  const cpm = globalCmdRate.getRate().toFixed(1);
3095
3173
 
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'));
3174
+ console.log('\n ' + chalk.magenta('◆') + ' ' + chalk.bold('TOTAL') + ' ' + chalk.green('+⏣' + finalCoins.toLocaleString()) + ' ' + chalk.dim('in ' + ui.formatUptime()));
3175
+ console.log(' ' + chalk.cyan('◆') + ' ' + chalk.bold('STATS') + ' ' + finalCmds + chalk.dim(' cmds │ ') + '~' + cpm + chalk.dim(' cmd/m │ ') + memFinal + chalk.dim('MB RAM'));
3098
3176
  console.log('\n');
3099
3177
 
3100
3178
 
@@ -3127,7 +3205,7 @@ const figlet = require('figlet');
3127
3205
 
3128
3206
  const disResult = redis?.disconnect?.();
3129
3207
  if (disResult && typeof disResult.catch === 'function') disResult.catch(() => {});
3130
- console.log(c.magenta + figlet.textSync('GOODBYE!', { font: 'ANSI Shadow' }) + c.reset + '\n');
3208
+ console.log(chalk.magenta + figlet.textSync('GOODBYE!', { font: 'ANSI Shadow' }) + c.reset + '\n');
3131
3209
  // Force exit so Ctrl+C always terminates immediately
3132
3210
  setTimeout(() => process.exit(0), 2000);
3133
3211
  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.93.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"