dankgrinder 8.94.0 → 8.95.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
@@ -671,6 +671,7 @@ class AccountWorker {
671
671
  this._levelQuestQueue = [];
672
672
  this._levelQuestDone = new Set();
673
673
  this._questBetOverride = null;
674
+ this._commandRunning = false; // prevents grinding commands from overlapping with quest commands
674
675
  this.commandQueue = null;
675
676
  this.lastHealthCheck = Date.now();
676
677
  this.doneToday = new Map();
@@ -1729,8 +1730,11 @@ class AccountWorker {
1729
1730
  // Detect level-locked command → start quest mode
1730
1731
  if (cmdResult.levelLocked) {
1731
1732
  const targetLv = cmdResult.levelLocked;
1733
+ this.log('warn', `Command /${cmdName} is LOCKED at Level ${targetLv} — starting quest unlock flow`);
1732
1734
  if (this._startLevelQuests(targetLv)) {
1733
- this.log('info', `[QUEST] Detected locked command target Level ${targetLv}`);
1735
+ this.log('info', `[QUEST] Quest mode activated for Level ${targetLv} — grinding will resume after quests complete`);
1736
+ } else {
1737
+ this.log('warn', `[QUEST] Level ${targetLv} quests already done or not defined — grinding continues (may re-trigger)`);
1734
1738
  }
1735
1739
  }
1736
1740
 
@@ -1908,7 +1912,12 @@ class AccountWorker {
1908
1912
  this._levelQuestQueue = quests.map(q => ({ ...q, level: targetLevel }));
1909
1913
  this._levelQuestActive = true;
1910
1914
  this._questBetOverride = null;
1915
+ // Clear commandQueue so no stale grinding commands fire after quests finish
1916
+ this.commandQueue = null;
1917
+ this._commandRunning = false; // cancel any in-flight grinding command
1911
1918
  this.log('info', `[QUEST] Level ${targetLevel} quests started — grinding PAUSED`);
1919
+ const questList = this._levelQuestQueue.map(q => `"${q.cmd}" x${q.times}`).join(', ');
1920
+ this.log('info', `[QUEST] Quests: ${questList}`);
1912
1921
  return true;
1913
1922
  }
1914
1923
 
@@ -2169,7 +2178,11 @@ class AccountWorker {
2169
2178
  const prefix = this.account.use_slash ? '/' : 'pls';
2170
2179
  this.setStatus(`[L${quest.level}] QUEST ${quest.cmd} (${quest.times}x left)`);
2171
2180
  this.lastStatus = `[L${quest.level}] ${quest.cmd}`;
2181
+ this._commandRunning = true;
2182
+ this.busy = true; // also set busy so grinding tick path blocks while quest runs
2172
2183
  await this.runCommand(quest.cmd, prefix);
2184
+ this._commandRunning = false;
2185
+ this.busy = false;
2173
2186
  this._questBetOverride = null;
2174
2187
  quest.times--;
2175
2188
  if (quest.times <= 0) this._levelQuestQueue.shift();
@@ -2177,6 +2190,8 @@ class AccountWorker {
2177
2190
  this._levelQuestActive = false;
2178
2191
  this._levelQuestDone.add(quest.level);
2179
2192
  this.log('success', `[QUEST] Level ${quest.level} quests DONE — resuming grinding`);
2193
+ // Null out commandQueue so buildCommandQueue picks only unlocked commands
2194
+ this.commandQueue = null;
2180
2195
  this.setStatus('idle');
2181
2196
  }
2182
2197
  this.tickTimeout = setTimeout(() => this.tick(), 2500);
@@ -2198,6 +2213,11 @@ class AccountWorker {
2198
2213
  this.tickTimeout = setTimeout(() => this.tick(), 2000);
2199
2214
  return;
2200
2215
  }
2216
+ // Block grinding if a quest command is currently running (race condition guard)
2217
+ if (this._commandRunning) {
2218
+ this.tickTimeout = setTimeout(() => this.tick(), 1500);
2219
+ return;
2220
+ }
2201
2221
 
2202
2222
  const now = Date.now();
2203
2223
 
package/lib/rawLogger.js CHANGED
@@ -528,14 +528,42 @@ function attachDmLogger(client, opts = {}) {
528
528
  if (targetAuthorId && d.author?.id !== targetAuthorId) return;
529
529
 
530
530
  const content = d.content || '';
531
+ // Also extract individual embed field values for richer level-up detection
532
+ const embedFieldsText = (d.embeds || []).map(e => {
533
+ const fields = (e.fields || []).map(f => `${f.name || ''} ${f.value || ''}`).join('\n');
534
+ return `${e.title || ''} ${e.description || ''} ${e.footer?.text || ''} ${e.author?.name || ''} ${fields}`;
535
+ }).join('\n');
531
536
  const embedText = extractEmbedText(d.embeds).toLowerCase();
532
- const allText = (content + '\n' + embedText).toLowerCase();
537
+ const allText = (content + '\n' + embedFieldsText + '\n' + embedText).toLowerCase();
533
538
 
534
539
  // Detect event type
535
540
  let dmEvent = null;
536
- if (allText.includes('leveled up') || allText.includes('level up')) {
537
- const m = allText.match(/level\s+(\d+)\s+to\s+(\d+)/i);
538
- dmEvent = { type: 'levelup', from: m ? parseInt(m[1]) : 0, to: m ? parseInt(m[2]) : 0 };
541
+ if (allText.includes('leveled up') || allText.includes('level up') || allText.includes('to level')) {
542
+ // Try multiple level-up patterns (Dank Memer varies the message format):
543
+ // "You leveled up from level X to Y"
544
+ // "You leveled up! You're now level Y"
545
+ // "You are now level Y"
546
+ // "You advanced to level Y"
547
+ // "Congratulations! Level Y"
548
+ let fromLv = 0, toLv = 0;
549
+ const m1 = allText.match(/level\s+(\d+)\s+to\s+level?\s*(\d+)/i);
550
+ const m2 = allText.match(/leveled?\s*up[!\s]+(?:.*?)?(?:to\s+)?(?:level\s+)?(\d+)/i);
551
+ const m3 = allText.match(/(?:you(?:'re| are) now|now at|advanced? to|reached|congratulations.*?level)\s+(?:level\s+)?(\d+)/i);
552
+ const m4 = allText.match(/(?:now\s+)?(?:at\s+)?level\s*(\d+)/i);
553
+ const m5 = allText.match(/to level\s*(\d+)/i);
554
+
555
+ if (m1) { fromLv = parseInt(m1[1]); toLv = parseInt(m1[2]); }
556
+ else if (m2) { toLv = parseInt(m2[1]); }
557
+ else if (m3) { toLv = parseInt(m3[1]); }
558
+ else if (m4) { toLv = parseInt(m4[1]); }
559
+ else if (m5) { toLv = parseInt(m5[1]); }
560
+
561
+ if (toLv > 0) {
562
+ dmEvent = { type: 'levelup', from: fromLv, to: toLv };
563
+ } else if ((allText.includes('leveled up') || allText.includes('level up')) && verboseMode) {
564
+ // Level-up keyword found but no number matched — log for debugging
565
+ console.log(` [DM LEVELUP] detected keyword but no number matched in: "${allText.substring(0, 200)}"`);
566
+ }
539
567
  } else if (allText.includes('lifesaver protected') || allText.includes('you died')) {
540
568
  // Parse lifesaver count from button labels: "You have 0 Life Saver left"
541
569
  let lsLeft = -1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "8.94.0",
3
+ "version": "8.95.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"