dankgrinder 5.0.7 → 5.9.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.
@@ -5,6 +5,7 @@
5
5
 
6
6
  const {
7
7
  LOG, c, getFullText, parseCoins, logMsg, isHoldTight, getHoldTightReason, sleep, needsItem,
8
+ isCV2, ensureCV2, stripAnsi,
8
9
  } = require('./utils');
9
10
  const { buyItem } = require('./shop');
10
11
 
@@ -33,13 +34,19 @@ async function runDig({ channel, waitForDankMemer, client }) {
33
34
  return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
34
35
  }
35
36
 
37
+ if (isCV2(response)) await ensureCV2(response);
36
38
  logMsg(response, 'dig');
37
39
  const text = getFullText(response);
38
- const textLower = text.toLowerCase();
40
+ const cleanText = stripAnsi(text).replace(/\s+/g, ' ').trim();
41
+ const textLower = cleanText.toLowerCase();
42
+ const missing = needsItem(cleanText);
43
+
44
+ const shovelMissing = missing === 'shovel'
45
+ || /(?:don't have|do not have|need|needs|requires?|missing|must have|you need|lack)\s+(?:a\s+)?shovel/.test(textLower)
46
+ || (textLower.includes('following items') && textLower.includes('shovel'));
39
47
 
40
48
  // Check if we need a shovel
41
- if (textLower.includes("don't have") && textLower.includes('shovel') ||
42
- textLower.includes('need') && textLower.includes('shovel')) {
49
+ if (shovelMissing) {
43
50
  LOG.warn('[dig] No shovel! Attempting to buy...');
44
51
 
45
52
  const bought = await buyItem({
@@ -57,6 +64,7 @@ async function runDig({ channel, waitForDankMemer, client }) {
57
64
  await channel.send('pls dig');
58
65
  const r2 = await waitForDankMemer(10000);
59
66
  if (r2) {
67
+ if (isCV2(r2)) await ensureCV2(r2);
60
68
  logMsg(r2, 'dig-retry');
61
69
  const t2 = getFullText(r2);
62
70
  const coins = parseCoins(t2);
@@ -64,19 +72,19 @@ async function runDig({ channel, waitForDankMemer, client }) {
64
72
  LOG.coin(`[dig] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
65
73
  return { result: `dig → +⏣ ${coins.toLocaleString()}`, coins };
66
74
  }
67
- return { result: t2.substring(0, 60), coins: 0 };
75
+ return { result: stripAnsi(t2).replace(/\s+/g, ' ').trim().substring(0, 60), coins: 0 };
68
76
  }
69
77
  }
70
78
  return { result: 'need shovel (buy failed)', coins: 0 };
71
79
  }
72
80
 
73
- const coins = parseCoins(text);
81
+ const coins = parseCoins(cleanText);
74
82
  if (coins > 0) {
75
83
  LOG.coin(`[dig] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
76
84
  return { result: `dig → +⏣ ${coins.toLocaleString()}`, coins };
77
85
  }
78
86
 
79
- return { result: text.substring(0, 60) || 'done', coins: 0 };
87
+ return { result: cleanText.substring(0, 60) || 'done', coins: 0 };
80
88
  }
81
89
 
82
90
  module.exports = { runDig };
@@ -5,6 +5,7 @@
5
5
 
6
6
  const {
7
7
  LOG, c, getFullText, parseCoins, logMsg, isHoldTight, getHoldTightReason, sleep, needsItem,
8
+ isCV2, ensureCV2, stripAnsi,
8
9
  } = require('./utils');
9
10
  const { buyItem } = require('./shop');
10
11
 
@@ -33,53 +34,59 @@ async function runHunt({ channel, waitForDankMemer, client }) {
33
34
  return { result: `hold tight (${reason || 'unknown'})`, coins: 0, needsRifle: false, holdTightReason: reason };
34
35
  }
35
36
 
37
+ if (isCV2(response)) await ensureCV2(response);
36
38
  logMsg(response, 'hunt');
37
39
  const text = getFullText(response);
38
- const textLower = text.toLowerCase();
40
+ const cleanText = stripAnsi(text).replace(/\s+/g, ' ').trim();
41
+ const textLower = cleanText.toLowerCase();
42
+ const missing = needsItem(cleanText);
43
+
44
+ const rifleMissing = missing === 'hunting rifle'
45
+ || /(?:don't have|do not have|need|needs|requires?|missing|must have|you need|lack)\s+(?:a\s+)?(?:hunting\s+)?rifle/.test(textLower)
46
+ || ((textLower.includes('following items') || textLower.includes('missing items')) && textLower.includes('rifle'));
39
47
 
40
48
  // Check if we need a rifle
41
- if (textLower.includes("don't have") || textLower.includes('need a') || textLower.includes('hunting rifle')) {
42
- if (textLower.includes('rifle') || textLower.includes('hunt')) {
43
- LOG.warn('[hunt] No rifle! Attempting to buy...');
49
+ if (rifleMissing) {
50
+ LOG.warn('[hunt] No rifle! Attempting to buy...');
44
51
 
45
- const bought = await buyItem({
46
- channel, waitForDankMemer, client,
47
- itemName: 'Hunting Rifle',
48
- quantity: 1,
49
- });
52
+ const bought = await buyItem({
53
+ channel, waitForDankMemer, client,
54
+ itemName: 'Hunting Rifle',
55
+ quantity: 1,
56
+ });
50
57
 
51
- if (bought) {
52
- LOG.success('[hunt] Rifle purchased! Re-running hunt...');
53
- // Drain stale shop messages
54
- while (await waitForDankMemer(1500)) { /* drain */ }
55
- await sleep(1000);
58
+ if (bought) {
59
+ LOG.success('[hunt] Rifle purchased! Re-running hunt...');
60
+ // Drain stale shop messages
61
+ while (await waitForDankMemer(1500)) { /* drain */ }
62
+ await sleep(1000);
56
63
 
57
- await channel.send('pls hunt');
58
- const r2 = await waitForDankMemer(10000);
59
- if (r2) {
60
- logMsg(r2, 'hunt-retry');
61
- const t2 = getFullText(r2);
62
- const coins = parseCoins(t2);
63
- if (coins > 0) {
64
- LOG.coin(`[hunt] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
65
- return { result: `hunt +⏣ ${coins.toLocaleString()}`, coins, needsRifle: false };
66
- }
67
- return { result: t2.substring(0, 60), coins: 0, needsRifle: false };
64
+ await channel.send('pls hunt');
65
+ const r2 = await waitForDankMemer(10000);
66
+ if (r2) {
67
+ if (isCV2(r2)) await ensureCV2(r2);
68
+ logMsg(r2, 'hunt-retry');
69
+ const t2 = getFullText(r2);
70
+ const c2 = parseCoins(t2);
71
+ if (c2 > 0) {
72
+ LOG.coin(`[hunt] ${c.green}+⏣ ${c2.toLocaleString()}${c.reset}`);
73
+ return { result: `hunt → +⏣ ${c2.toLocaleString()}`, coins: c2, needsRifle: false };
68
74
  }
69
- return { result: 'no response after rifle buy', coins: 0, needsRifle: false };
75
+ return { result: stripAnsi(t2).replace(/\s+/g, ' ').trim().substring(0, 60), coins: 0, needsRifle: false };
70
76
  }
71
-
72
- return { result: 'need rifle (buy failed)', coins: 0, needsRifle: true };
77
+ return { result: 'no response after rifle buy', coins: 0, needsRifle: false };
73
78
  }
79
+
80
+ return { result: 'need rifle (buy failed)', coins: 0, needsRifle: true };
74
81
  }
75
82
 
76
- const coins = parseCoins(text);
83
+ const coins = parseCoins(cleanText);
77
84
  if (coins > 0) {
78
85
  LOG.coin(`[hunt] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
79
86
  return { result: `hunt → +⏣ ${coins.toLocaleString()}`, coins, needsRifle: false };
80
87
  }
81
88
 
82
- return { result: text.substring(0, 60) || 'done', coins: 0, needsRifle: false };
89
+ return { result: cleanText.substring(0, 60) || 'done', coins: 0, needsRifle: false };
83
90
  }
84
91
 
85
92
  module.exports = { runHunt };
@@ -512,7 +512,7 @@ async function clickCV2Button(msg, customId) {
512
512
  // This is the same algorithm used by `fgrep` for multi-pattern search.
513
513
  const itemDetector = new AhoCorasick();
514
514
  const ITEM_PATTERN_MAP = [
515
- { item: 'shovel', patterns: ["don't have a shovel", 'need a shovel', 'you need a shovel'] },
515
+ { item: 'shovel', patterns: ["don't have a shovel", "don't own a shovel", 'need a shovel', 'you need a shovel', 'requires a shovel', 'missing shovel'] },
516
516
  { item: 'fishing pole', patterns: ["don't have a fishing", 'need a fishing', 'you need a fishing pole'] },
517
517
  { item: 'hunting rifle', patterns: ["don't have a hunting rifle", 'need a hunting rifle', 'you need a rifle'] },
518
518
  { item: 'adventure ticket', patterns: ["don't have a ticket", "don't have an adventure", 'need a ticket', 'need an adventure ticket'] },
@@ -523,6 +523,8 @@ for (const { item, patterns } of ITEM_PATTERN_MAP) {
523
523
  for (const p of patterns) itemDetector.addPattern(p, item);
524
524
  }
525
525
  itemDetector.addPattern('need following items', '__multi__');
526
+ itemDetector.addPattern('need the following items', '__multi__');
527
+ itemDetector.addPattern('you need the following items', '__multi__');
526
528
  itemDetector.addPattern('missing items', '__multi__');
527
529
  itemDetector.build();
528
530
 
@@ -531,6 +533,10 @@ function needsItem(text) {
531
533
  if (!match) return null;
532
534
  if (match === '__multi__') {
533
535
  const lower = text.toLowerCase();
536
+ if (lower.includes('shovel')) return 'shovel';
537
+ if (lower.includes('fishing pole') || lower.includes('fishing')) return 'fishing pole';
538
+ if (lower.includes('hunting rifle') || lower.includes('rifle')) return 'hunting rifle';
539
+ if (lower.includes('adventure ticket') || lower.includes('ticket')) return 'adventure ticket';
534
540
  if (lower.includes('keyboard')) return 'keyboard';
535
541
  if (lower.includes('mouse')) return 'mouse';
536
542
  return null;
package/lib/grinder.js CHANGED
@@ -1998,10 +1998,13 @@ class AccountWorker {
1998
1998
  this.setStatus(`pls ${item.cmd}`);
1999
1999
 
2000
2000
  // Report "running" to dashboard
2001
+ const nextItemRun = this.commandQueue?.peek?.();
2001
2002
  reportCommandFeed(this.account.id, this.username, {
2002
2003
  phase: 'running',
2003
2004
  command: item.cmd,
2004
2005
  started_at: Date.now(),
2006
+ next_command: nextItemRun?.cmd || null,
2007
+ next_run_at: nextItemRun?.nextRunAt || null,
2005
2008
  });
2006
2009
 
2007
2010
  const beforeCoins = this.stats.coins;
@@ -2196,11 +2199,12 @@ class AccountWorker {
2196
2199
  return new Promise((resolve) => {
2197
2200
  this.client.on('ready', async () => {
2198
2201
  this.username = this.client.user.tag || this.username;
2202
+ this.avatarUrl = this.client.user.displayAvatarURL?.({ format: 'png', dynamic: true, size: 128 }) || null;
2199
2203
  try {
2200
2204
  await fetch(`${API_URL}/api/grinder/status`, {
2201
2205
  method: 'POST',
2202
2206
  headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
2203
- body: JSON.stringify({ account_id: this.account.id, discord_username: this.username }),
2207
+ body: JSON.stringify({ account_id: this.account.id, discord_username: this.username, avatar_url: this.avatarUrl }),
2204
2208
  });
2205
2209
  } catch { /* silent */ }
2206
2210
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "5.0.7",
3
+ "version": "5.9.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"