dankgrinder 8.94.0 → 8.96.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 +56 -3
- package/lib/rawLogger.js +32 -4
- package/package.json +1 -1
package/lib/grinder.js
CHANGED
|
@@ -671,6 +671,8 @@ 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
|
|
675
|
+
this._verifyLevelUnlock = null; // holds level to verify after quest completion
|
|
674
676
|
this.commandQueue = null;
|
|
675
677
|
this.lastHealthCheck = Date.now();
|
|
676
678
|
this.doneToday = new Map();
|
|
@@ -1729,8 +1731,11 @@ class AccountWorker {
|
|
|
1729
1731
|
// Detect level-locked command → start quest mode
|
|
1730
1732
|
if (cmdResult.levelLocked) {
|
|
1731
1733
|
const targetLv = cmdResult.levelLocked;
|
|
1734
|
+
this.log('warn', `Command /${cmdName} is LOCKED at Level ${targetLv} — starting quest unlock flow`);
|
|
1732
1735
|
if (this._startLevelQuests(targetLv)) {
|
|
1733
|
-
this.log('info', `[QUEST]
|
|
1736
|
+
this.log('info', `[QUEST] Quest mode activated for Level ${targetLv} — grinding will resume after quests complete`);
|
|
1737
|
+
} else {
|
|
1738
|
+
this.log('warn', `[QUEST] Level ${targetLv} quests already done or not defined — grinding continues (may re-trigger)`);
|
|
1734
1739
|
}
|
|
1735
1740
|
}
|
|
1736
1741
|
|
|
@@ -1908,7 +1913,12 @@ class AccountWorker {
|
|
|
1908
1913
|
this._levelQuestQueue = quests.map(q => ({ ...q, level: targetLevel }));
|
|
1909
1914
|
this._levelQuestActive = true;
|
|
1910
1915
|
this._questBetOverride = null;
|
|
1916
|
+
// Clear commandQueue so no stale grinding commands fire after quests finish
|
|
1917
|
+
this.commandQueue = null;
|
|
1918
|
+
this._commandRunning = false; // cancel any in-flight grinding command
|
|
1911
1919
|
this.log('info', `[QUEST] Level ${targetLevel} quests started — grinding PAUSED`);
|
|
1920
|
+
const questList = this._levelQuestQueue.map(q => `"${q.cmd}" x${q.times}`).join(', ');
|
|
1921
|
+
this.log('info', `[QUEST] Quests: ${questList}`);
|
|
1912
1922
|
return true;
|
|
1913
1923
|
}
|
|
1914
1924
|
|
|
@@ -2162,6 +2172,33 @@ class AccountWorker {
|
|
|
2162
2172
|
|
|
2163
2173
|
// ── Main Non-Blocking Grind Scheduler ───────────────────────
|
|
2164
2174
|
async tick() {
|
|
2175
|
+
// ── Level-unlock verification: runs after quests complete ──
|
|
2176
|
+
if (this._verifyLevelUnlock) {
|
|
2177
|
+
const targetLv = this._verifyLevelUnlock;
|
|
2178
|
+
this.setStatus(`verifying level ${targetLv}...`);
|
|
2179
|
+
try {
|
|
2180
|
+
const profile = await this.checkProfile(true);
|
|
2181
|
+
const currentLv = profile.level || this._level || 0;
|
|
2182
|
+
if (currentLv >= targetLv) {
|
|
2183
|
+
this.log('success', `[QUEST] Level ${targetLv} verified ✓ — level unlocked!`);
|
|
2184
|
+
this._level = currentLv;
|
|
2185
|
+
} else {
|
|
2186
|
+
this.log('warn', `[QUEST] Level ${targetLv} NOT verified — still at ${currentLv}. Re-triggering quests.`);
|
|
2187
|
+
// Re-trigger quests for this level
|
|
2188
|
+
this._levelQuestDone.delete(targetLv);
|
|
2189
|
+
this._startLevelQuests(targetLv);
|
|
2190
|
+
this.tickTimeout = setTimeout(() => this.tick(), 2000);
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
} catch (e) {
|
|
2194
|
+
this.log('warn', `[QUEST] Level verification failed: ${e.message} — continuing anyway`);
|
|
2195
|
+
}
|
|
2196
|
+
this._verifyLevelUnlock = null;
|
|
2197
|
+
this.setStatus('idle');
|
|
2198
|
+
this.tickTimeout = setTimeout(() => this.tick(), 2000);
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2165
2202
|
// BLOCK: quest mode active — run quests only, no normal grinding
|
|
2166
2203
|
if (this._levelQuestActive && this._levelQuestQueue.length > 0) {
|
|
2167
2204
|
const quest = this._levelQuestQueue[0];
|
|
@@ -2169,15 +2206,26 @@ class AccountWorker {
|
|
|
2169
2206
|
const prefix = this.account.use_slash ? '/' : 'pls';
|
|
2170
2207
|
this.setStatus(`[L${quest.level}] QUEST ${quest.cmd} (${quest.times}x left)`);
|
|
2171
2208
|
this.lastStatus = `[L${quest.level}] ${quest.cmd}`;
|
|
2209
|
+
this._commandRunning = true;
|
|
2210
|
+
this.busy = true; // also set busy so grinding tick path blocks while quest runs
|
|
2172
2211
|
await this.runCommand(quest.cmd, prefix);
|
|
2212
|
+
this._commandRunning = false;
|
|
2213
|
+
this.busy = false;
|
|
2173
2214
|
this._questBetOverride = null;
|
|
2174
2215
|
quest.times--;
|
|
2175
2216
|
if (quest.times <= 0) this._levelQuestQueue.shift();
|
|
2176
2217
|
if (this._levelQuestQueue.length === 0) {
|
|
2177
2218
|
this._levelQuestActive = false;
|
|
2178
2219
|
this._levelQuestDone.add(quest.level);
|
|
2179
|
-
|
|
2180
|
-
this.
|
|
2220
|
+
const justCompletedLevel = quest.level;
|
|
2221
|
+
this.log('success', `[QUEST] Level ${justCompletedLevel} quests DONE — verifying unlock...`);
|
|
2222
|
+
// Null out commandQueue so buildCommandQueue picks only unlocked commands
|
|
2223
|
+
this.commandQueue = null;
|
|
2224
|
+
this.setStatus('verifying level...');
|
|
2225
|
+
// Wait a moment for Dank Memer to process the unlock, then verify level
|
|
2226
|
+
this._verifyLevelUnlock = justCompletedLevel;
|
|
2227
|
+
this.tickTimeout = setTimeout(() => this.tick(), 3000);
|
|
2228
|
+
return;
|
|
2181
2229
|
}
|
|
2182
2230
|
this.tickTimeout = setTimeout(() => this.tick(), 2500);
|
|
2183
2231
|
return;
|
|
@@ -2198,6 +2246,11 @@ class AccountWorker {
|
|
|
2198
2246
|
this.tickTimeout = setTimeout(() => this.tick(), 2000);
|
|
2199
2247
|
return;
|
|
2200
2248
|
}
|
|
2249
|
+
// Block grinding if a quest command is currently running (race condition guard)
|
|
2250
|
+
if (this._commandRunning) {
|
|
2251
|
+
this.tickTimeout = setTimeout(() => this.tick(), 1500);
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2201
2254
|
|
|
2202
2255
|
const now = Date.now();
|
|
2203
2256
|
|
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
|
-
|
|
538
|
-
|
|
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;
|