dankgrinder 4.8.1 → 4.9.1
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/blackjack.js +51 -32
- package/lib/commands/gamble.js +161 -58
- package/lib/commands/generic.js +7 -3
- package/lib/commands/highlow.js +2 -2
- package/lib/commands/index.js +11 -2
- package/lib/commands/inventory.js +344 -0
- package/lib/commands/shop.js +127 -201
- package/lib/commands/utils.js +144 -9
- package/lib/grinder.js +254 -71
- package/package.json +1 -1
|
@@ -5,33 +5,51 @@
|
|
|
5
5
|
|
|
6
6
|
const {
|
|
7
7
|
LOG, c, getFullText, parseCoins, getAllButtons, safeClickButton,
|
|
8
|
-
logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay,
|
|
8
|
+
logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay, ensureCV2,
|
|
9
9
|
} = require('./utils');
|
|
10
10
|
|
|
11
|
-
function parsePlayerTotal(
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
function parsePlayerTotal(msg) {
|
|
12
|
+
// Score is in embed field: ` 18 ` (backtick-wrapped number in Player field)
|
|
13
|
+
for (const embed of msg?.embeds || []) {
|
|
14
|
+
for (const field of embed.fields || []) {
|
|
15
|
+
if (field.name?.toLowerCase().includes('player')) {
|
|
16
|
+
const m = field.value.match(/`\s*(\d+)\s*`/);
|
|
17
|
+
if (m) return parseInt(m[1]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const text = typeof msg === 'string' ? msg : getFullText(msg);
|
|
22
|
+
const m = text.match(/`\s*(\d+)\s*`/);
|
|
23
|
+
return m ? parseInt(m[1]) : 0;
|
|
18
24
|
}
|
|
19
25
|
|
|
20
|
-
function parseDealerUpcard(
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
function parseDealerUpcard(msg) {
|
|
27
|
+
// Dealer field: revealed total ` 18 ` or hidden ` ? `
|
|
28
|
+
// Upcard from emoji: bjFaceKB=K(10), bjFaceAR=A(11), bjFace7R=7
|
|
29
|
+
for (const embed of msg?.embeds || []) {
|
|
30
|
+
for (const field of embed.fields || []) {
|
|
31
|
+
if (field.name?.toLowerCase().includes('dealer')) {
|
|
32
|
+
const totalMatch = field.value.match(/`\s*(\d+)\s*`/);
|
|
33
|
+
if (totalMatch) return parseInt(totalMatch[1]);
|
|
34
|
+
const faces = [...field.value.matchAll(/bjFace(\w+?):/g)];
|
|
35
|
+
for (const [, face] of faces) {
|
|
36
|
+
if (face === 'Unknown') continue;
|
|
37
|
+
const v = face.replace(/[RB]$/, '');
|
|
38
|
+
if (v === 'A') return 11;
|
|
39
|
+
if (['K', 'Q', 'J'].includes(v)) return 10;
|
|
40
|
+
const n = parseInt(v);
|
|
41
|
+
if (!isNaN(n)) return n;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
24
46
|
return 0;
|
|
25
47
|
}
|
|
26
48
|
|
|
27
49
|
function parseNetCoins(text) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return parseInt(netMatch[1].replace(/,/g, ''));
|
|
32
|
-
}
|
|
33
|
-
// Parse "Winnings: ⏣ 5,000"
|
|
34
|
-
const winMatch = text.match(/Winnings:\s*[⏣o]\s*([\d,]+)/i);
|
|
50
|
+
const netMatch = text.match(/Net:\s*\*{0,2}\s*[⏣]\s*\*{0,2}([+-]?[\d,]+)/i);
|
|
51
|
+
if (netMatch) return parseInt(netMatch[1].replace(/,/g, ''));
|
|
52
|
+
const winMatch = text.match(/Winnings:\s*\*{0,2}\s*[⏣]\s*\*{0,2}([\d,]+)/i);
|
|
35
53
|
if (winMatch) return parseInt(winMatch[1].replace(/,/g, ''));
|
|
36
54
|
return 0;
|
|
37
55
|
}
|
|
@@ -63,6 +81,8 @@ async function runBlackjack({ channel, waitForDankMemer, betAmount = 5000 }) {
|
|
|
63
81
|
return { result: 'no response', coins: 0 };
|
|
64
82
|
}
|
|
65
83
|
|
|
84
|
+
await ensureCV2(current);
|
|
85
|
+
|
|
66
86
|
if (isHoldTight(current)) {
|
|
67
87
|
const reason = getHoldTightReason(current);
|
|
68
88
|
LOG.warn(`[bj] Hold Tight${reason ? ` (reason: /${reason})` : ''} — waiting 30s`);
|
|
@@ -74,25 +94,26 @@ async function runBlackjack({ channel, waitForDankMemer, betAmount = 5000 }) {
|
|
|
74
94
|
|
|
75
95
|
const MAX_ROUNDS = 10;
|
|
76
96
|
for (let i = 0; i < MAX_ROUNDS; i++) {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
97
|
+
const buttons = getAllButtons(current).filter(b => b.label && b.label !== 'null');
|
|
98
|
+
const hitBtn = buttons.find(b => !b.disabled && (b.label || '').toLowerCase().includes('hit'));
|
|
99
|
+
const standBtn = buttons.find(b => !b.disabled && (b.label || '').toLowerCase().includes('stand'));
|
|
100
|
+
if (!hitBtn && !standBtn) break;
|
|
80
101
|
|
|
81
|
-
const playerTotal = parsePlayerTotal(
|
|
82
|
-
const
|
|
102
|
+
const playerTotal = parsePlayerTotal(current);
|
|
103
|
+
const dealerUpcard = parseDealerUpcard(current);
|
|
83
104
|
|
|
84
105
|
let targetBtn;
|
|
85
106
|
if (playerTotal === 0) {
|
|
86
|
-
targetBtn =
|
|
87
|
-
} else if (shouldHit(playerTotal,
|
|
88
|
-
targetBtn =
|
|
107
|
+
targetBtn = standBtn || hitBtn;
|
|
108
|
+
} else if (shouldHit(playerTotal, dealerUpcard)) {
|
|
109
|
+
targetBtn = hitBtn || standBtn;
|
|
89
110
|
} else {
|
|
90
|
-
targetBtn =
|
|
111
|
+
targetBtn = standBtn || hitBtn;
|
|
91
112
|
}
|
|
92
113
|
|
|
93
|
-
if (!targetBtn
|
|
114
|
+
if (!targetBtn) break;
|
|
94
115
|
|
|
95
|
-
LOG.info(`[bj] You:${playerTotal} Dealer:${
|
|
116
|
+
LOG.info(`[bj] You:${playerTotal} Dealer:${dealerUpcard} → ${targetBtn.label}`);
|
|
96
117
|
await humanDelay(400, 900);
|
|
97
118
|
|
|
98
119
|
try {
|
|
@@ -100,8 +121,6 @@ async function runBlackjack({ channel, waitForDankMemer, betAmount = 5000 }) {
|
|
|
100
121
|
if (followUp) {
|
|
101
122
|
current = followUp;
|
|
102
123
|
logMsg(current, `bj-round-${i}`);
|
|
103
|
-
const fButtons = getAllButtons(current);
|
|
104
|
-
if (fButtons.length === 0 || fButtons.every(b => b.disabled)) break;
|
|
105
124
|
} else break;
|
|
106
125
|
} catch { break; }
|
|
107
126
|
}
|
package/lib/commands/gamble.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gambling command handlers.
|
|
3
|
-
* Covers: cointoss, roulette, slots, snakeeyes
|
|
3
|
+
* Covers: cointoss (CV2), roulette, slots, snakeeyes
|
|
4
|
+
* Each handler is tailored to the actual Dank Memer response format.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
const {
|
|
7
8
|
LOG, c, getFullText, parseCoins, parseNetCoins, getAllButtons, safeClickButton,
|
|
8
|
-
logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay,
|
|
9
|
+
logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay, ensureCV2,
|
|
9
10
|
} = require('./utils');
|
|
10
11
|
|
|
11
|
-
function
|
|
12
|
+
function parseResult(text, cmdName) {
|
|
12
13
|
const net = parseNetCoins(text);
|
|
13
|
-
const lower = text.toLowerCase();
|
|
14
|
-
|
|
15
14
|
if (net > 0) {
|
|
16
15
|
LOG.coin(`[${cmdName}] ${c.green}+⏣ ${net.toLocaleString()}${c.reset}`);
|
|
17
16
|
return { result: `${cmdName} → +⏣ ${net.toLocaleString()}`, coins: net };
|
|
@@ -25,84 +24,188 @@ function parseGambleResult(text, cmdName) {
|
|
|
25
24
|
LOG.coin(`[${cmdName}] ${c.green}+⏣ ${coins.toLocaleString()}${c.reset}`);
|
|
26
25
|
return { result: `${cmdName} → +⏣ ${coins.toLocaleString()}`, coins };
|
|
27
26
|
}
|
|
28
|
-
if (lower.includes('won') || lower.includes('beat')) return { result: `${cmdName} → won`, coins: 0 };
|
|
29
|
-
if (lower.includes('lost') || lower.includes('bust')) return { result: `${cmdName} → lost`, coins: 0, lost: coins };
|
|
30
|
-
|
|
31
27
|
return { result: `${cmdName} done`, coins: 0 };
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
function checkMinBet(text) {
|
|
31
|
+
const lower = text.toLowerCase();
|
|
32
|
+
if (lower.includes("can't bet less than") || lower.includes('cannot bet less than') || lower.includes('minimum bet')) {
|
|
33
|
+
const m = text.match(/less than\s*\*?\*?[⏣o]?\s*([\d,]+)/i) || text.match(/(\d[\d,]+)/);
|
|
34
|
+
if (m) return parseInt(m[1].replace(/,/g, ''));
|
|
35
|
+
return -1;
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
// Filter utility buttons (null labels, warnings, refresh)
|
|
41
|
+
function gameButtons(msg) {
|
|
42
|
+
return getAllButtons(msg).filter(b =>
|
|
43
|
+
!b.disabled && b.label && b.label !== 'null' &&
|
|
44
|
+
!(b.customId || b.custom_id || '').includes('warning') &&
|
|
45
|
+
!(b.customId || b.custom_id || '').includes('refresh')
|
|
46
|
+
);
|
|
47
|
+
}
|
|
39
48
|
|
|
49
|
+
async function commonChecks(response, cmdName) {
|
|
40
50
|
if (!response) {
|
|
41
51
|
LOG.warn(`[${cmdName}] No response`);
|
|
42
|
-
return { result: 'no response', coins: 0 };
|
|
52
|
+
return { skip: true, ret: { result: 'no response', coins: 0 } };
|
|
43
53
|
}
|
|
44
|
-
|
|
45
54
|
if (isHoldTight(response)) {
|
|
46
55
|
const reason = getHoldTightReason(response);
|
|
47
|
-
LOG.warn(`[${cmdName}] Hold Tight${reason ? ` (
|
|
56
|
+
LOG.warn(`[${cmdName}] Hold Tight${reason ? ` (/${reason})` : ''} — waiting 30s`);
|
|
48
57
|
await sleep(30000);
|
|
49
|
-
return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
|
|
58
|
+
return { skip: true, ret: { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason } };
|
|
59
|
+
}
|
|
60
|
+
const text = getFullText(response);
|
|
61
|
+
const minBet = checkMinBet(text);
|
|
62
|
+
if (minBet !== 0) {
|
|
63
|
+
const val = minBet > 0 ? minBet : 0;
|
|
64
|
+
LOG.warn(`[${cmdName}] Min bet: ⏣ ${val > 0 ? val.toLocaleString() : '?'}`);
|
|
65
|
+
return { skip: true, ret: { result: `min bet ${val}`, coins: 0, newMinBet: val > 0 ? val : undefined } };
|
|
50
66
|
}
|
|
67
|
+
return { skip: false };
|
|
68
|
+
}
|
|
51
69
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Cointoss — CV2 format. Fetch raw components, pick Heads/Tails randomly.
|
|
72
|
+
*/
|
|
73
|
+
async function runCointoss({ channel, waitForDankMemer, betAmount = 10000 }) {
|
|
74
|
+
const cmd = `pls cointoss ${betAmount}`;
|
|
75
|
+
LOG.cmd(`${c.white}${c.bold}${cmd}${c.reset}`);
|
|
76
|
+
await channel.send(cmd);
|
|
77
|
+
let response = await waitForDankMemer(10000);
|
|
78
|
+
|
|
79
|
+
await ensureCV2(response);
|
|
80
|
+
const chk = await commonChecks(response, 'cointoss');
|
|
81
|
+
if (chk.skip) return chk.ret;
|
|
82
|
+
|
|
83
|
+
logMsg(response, 'cointoss');
|
|
84
|
+
const btns = gameButtons(response).filter(b =>
|
|
85
|
+
!(b.customId || b.custom_id || '').includes('bet')
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (btns.length > 0) {
|
|
89
|
+
const choice = Math.random() < 0.5 ? 'head' : 'tail';
|
|
90
|
+
const btn = btns.find(b => (b.label || '').toLowerCase().includes(choice)) || btns[0];
|
|
91
|
+
LOG.info(`[cointoss] Picking "${btn.label}"`);
|
|
92
|
+
await humanDelay(200, 500);
|
|
93
|
+
try {
|
|
94
|
+
const followUp = await safeClickButton(response, btn);
|
|
95
|
+
if (followUp) {
|
|
96
|
+
await ensureCV2(followUp);
|
|
97
|
+
return parseResult(getFullText(followUp), 'cointoss');
|
|
98
|
+
}
|
|
99
|
+
// CV2 click returns null — wait for message update, clear cache, refetch
|
|
100
|
+
await sleep(2500);
|
|
101
|
+
response._cv2 = null;
|
|
102
|
+
response._cv2text = null;
|
|
103
|
+
response._cv2buttons = null;
|
|
104
|
+
await ensureCV2(response);
|
|
105
|
+
return parseResult(getFullText(response), 'cointoss');
|
|
106
|
+
} catch (e) { LOG.error(`[cointoss] Click error: ${e.message}`); }
|
|
62
107
|
}
|
|
63
108
|
|
|
64
|
-
|
|
65
|
-
|
|
109
|
+
return parseResult(getFullText(response), 'cointoss');
|
|
110
|
+
}
|
|
66
111
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Roulette — embed format. Always pick Red (best even-money odds ~48.6%).
|
|
114
|
+
* Sometimes auto-bets if color is in command, sometimes shows buttons.
|
|
115
|
+
*/
|
|
116
|
+
async function runRoulette({ channel, waitForDankMemer, betAmount = 10000 }) {
|
|
117
|
+
const cmd = `pls roulette ${betAmount} red`;
|
|
118
|
+
LOG.cmd(`${c.white}${c.bold}${cmd}${c.reset}`);
|
|
119
|
+
await channel.send(cmd);
|
|
120
|
+
let response = await waitForDankMemer(10000);
|
|
121
|
+
|
|
122
|
+
const chk = await commonChecks(response, 'roulette');
|
|
123
|
+
if (chk.skip) return chk.ret;
|
|
124
|
+
|
|
125
|
+
logMsg(response, 'roulette');
|
|
126
|
+
|
|
127
|
+
// Check if buttons appeared (manual pick needed)
|
|
128
|
+
const btns = gameButtons(response).filter(b =>
|
|
129
|
+
!(b.customId || b.custom_id || '').includes('bet')
|
|
130
|
+
);
|
|
131
|
+
const redBtn = btns.find(b => (b.label || '').toLowerCase() === 'red');
|
|
132
|
+
if (redBtn) {
|
|
133
|
+
LOG.info(`[roulette] Picking "Red"`);
|
|
134
|
+
await humanDelay(200, 500);
|
|
135
|
+
try {
|
|
136
|
+
const followUp = await safeClickButton(response, redBtn);
|
|
137
|
+
if (followUp) {
|
|
138
|
+
response = followUp;
|
|
139
|
+
logMsg(response, 'roulette-after-pick');
|
|
140
|
+
}
|
|
141
|
+
} catch (e) { LOG.error(`[roulette] Click error: ${e.message}`); }
|
|
84
142
|
}
|
|
85
143
|
|
|
86
|
-
|
|
144
|
+
// Wait for spin animation to complete
|
|
145
|
+
let text = getFullText(response);
|
|
146
|
+
if (text.toLowerCase().includes('spinning') || text.toLowerCase().includes('rolling') || text.toLowerCase().includes('pick a color')) {
|
|
147
|
+
await sleep(3500);
|
|
148
|
+
try {
|
|
149
|
+
const final = await channel.messages.fetch(response.id);
|
|
150
|
+
if (final) text = getFullText(final);
|
|
151
|
+
} catch {}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return parseResult(text, 'roulette');
|
|
87
155
|
}
|
|
88
156
|
|
|
89
157
|
/**
|
|
90
|
-
*
|
|
158
|
+
* Slots — embed format. Auto-spins, no button needed. Wait for animation.
|
|
91
159
|
*/
|
|
92
|
-
async function
|
|
93
|
-
|
|
94
|
-
}
|
|
160
|
+
async function runSlots({ channel, waitForDankMemer, betAmount = 10000 }) {
|
|
161
|
+
const cmd = `pls slots ${betAmount}`;
|
|
162
|
+
LOG.cmd(`${c.white}${c.bold}${cmd}${c.reset}`);
|
|
163
|
+
await channel.send(cmd);
|
|
164
|
+
let response = await waitForDankMemer(10000);
|
|
95
165
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
166
|
+
const chk = await commonChecks(response, 'slots');
|
|
167
|
+
if (chk.skip) return chk.ret;
|
|
168
|
+
|
|
169
|
+
logMsg(response, 'slots');
|
|
170
|
+
const text = getFullText(response);
|
|
171
|
+
|
|
172
|
+
// Slots auto-spins — if still animating, wait and refetch
|
|
173
|
+
if (text.toLowerCase().includes('spinning')) {
|
|
174
|
+
await sleep(3500);
|
|
175
|
+
try {
|
|
176
|
+
const final = await channel.messages.fetch(response.id);
|
|
177
|
+
if (final) return parseResult(getFullText(final), 'slots');
|
|
178
|
+
} catch {}
|
|
179
|
+
}
|
|
99
180
|
|
|
100
|
-
|
|
101
|
-
return runGamble({ channel, waitForDankMemer, cmdName: 'slots', cmdString: `pls slots ${betAmount}` });
|
|
181
|
+
return parseResult(text, 'slots');
|
|
102
182
|
}
|
|
103
183
|
|
|
104
|
-
|
|
105
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Snakeeyes — embed format. Auto-rolls, no button needed. Wait for animation.
|
|
186
|
+
*/
|
|
187
|
+
async function runSnakeeyes({ channel, waitForDankMemer, betAmount = 10000 }) {
|
|
188
|
+
const cmd = `pls snakeeyes ${betAmount}`;
|
|
189
|
+
LOG.cmd(`${c.white}${c.bold}${cmd}${c.reset}`);
|
|
190
|
+
await channel.send(cmd);
|
|
191
|
+
let response = await waitForDankMemer(10000);
|
|
192
|
+
|
|
193
|
+
const chk = await commonChecks(response, 'snakeeyes');
|
|
194
|
+
if (chk.skip) return chk.ret;
|
|
195
|
+
|
|
196
|
+
logMsg(response, 'snakeeyes');
|
|
197
|
+
const text = getFullText(response);
|
|
198
|
+
|
|
199
|
+
// Snakeeyes auto-rolls — if still animating, wait and refetch
|
|
200
|
+
if (text.toLowerCase().includes('rolling')) {
|
|
201
|
+
await sleep(3500);
|
|
202
|
+
try {
|
|
203
|
+
const final = await channel.messages.fetch(response.id);
|
|
204
|
+
if (final) return parseResult(getFullText(final), 'snakeeyes');
|
|
205
|
+
} catch {}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return parseResult(text, 'snakeeyes');
|
|
106
209
|
}
|
|
107
210
|
|
|
108
|
-
module.exports = {
|
|
211
|
+
module.exports = { runCointoss, runRoulette, runSlots, runSnakeeyes };
|
package/lib/commands/generic.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const {
|
|
8
8
|
LOG, c, getFullText, parseCoins, getAllButtons, getAllSelectMenus,
|
|
9
9
|
safeClickButton, logMsg, isHoldTight, getHoldTightReason, sleep, humanDelay, needsItem,
|
|
10
|
+
isCV2, ensureCV2,
|
|
10
11
|
} = require('./utils');
|
|
11
12
|
const { buyItem } = require('./shop');
|
|
12
13
|
|
|
@@ -23,7 +24,7 @@ async function runGeneric({ channel, waitForDankMemer, cmdString, cmdName, clien
|
|
|
23
24
|
LOG.cmd(`${c.white}${c.bold}${cmdString}${c.reset}`);
|
|
24
25
|
|
|
25
26
|
await channel.send(cmdString);
|
|
26
|
-
|
|
27
|
+
let response = await waitForDankMemer(10000);
|
|
27
28
|
|
|
28
29
|
if (!response) {
|
|
29
30
|
LOG.warn(`[${cmdName}] No response`);
|
|
@@ -37,6 +38,7 @@ async function runGeneric({ channel, waitForDankMemer, cmdString, cmdName, clien
|
|
|
37
38
|
return { result: `hold tight (${reason || 'unknown'})`, coins: 0, holdTightReason: reason };
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
if (isCV2(response)) await ensureCV2(response);
|
|
40
42
|
logMsg(response, cmdName);
|
|
41
43
|
const text = getFullText(response);
|
|
42
44
|
const coins = parseCoins(text);
|
|
@@ -109,12 +111,14 @@ async function runGeneric({ channel, waitForDankMemer, cmdString, cmdName, clien
|
|
|
109
111
|
// Find row index of first select menu
|
|
110
112
|
let menuRowIdx = -1;
|
|
111
113
|
for (let i = 0; i < (response.components || []).length; i++) {
|
|
112
|
-
|
|
114
|
+
const row = response.components[i];
|
|
115
|
+
if (!row) continue;
|
|
116
|
+
for (const comp of row.components || []) {
|
|
113
117
|
if (comp.type === 'STRING_SELECT' || comp.type === 3) { menuRowIdx = i; break; }
|
|
114
118
|
}
|
|
115
119
|
if (menuRowIdx >= 0) break;
|
|
116
120
|
}
|
|
117
|
-
const menu = menuRowIdx >= 0 ? response.components[menuRowIdx]
|
|
121
|
+
const menu = menuRowIdx >= 0 ? response.components?.[menuRowIdx]?.components?.[0] : null;
|
|
118
122
|
const options = menu?.options || [];
|
|
119
123
|
if (options.length > 0 && menuRowIdx >= 0) {
|
|
120
124
|
const opt = options[Math.floor(Math.random() * options.length)];
|
package/lib/commands/highlow.js
CHANGED
|
@@ -27,9 +27,9 @@ function parseHintNumber(text) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function parseNetCoins(text) {
|
|
30
|
-
const netMatch = text.match(/Net:\s*[⏣
|
|
30
|
+
const netMatch = text.match(/Net:\s*\*{0,2}\s*[⏣]\s*\*{0,2}([+-]?[\d,]+)/i);
|
|
31
31
|
if (netMatch) return parseInt(netMatch[1].replace(/,/g, ''));
|
|
32
|
-
const winMatch = text.match(/Winnings:\s*[⏣
|
|
32
|
+
const winMatch = text.match(/Winnings:\s*\*{0,2}\s*[⏣]\s*\*{0,2}([\d,]+)/i);
|
|
33
33
|
if (winMatch) return parseInt(winMatch[1].replace(/,/g, ''));
|
|
34
34
|
return 0;
|
|
35
35
|
}
|
package/lib/commands/index.js
CHANGED
|
@@ -16,13 +16,14 @@ const { runScratch } = require('./scratch');
|
|
|
16
16
|
const { runBlackjack } = require('./blackjack');
|
|
17
17
|
const { runTrivia, triviaDB } = require('./trivia');
|
|
18
18
|
const { runWorkShift } = require('./work');
|
|
19
|
-
const { runCointoss, runRoulette, runSlots, runSnakeeyes
|
|
19
|
+
const { runCointoss, runRoulette, runSlots, runSnakeeyes } = require('./gamble');
|
|
20
20
|
const { runDeposit } = require('./deposit');
|
|
21
21
|
const { runGeneric, runAlert } = require('./generic');
|
|
22
22
|
const { runStream } = require('./stream');
|
|
23
23
|
const { runDrops } = require('./drops');
|
|
24
24
|
const { buyItem, ITEM_COSTS } = require('./shop');
|
|
25
25
|
const { getPlayerLevel, meetsLevelRequirement } = require('./profile');
|
|
26
|
+
const { runInventory, fetchItemValues, enrichItems, getCachedInventory, getAllInventories, updateInventoryItem, deleteInventoryItem } = require('./inventory');
|
|
26
27
|
|
|
27
28
|
module.exports = {
|
|
28
29
|
// Individual commands
|
|
@@ -43,7 +44,6 @@ module.exports = {
|
|
|
43
44
|
runRoulette,
|
|
44
45
|
runSlots,
|
|
45
46
|
runSnakeeyes,
|
|
46
|
-
runGamble,
|
|
47
47
|
runDeposit,
|
|
48
48
|
runGeneric,
|
|
49
49
|
runAlert,
|
|
@@ -51,6 +51,15 @@ module.exports = {
|
|
|
51
51
|
runDrops,
|
|
52
52
|
buyItem,
|
|
53
53
|
|
|
54
|
+
// Inventory
|
|
55
|
+
runInventory,
|
|
56
|
+
fetchItemValues,
|
|
57
|
+
enrichItems,
|
|
58
|
+
getCachedInventory,
|
|
59
|
+
getAllInventories,
|
|
60
|
+
updateInventoryItem,
|
|
61
|
+
deleteInventoryItem,
|
|
62
|
+
|
|
54
63
|
// Profile / Level
|
|
55
64
|
getPlayerLevel,
|
|
56
65
|
meetsLevelRequirement,
|