dankgrinder 6.37.0 → 6.42.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/rawLogger.js CHANGED
@@ -151,24 +151,26 @@ function detectCommand(d) {
151
151
  // Non-gambling CV2
152
152
  if (cv2Text.includes('fishing') || cv2Text.includes('fisherfolk')) return 'fish';
153
153
  if (cv2Text.includes('deposit') || cv2Text.includes('bank account')) return 'deposit';
154
- if (cv2Text.includes('begging') || cv2Text.includes('imagine begging')) return 'beg';
155
- if (cv2Text.includes('hunting') || cv2Text.includes('went hunting') || cv2Text.includes('hunting rifle') || cv2Text.includes('your aim was so bad') || cv2Text.includes('animals laughed') || cv2Text.includes('animals attacked') || cv2Text.includes('barely escaped') || cv2Text.includes('fell asleep in a tree') || cv2Text.includes('caught nothing') || cv2Text.includes('brought back literally nothing') || cv2Text.includes('rifle broke')) return 'hunt';
156
- if (cv2Text.includes('digging') || cv2Text.includes('found nothing while') || cv2Text.includes('you dig') || cv2Text.includes('dug in the dirt') || cv2Text.includes('brought back') && (cv2Text.includes('ant') || cv2Text.includes('worm') || cv2Text.includes('stickbug') || cv2Text.includes('ladybug'))) return 'dig';
154
+ if (cv2Text.includes('begging') || cv2Text.includes('imagine begging') || cv2Text.includes('wumpus gives you') || (cv2Text.includes('you received') && !cv2Text.includes('search') && !cv2Text.includes('hunt') && !cv2Text.includes('dig'))) return 'beg';
155
+ if (cv2Text.includes('hunting') || cv2Text.includes('went hunting') || cv2Text.includes('hunting rifle') || cv2Text.includes('your aim was so bad') || cv2Text.includes('animals laughed') || cv2Text.includes('animals attacked') || cv2Text.includes('barely escaped') || cv2Text.includes('fell asleep in a tree') || cv2Text.includes('caught nothing') || cv2Text.includes('brought back literally nothing') || cv2Text.includes('rifle broke') || (cv2Text.includes('brought back') && (cv2Text.includes('deer') || cv2Text.includes('wolf') || cv2Text.includes('bear') || cv2Text.includes('boar') || cv2Text.includes('lion') || cv2Text.includes('rabbit') || cv2Text.includes('squirrel') || cv2Text.includes('moose') || cv2Text.includes('bird') || cv2Text.includes('elk') || cv2Text.includes('hunting') || cv2Text.includes('capybara')))) return 'hunt';
156
+ if (cv2Text.includes('digging') || cv2Text.includes('found nothing while') || cv2Text.includes('you dig') || cv2Text.includes('dug in the dirt') || (cv2Text.includes('brought back') && (cv2Text.includes('ant') || cv2Text.includes('worm') || cv2Text.includes('stickbug') || cv2Text.includes('ladybug')))) return 'dig';
157
157
  if (cv2Text.includes('great work') || cv2Text.includes('for your shift') || cv2Text.includes('working as') || cv2Text.includes('work shift') || cv2Text.includes('what color was') || cv2Text.includes('remember words order') || cv2Text.includes('remember the colors') || cv2Text.includes('remember the emojis') || cv2Text.includes('what word was repeated') || cv2Text.includes('unscramble') || cv2Text.includes('remember the number') || cv2Text.includes('click the buttons in correct order') || cv2Text.includes('babysitter') || cv2Text.includes('click the matching')) return 'work';
158
+ // Quest completions (before generic daily/weekly)
159
+ if (cv2Text.includes('locations') && (cv2Text.includes('inventory locations') || cv2Text.includes('found locations') || cv2Text.includes('completed your') || cv2Text.includes('locations remaining'))) return 'search';
158
160
  if (cv2Text.includes('weekly')) return 'weekly';
159
161
  if (cv2Text.includes('daily')) return 'daily';
160
- if (cv2Text.includes('inventory')) return 'inventory';
162
+ // Only match inventory if it looks like an actual inventory display (header format or sellable indicator)
163
+ if (cv2Text.includes('inventory') && (cv2Text.includes('###') || cv2Text.includes('sellable') || cv2Text.includes('<:reply:'))) return 'inventory';
161
164
  if (cv2Text.includes('profile') || cv2Text.includes('level:')) return 'profile';
162
165
  if (cv2Text.includes('balances') && cv2Text.includes('global rank')) return 'balance';
163
166
 
164
167
  // Check content text (plain message content)
165
168
  const contentText = (d.content || '').toLowerCase();
166
169
  if (contentText.includes('balances') && contentText.includes('global rank')) return 'balance';
167
- if (contentText.includes('your aim was so bad') || contentText.includes('animals laughed')) return 'hunt';
168
- if (contentText.includes('imagine going into the woods')) return 'hunt';
170
+ if (contentText.includes('your aim was so bad') || contentText.includes('animals laughed') || contentText.includes('imagine going into the woods')) return 'hunt';
169
171
  if (contentText.includes('you ran an ad for') && contentText.includes('received')) return 'stream';
170
172
  if (contentText.includes('you can\'t interact with your stream')) return 'stream';
171
- if (contentText.includes('you dug in the dirt') || contentText.includes('found nothing while digging')) return 'dig';
173
+ if (contentText.includes('you dug in the dirt') || (contentText.includes('found nothing while digging') && (contentText.includes('dug') || contentText.includes('dirt')))) return 'dig';
172
174
 
173
175
  // Check embed text
174
176
  const embedText = extractEmbedText(d.embeds).toLowerCase();
@@ -197,8 +199,8 @@ function detectCommand(d) {
197
199
  return 'search';
198
200
  }
199
201
  // Hunt / dig
200
- if (embedText.includes('hunting') || embedText.includes('came back with') || embedText.includes('hunting rifle') || embedText.includes('dragon\'s fireball') || embedText.includes('dodge the') || embedText.includes('went hunting') || embedText.includes('your aim was so bad') || embedText.includes('animals laughed') || embedText.includes('animals attacked') || embedText.includes('barely escaped') || embedText.includes('fell asleep in a tree') || embedText.includes('caught nothing') || embedText.includes('brought back literally nothing') || embedText.includes('rifle broke') || embedText.includes('imagine going into the woods')) return 'hunt';
201
- if (embedText.includes('digging') || embedText.includes('you dig') || embedText.includes('dug in the dirt') || embedText.includes('found nothing while') || embedText.includes('what are the odds lol') || embedText.includes('brought back') && (embedText.includes('ant') || embedText.includes('worm') || embedText.includes('stickbug') || embedText.includes('ladybug'))) return 'dig';
202
+ if (embedText.includes('hunting') || embedText.includes('came back with') || embedText.includes('hunting rifle') || embedText.includes('dragon\'s fireball') || embedText.includes('dodge the') || embedText.includes('went hunting') || embedText.includes('your aim was so bad') || embedText.includes('animals laughed') || embedText.includes('animals attacked') || embedText.includes('barely escaped') || embedText.includes('fell asleep in a tree') || embedText.includes('caught nothing') || embedText.includes('brought back literally nothing') || embedText.includes('rifle broke') || embedText.includes('imagine going into the woods') || (embedText.includes('brought back') && (embedText.includes('deer') || embedText.includes('wolf') || embedText.includes('bear') || embedText.includes('boar') || embedText.includes('lion') || embedText.includes('rabbit') || embedText.includes('squirrel') || embedText.includes('moose') || embedText.includes('bird') || embedText.includes('elk') || embedText.includes('capybara')))) return 'hunt';
203
+ if (embedText.includes('digging') || embedText.includes('you dig') || embedText.includes('dug in the dirt') || embedText.includes('found nothing while') || embedText.includes('what are the odds lol') || (embedText.includes('brought back') && (embedText.includes('ant') || embedText.includes('worm') || embedText.includes('stickbug') || embedText.includes('ladybug')))) return 'dig';
202
204
  // Work — match both minigame prompt AND completion
203
205
  if (embedText.includes('work') && (embedText.includes('shift') || embedText.includes('mini-game') || embedText.includes('color') || embedText.includes('what color') || embedText.includes('babysitter') || embedText.includes('great work') || embedText.includes('for your shift'))) return 'work';
204
206
  if (embedText.includes('you were given') && embedText.includes('shift')) return 'work';
@@ -207,7 +209,7 @@ function detectCommand(d) {
207
209
  // Postmemes
208
210
  if (embedText.includes('pick a meme') || embedText.includes('meme posting')) return 'postmemes';
209
211
  // Stream
210
- if (embedText.includes('stream manager') || embedText.includes('go live') || embedText.includes('what game do you want to stream') || embedText.includes('you ran an ad for') || embedText.includes('you received') && embedText.includes('from your sponsors') || embedText.includes('### chat') && embedText.includes('hasanbabi')) return 'stream';
212
+ if (embedText.includes('stream manager') || embedText.includes('go live') || embedText.includes('what game do you want to stream') || embedText.includes('you ran an ad for') || (embedText.includes('you received') && embedText.includes('from your sponsors')) || (embedText.includes('### chat') && embedText.includes('hasanbabi'))) return 'stream';
211
213
  if (embedText.includes('you can\'t interact with your stream') || embedText.includes('stream can last')) return 'stream';
212
214
  // Deposit
213
215
  if (embedText.includes('deposited') && embedText.includes('bank balance')) return 'deposit';
@@ -228,7 +230,7 @@ function detectCommand(d) {
228
230
  // Farm
229
231
  if (embedText.includes('farm') && (embedText.includes('harvest') || embedText.includes('plant') || embedText.includes('hoe') || embedText.includes('water'))) return 'farm';
230
232
  // Beg
231
- if (embedText.includes('begging')) return 'beg';
233
+ if (embedText.includes('begging') || embedText.includes('wumpus gives you') || embedText.includes('life saver') || embedText.includes('lifesaver')) return 'beg';
232
234
  // Daily/weekly quest
233
235
  if (embedText.includes('daily quest')) return 'daily';
234
236
  // Fish
package/lib/structures.js CHANGED
@@ -443,6 +443,25 @@ class MinHeap {
443
443
 
444
444
  peek() { return this.heap[0]; }
445
445
 
446
+ /**
447
+ * Remove a specific node from the heap by reference.
448
+ * Used by CommandScheduler.unschedule() to cancel a pending scheduled command.
449
+ * O(n) — linear scan since we store node references in a Map.
450
+ */
451
+ remove(node) {
452
+ const idx = this.heap.indexOf(node);
453
+ if (idx === -1) return false;
454
+ const last = this.heap.pop();
455
+ if (idx < this.heap.length) {
456
+ this.heap[idx] = last;
457
+ // Bubble up or sink down depending on where the removed item was
458
+ if (this._bubbleUp(idx) === idx) {
459
+ this._sinkDown(idx);
460
+ }
461
+ }
462
+ return true;
463
+ }
464
+
446
465
  _bubbleUp(i) {
447
466
  while (i > 0) {
448
467
  const parent = (i - 1) >>> 1;
@@ -680,31 +699,6 @@ class AsyncBatchQueue {
680
699
  destroy() { if (this._timer) clearTimeout(this._timer); this.queue.length = 0; }
681
700
  }
682
701
 
683
- // ═══════════════════════════════════════════════════════════════
684
- // JitterBackoff – Decorrelated jitter (AWS-style), O(1)
685
- // Standard exponential backoff causes "thundering herd" when 10K
686
- // accounts retry simultaneously. Decorrelated jitter spreads them:
687
- // sleep = min(cap, random_between(base, sleep_prev * 3))
688
- // This is the recommended strategy from AWS Architecture Blog.
689
- // ═══════════════════════════════════════════════════════════════
690
- class JitterBackoff {
691
- constructor(baseMs = 1000, capMs = 30000) {
692
- this.baseMs = baseMs;
693
- this.capMs = capMs;
694
- this._sleep = baseMs;
695
- this.attempt = 0;
696
- }
697
-
698
- next() {
699
- this._sleep = Math.min(this.capMs, this.baseMs + Math.random() * (this._sleep * 3 - this.baseMs));
700
- this.attempt++;
701
- return this._sleep;
702
- }
703
-
704
- reset() { this._sleep = this.baseMs; this.attempt = 0; }
705
- get current() { return this._sleep; }
706
- }
707
-
708
702
  module.exports = {
709
703
  BloomFilter,
710
704
  LRUCache,
@@ -721,5 +715,4 @@ module.exports = {
721
715
  ObjectPool,
722
716
  TimerWheel,
723
717
  AsyncBatchQueue,
724
- JitterBackoff,
725
718
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "6.37.0",
3
+ "version": "6.42.0",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"
@@ -1,83 +0,0 @@
1
- /**
2
- * Scratch command handler.
3
- * Send "pls scratch", click through scratch card buttons.
4
- * Requires level 25 — checks profile level before running.
5
- */
6
-
7
- const {
8
- LOG, c, getFullText, parseCoins, getAllButtons, safeClickButton,
9
- logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay,
10
- } = require('./utils');
11
- const { meetsLevelRequirement } = require('./profile');
12
-
13
- /**
14
- * @param {object} opts
15
- * @param {object} opts.channel
16
- * @param {function} opts.waitForDankMemer
17
- * @param {string} [opts.accountId]
18
- * @param {object} [opts.redis]
19
- * @returns {Promise<{result: string, coins: number}>}
20
- */
21
- async function runScratch({ channel, waitForDankMemer, accountId, redis }) {
22
- // Check level 25 requirement before wasting a command
23
- const canRun = await meetsLevelRequirement({ channel, waitForDankMemer, accountId, redis }, 25);
24
- if (!canRun) {
25
- LOG.warn(`[scratch] Skipped — need level 25`);
26
- return { result: 'skipped (need level 25)', coins: 0, skipReason: 'level' };
27
- }
28
- LOG.cmd(`${c.white}${c.bold}pls scratch${c.reset}`);
29
-
30
- await channel.send('pls scratch');
31
- const response = await waitForDankMemer(10000);
32
-
33
- if (!response) {
34
- LOG.warn('[scratch] No response');
35
- return { result: 'no response', coins: 0 };
36
- }
37
-
38
- if (isHoldTight(response)) {
39
- const reason = getHoldTightReason(response);
40
- LOG.warn(`[scratch] Hold Tight${reason ? ` (reason: /${reason})` : ''} — waiting 30s`);
41
- await sleep(30000);
42
- return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
43
- }
44
-
45
- logMsg(response, 'scratch');
46
- const buttons = getAllButtons(response);
47
-
48
- if (buttons.length === 0) {
49
- const text = getFullText(response);
50
- const coins = parseCoins(text);
51
- if (coins > 0) {
52
- LOG.coin(`[scratch] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
53
- return { result: `scratch → +⏣ ${coins.toLocaleString()}`, coins };
54
- }
55
- return { result: text.substring(0, 60) || 'done', coins: 0 };
56
- }
57
-
58
- // Click through all scratch card cells
59
- let lastResponse = response;
60
- let clickCount = 0;
61
- for (let i = 0; i < Math.min(buttons.length, 9); i++) {
62
- const btn = buttons[i];
63
- if (btn && !btn.disabled) {
64
- await humanDelay(300, 700);
65
- try {
66
- const followUp = await safeClickButton(lastResponse, btn);
67
- if (followUp) { lastResponse = followUp; clickCount++; }
68
- } catch { break; }
69
- }
70
- }
71
-
72
- const finalText = getFullText(lastResponse);
73
- const coins = parseCoins(finalText);
74
-
75
- if (coins > 0) {
76
- LOG.coin(`[scratch] ${clickCount} clicks → ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
77
- return { result: `scratch (${clickCount} clicks) → +⏣ ${coins.toLocaleString()}`, coins };
78
- }
79
-
80
- return { result: `scratch done (${clickCount} clicks)`, coins: 0 };
81
- }
82
-
83
- module.exports = { runScratch };