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/commands/beg.js +26 -4
- package/lib/commands/farm.js +36 -10
- package/lib/commands/fishVision.js +27 -1
- package/lib/commands/index.js +2 -3
- package/lib/commands/profile.js +200 -52
- package/lib/commands/stream.js +1 -1
- package/lib/commands/utils.js +4 -2
- package/lib/commands/work.js +1 -1
- package/lib/grinder.js +390 -142
- package/lib/rawLogger.js +13 -11
- package/lib/structures.js +19 -26
- package/package.json +1 -1
- package/lib/commands/scratch.js +0 -83
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
|
|
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
package/lib/commands/scratch.js
DELETED
|
@@ -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 };
|