dankgrinder 3.0.0 → 4.1.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.
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Blackjack command handler.
3
+ * Send "pls bj <bet>", play hit/stand based on card total.
4
+ */
5
+
6
+ const {
7
+ LOG, c, getFullText, parseCoins, getAllButtons, safeClickButton,
8
+ logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay,
9
+ } = require('./utils');
10
+
11
+ /**
12
+ * @param {object} opts
13
+ * @param {object} opts.channel
14
+ * @param {function} opts.waitForDankMemer
15
+ * @param {number} [opts.betAmount=1000]
16
+ * @returns {Promise<{result: string, coins: number}>}
17
+ */
18
+ async function runBlackjack({ channel, waitForDankMemer, betAmount = 1000 }) {
19
+ const cmd = `pls bj ${betAmount}`;
20
+ LOG.cmd(`${c.white}${c.bold}${cmd}${c.reset}`);
21
+
22
+ await channel.send(cmd);
23
+ let current = await waitForDankMemer(10000);
24
+
25
+ if (!current) {
26
+ LOG.warn('[bj] No response');
27
+ return { result: 'no response', coins: 0 };
28
+ }
29
+
30
+ if (isHoldTight(current)) {
31
+ const reason = getHoldTightReason(current);
32
+ LOG.warn(`[bj] Hold Tight${reason ? ` (reason: /${reason})` : ''} — waiting 30s`);
33
+ await sleep(30000);
34
+ return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
35
+ }
36
+
37
+ logMsg(current, 'bj');
38
+
39
+ const MAX_ROUNDS = 10;
40
+ for (let i = 0; i < MAX_ROUNDS; i++) {
41
+ const text = getFullText(current);
42
+ const buttons = getAllButtons(current);
43
+ if (buttons.length === 0 || buttons.every(b => b.disabled)) break;
44
+
45
+ // Parse player total
46
+ const totalMatch = text.match(/total[:\s]*\**(\d+)\**/i) || text.match(/value[:\s]*\**(\d+)\**/i);
47
+ const playerTotal = totalMatch ? parseInt(totalMatch[1]) : 0;
48
+
49
+ let targetBtn;
50
+ if (playerTotal >= 17 || playerTotal === 0) {
51
+ targetBtn = buttons.find(b => (b.label || '').toLowerCase().includes('stand')) || buttons[1];
52
+ } else {
53
+ targetBtn = buttons.find(b => (b.label || '').toLowerCase().includes('hit')) || buttons[0];
54
+ }
55
+
56
+ if (!targetBtn || targetBtn.disabled) break;
57
+
58
+ LOG.info(`[bj] Total: ${playerTotal} → ${targetBtn.label}`);
59
+ await humanDelay(500, 1200);
60
+
61
+ try {
62
+ const followUp = await safeClickButton(current, targetBtn);
63
+ if (followUp) {
64
+ current = followUp;
65
+ logMsg(current, `bj-round-${i}`);
66
+ const fButtons = getAllButtons(current);
67
+ if (fButtons.length === 0 || fButtons.every(b => b.disabled)) break;
68
+ } else break;
69
+ } catch { break; }
70
+ }
71
+
72
+ const finalText = getFullText(current);
73
+ const coins = parseCoins(finalText);
74
+ const lower = finalText.toLowerCase();
75
+
76
+ if (coins > 0) {
77
+ LOG.coin(`[bj] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
78
+ return { result: `bj → +⏣ ${coins.toLocaleString()}`, coins };
79
+ }
80
+ if (lower.includes('won')) return { result: `bj → ${c.green}won${c.reset}`, coins: 0 };
81
+ if (lower.includes('lost') || lower.includes('bust')) return { result: `bj → ${c.red}lost${c.reset}`, coins: 0 };
82
+ return { result: 'bj done', coins: 0 };
83
+ }
84
+
85
+ module.exports = { runBlackjack };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Crime command handler.
3
+ * Send "pls crime", pick a safe option, click it, parse coins.
4
+ */
5
+
6
+ const {
7
+ LOG, c, getFullText, parseCoins, getAllButtons, safeClickButton,
8
+ logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay,
9
+ } = require('./utils');
10
+
11
+ const SAFE_CRIME_OPTIONS = [
12
+ 'tax evasion', 'fraud', 'cybercrime', 'hacking', 'identity theft',
13
+ 'money laundering', 'tax fraud', 'insurance fraud', 'scam',
14
+ ];
15
+
16
+ function pickSafeButton(buttons, customSafe) {
17
+ if (!buttons || buttons.length === 0) return null;
18
+ if (customSafe && customSafe.length > 0) {
19
+ for (const btn of buttons) {
20
+ const label = (btn.label || '').toLowerCase();
21
+ if (customSafe.some(s => label.includes(s.toLowerCase()))) return btn;
22
+ }
23
+ }
24
+ for (const btn of buttons) {
25
+ const label = (btn.label || '').toLowerCase();
26
+ if (SAFE_CRIME_OPTIONS.some(s => label.includes(s))) return btn;
27
+ }
28
+ const clickable = buttons.filter(b => !b.disabled);
29
+ return clickable.length > 0 ? clickable[Math.floor(Math.random() * clickable.length)] : null;
30
+ }
31
+
32
+ /**
33
+ * @param {object} opts
34
+ * @param {object} opts.channel
35
+ * @param {function} opts.waitForDankMemer
36
+ * @param {string[]} [opts.safeAnswers]
37
+ * @returns {Promise<{result: string, coins: number}>}
38
+ */
39
+ async function runCrime({ channel, waitForDankMemer, safeAnswers }) {
40
+ LOG.cmd(`${c.white}${c.bold}pls crime${c.reset}`);
41
+
42
+ await channel.send('pls crime');
43
+ const response = await waitForDankMemer(10000);
44
+
45
+ if (!response) {
46
+ LOG.warn('[crime] No response');
47
+ return { result: 'no response', coins: 0 };
48
+ }
49
+
50
+ if (isHoldTight(response)) {
51
+ const reason = getHoldTightReason(response);
52
+ LOG.warn(`[crime] Hold Tight${reason ? ` (reason: /${reason})` : ''} — waiting 30s`);
53
+ await sleep(30000);
54
+ return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
55
+ }
56
+
57
+ logMsg(response, 'crime');
58
+ const buttons = getAllButtons(response);
59
+
60
+ if (buttons.length === 0) {
61
+ const text = getFullText(response);
62
+ LOG.info(`[crime] No buttons: ${text.substring(0, 80)}`);
63
+ return { result: text.substring(0, 60), coins: 0 };
64
+ }
65
+
66
+ const btn = pickSafeButton(buttons, safeAnswers);
67
+ if (!btn) {
68
+ LOG.warn('[crime] No safe button found');
69
+ return { result: 'no safe option', coins: 0 };
70
+ }
71
+
72
+ LOG.info(`[crime] Picking: "${btn.label}"`);
73
+ await humanDelay();
74
+
75
+ try {
76
+ const followUp = await safeClickButton(response, btn);
77
+ if (followUp) {
78
+ logMsg(followUp, 'crime-result');
79
+ const text = getFullText(followUp);
80
+ const coins = parseCoins(text);
81
+ if (coins > 0) {
82
+ LOG.coin(`[crime] ${btn.label} → ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
83
+ return { result: `${btn.label} → +⏣ ${coins.toLocaleString()}`, coins };
84
+ }
85
+ return { result: `${btn.label} → done`, coins: 0 };
86
+ }
87
+ return { result: `clicked: ${btn.label}`, coins: 0 };
88
+ } catch (e) {
89
+ LOG.error(`[crime] Click error: ${e.message}`);
90
+ return { result: `error: ${e.message}`, coins: 0 };
91
+ }
92
+ }
93
+
94
+ module.exports = { runCrime, SAFE_CRIME_OPTIONS };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Deposit command handler.
3
+ * Send "pls dep max" to deposit all wallet coins to bank.
4
+ */
5
+
6
+ const {
7
+ LOG, c, getFullText, parseCoins, logMsg, isHoldTight, getHoldTightReason, sleep,
8
+ } = require('./utils');
9
+
10
+ /**
11
+ * @param {object} opts
12
+ * @param {object} opts.channel
13
+ * @param {function} opts.waitForDankMemer
14
+ * @returns {Promise<{result: string, coins: number}>}
15
+ */
16
+ async function runDeposit({ channel, waitForDankMemer }) {
17
+ LOG.cmd(`${c.white}${c.bold}pls dep max${c.reset}`);
18
+
19
+ await channel.send('pls dep max');
20
+ const response = await waitForDankMemer(10000);
21
+
22
+ if (!response) {
23
+ LOG.warn('[deposit] No response');
24
+ return { result: 'no response', coins: 0 };
25
+ }
26
+
27
+ if (isHoldTight(response)) {
28
+ const reason = getHoldTightReason(response);
29
+ LOG.warn(`[deposit] Hold Tight${reason ? ` (reason: /${reason})` : ''} — waiting 30s`);
30
+ await sleep(30000);
31
+ return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
32
+ }
33
+
34
+ logMsg(response, 'deposit');
35
+ const text = getFullText(response);
36
+ const coins = parseCoins(text);
37
+
38
+ if (text.toLowerCase().includes('deposited')) {
39
+ LOG.success(`[deposit] Deposited ${c.green}⏣ ${coins.toLocaleString()}${c.reset}`);
40
+ return { result: `deposited ⏣ ${coins.toLocaleString()}`, coins };
41
+ }
42
+
43
+ return { result: text.substring(0, 60) || 'deposit done', coins: 0 };
44
+ }
45
+
46
+ module.exports = { runDeposit };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Dig command handler.
3
+ * Send "pls dig", detect if shovel is missing, auto-buy if needed.
4
+ */
5
+
6
+ const {
7
+ LOG, c, getFullText, parseCoins, logMsg, isHoldTight, getHoldTightReason, sleep, needsItem,
8
+ } = require('./utils');
9
+ const { buyItem } = require('./shop');
10
+
11
+ /**
12
+ * @param {object} opts
13
+ * @param {object} opts.channel
14
+ * @param {function} opts.waitForDankMemer
15
+ * @param {object} [opts.client]
16
+ * @returns {Promise<{result: string, coins: number}>}
17
+ */
18
+ async function runDig({ channel, waitForDankMemer, client }) {
19
+ LOG.cmd(`${c.white}${c.bold}pls dig${c.reset}`);
20
+
21
+ await channel.send('pls dig');
22
+ const response = await waitForDankMemer(10000);
23
+
24
+ if (!response) {
25
+ LOG.warn('[dig] No response');
26
+ return { result: 'no response', coins: 0 };
27
+ }
28
+
29
+ if (isHoldTight(response)) {
30
+ const reason = getHoldTightReason(response);
31
+ LOG.warn(`[dig] Hold Tight${reason ? ` (reason: /${reason})` : ''} — waiting 30s`);
32
+ await sleep(30000);
33
+ return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
34
+ }
35
+
36
+ logMsg(response, 'dig');
37
+ const text = getFullText(response);
38
+ const textLower = text.toLowerCase();
39
+
40
+ // Check if we need a shovel
41
+ if (textLower.includes("don't have") && textLower.includes('shovel') ||
42
+ textLower.includes('need') && textLower.includes('shovel')) {
43
+ LOG.warn('[dig] No shovel! Attempting to buy...');
44
+
45
+ const bought = await buyItem({
46
+ channel, waitForDankMemer, client,
47
+ itemName: 'Shovel',
48
+ quantity: 3,
49
+ });
50
+
51
+ if (bought) {
52
+ LOG.success('[dig] Shovel purchased! Re-running dig...');
53
+ // Drain any stale shop messages before retrying
54
+ while (await waitForDankMemer(1500)) { /* drain */ }
55
+ await sleep(1000);
56
+
57
+ await channel.send('pls dig');
58
+ const r2 = await waitForDankMemer(10000);
59
+ if (r2) {
60
+ logMsg(r2, 'dig-retry');
61
+ const t2 = getFullText(r2);
62
+ const coins = parseCoins(t2);
63
+ if (coins > 0) {
64
+ LOG.coin(`[dig] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
65
+ return { result: `dig → +⏣ ${coins.toLocaleString()}`, coins };
66
+ }
67
+ return { result: t2.substring(0, 60), coins: 0 };
68
+ }
69
+ }
70
+ return { result: 'need shovel (buy failed)', coins: 0 };
71
+ }
72
+
73
+ const coins = parseCoins(text);
74
+ if (coins > 0) {
75
+ LOG.coin(`[dig] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
76
+ return { result: `dig → +⏣ ${coins.toLocaleString()}`, coins };
77
+ }
78
+
79
+ return { result: text.substring(0, 60) || 'done', coins: 0 };
80
+ }
81
+
82
+ module.exports = { runDig };