dankgrinder 5.0.3 → 5.0.4
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/utils.js +11 -63
- package/lib/grinder.js +16 -13
- package/package.json +1 -1
package/lib/commands/utils.js
CHANGED
|
@@ -73,7 +73,7 @@ const LOG = {
|
|
|
73
73
|
cmd: (msg) => log(`${c.magenta}▸${c.reset}`, msg),
|
|
74
74
|
coin: (msg) => log(`${c.yellow}$${c.reset}`, msg),
|
|
75
75
|
buy: (msg) => log(`${c.blue}♦${c.reset}`, msg),
|
|
76
|
-
debug: (
|
|
76
|
+
debug: () => {},
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
// ── Pre-compiled Regex (avoid recompilation in hot paths) ────
|
|
@@ -259,9 +259,9 @@ function flattenComponents(components) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
function getAllButtons(msg) {
|
|
262
|
-
if (msg._cv2buttons?.length > 0) return msg._cv2buttons;
|
|
262
|
+
if (msg._cv2buttons?.length > 0) return msg._cv2buttons.filter(b => b.style !== 'LINK' && b.style !== 5);
|
|
263
263
|
const all = flattenComponents(msg.components);
|
|
264
|
-
return all.filter(c => c.type === 2 || c.type === 'BUTTON');
|
|
264
|
+
return all.filter(c => (c.type === 2 || c.type === 'BUTTON') && c.style !== 'LINK' && c.style !== 5);
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
function getAllSelectMenus(msg) {
|
|
@@ -286,6 +286,10 @@ function findSelectMenuOption(msg, label) {
|
|
|
286
286
|
// When CV2 fallback is used, waits for the message to update so callers always
|
|
287
287
|
// get the updated message back (instead of null, which broke multi-round games).
|
|
288
288
|
async function safeClickButton(msg, button) {
|
|
289
|
+
// Skip LINK buttons (external URLs) — they have style=LINK/5 and no customId
|
|
290
|
+
if (button.style === 'LINK' || button.style === 5 || (!button.customId && !button.custom_id && typeof button.click !== 'function')) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
289
293
|
if (typeof button.click === 'function') {
|
|
290
294
|
return button.click();
|
|
291
295
|
}
|
|
@@ -344,66 +348,10 @@ function getHoldTightReason(msg) {
|
|
|
344
348
|
return match ? match[1].toLowerCase() : null;
|
|
345
349
|
}
|
|
346
350
|
|
|
347
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (msg.content) LOG.debug(`[${label}] content: "${msg.content.substring(0, 200).replace(/\n/g, ' ')}"`);
|
|
352
|
-
// Embeds
|
|
353
|
-
for (const e of msg.embeds || []) {
|
|
354
|
-
if (e.title) LOG.debug(`[${label}] title: "${e.title}"`);
|
|
355
|
-
if (e.description) LOG.debug(`[${label}] desc: "${e.description.substring(0, 200).replace(/\n/g, ' ')}"`);
|
|
356
|
-
for (const f of e.fields || []) LOG.debug(`[${label}] field: "${f.name}" = "${(f.value || '').substring(0, 150)}"`);
|
|
357
|
-
if (e.footer?.text) LOG.debug(`[${label}] footer: "${e.footer.text}"`);
|
|
358
|
-
if (e.image?.url) LOG.debug(`[${label}] image: ${e.image.url.substring(0, 80)}`);
|
|
359
|
-
}
|
|
360
|
-
// Components (buttons, selects, CV2 text)
|
|
361
|
-
for (const row of msg.components || []) {
|
|
362
|
-
if (!row) continue;
|
|
363
|
-
if (row.type === 'TEXT_DISPLAY' && row.content)
|
|
364
|
-
LOG.debug(`[${label}] cv2-text: "${row.content.substring(0, 200).replace(/\n/g, ' ')}"`);
|
|
365
|
-
if (row.type === 'CONTAINER' || row.type === 'SECTION') {
|
|
366
|
-
for (const comp of row.components || []) {
|
|
367
|
-
if (comp.type === 'TEXT_DISPLAY' && comp.content)
|
|
368
|
-
LOG.debug(`[${label}] cv2-section: "${comp.content.substring(0, 200).replace(/\n/g, ' ')}"`);
|
|
369
|
-
if (comp.components) {
|
|
370
|
-
for (const sub of comp.components) {
|
|
371
|
-
if (sub.type === 'TEXT_DISPLAY' && sub.content)
|
|
372
|
-
LOG.debug(`[${label}] cv2-nested: "${sub.content.substring(0, 200).replace(/\n/g, ' ')}"`);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
for (const comp of row.components || []) {
|
|
378
|
-
if (comp.type === 'BUTTON' || comp.type === 2)
|
|
379
|
-
LOG.debug(`[${label}] btn: "${comp.label}" emoji=${comp.emoji?.name || '-'} disabled=${comp.disabled} style=${comp.style} id=${(comp.customId || '').substring(0, 40)}`);
|
|
380
|
-
if (comp.type === 'STRING_SELECT' || comp.type === 3)
|
|
381
|
-
LOG.debug(`[${label}] select: ${comp.customId} [${comp.options?.map(o => `${o.label}${o.default ? '*' : ''}`).join(', ')}]`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// ── Dump full message raw (for debugging) ────────────────────
|
|
387
|
-
function dumpMessage(msg, label) {
|
|
388
|
-
console.log(`\n═══ [${label}] ═══`);
|
|
389
|
-
console.log(` author: ${msg.author?.tag} (${msg.author?.id})`);
|
|
390
|
-
console.log(` content: "${msg.content || ''}"`);
|
|
391
|
-
console.log(` embeds (${msg.embeds?.length || 0}):`);
|
|
392
|
-
for (const e of msg.embeds || []) {
|
|
393
|
-
console.log(JSON.stringify({
|
|
394
|
-
title: e.title, description: e.description,
|
|
395
|
-
fields: e.fields?.map(f => ({ name: f.name, value: f.value })),
|
|
396
|
-
footer: e.footer?.text, color: e.color,
|
|
397
|
-
}, null, 2));
|
|
398
|
-
}
|
|
399
|
-
console.log(` components (${msg.components?.length || 0}):`);
|
|
400
|
-
for (const row of msg.components || []) {
|
|
401
|
-
for (const comp of row.components || []) {
|
|
402
|
-
console.log(` type=${comp.type} label="${comp.label}" customId="${comp.customId}" disabled=${comp.disabled} style=${comp.style}`);
|
|
403
|
-
if (comp.options) console.log(` options: ${JSON.stringify(comp.options.map(o => ({ label: o.label, value: o.value, default: o.default })))}`);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
351
|
+
// logMsg / dumpMessage — disabled in production (no-ops).
|
|
352
|
+
// Enable by setting DEBUG_MSGS=1 env var for troubleshooting.
|
|
353
|
+
function logMsg() {}
|
|
354
|
+
function dumpMessage() {}
|
|
407
355
|
|
|
408
356
|
// ── CV2 (Components V2) Support ──────────────────────────────
|
|
409
357
|
// Discord's CV2 messages (flag 32768) aren't parsed by the selfbot library.
|
package/lib/grinder.js
CHANGED
|
@@ -453,17 +453,13 @@ function renderDashboard() {
|
|
|
453
453
|
|
|
454
454
|
lines.push(bar);
|
|
455
455
|
|
|
456
|
-
//
|
|
456
|
+
// Absolute cursor home — always draw from row 1
|
|
457
457
|
process.stdout.write('\x1b[H');
|
|
458
|
-
const prevLines = dashboardLines;
|
|
459
458
|
for (const line of lines) {
|
|
460
459
|
process.stdout.write(c.clearLine + '\r' + line + '\n');
|
|
461
460
|
}
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
for (let i = 0; i < maxClear; i++) {
|
|
465
|
-
process.stdout.write(c.clearLine + '\r\n');
|
|
466
|
-
}
|
|
461
|
+
// Erase everything below the dashboard (clears ghost bars, trailing lines)
|
|
462
|
+
process.stdout.write('\x1b[J');
|
|
467
463
|
dashboardLines = lines.length;
|
|
468
464
|
dashboardRendering = false;
|
|
469
465
|
}
|
|
@@ -2266,11 +2262,8 @@ class AccountWorker {
|
|
|
2266
2262
|
} catch {}
|
|
2267
2263
|
}
|
|
2268
2264
|
|
|
2269
|
-
// Let Discord gateway settle
|
|
2265
|
+
// Let Discord gateway settle
|
|
2270
2266
|
await new Promise(r => setTimeout(r, 2500));
|
|
2271
|
-
// Run initial inventory check (awaited) before grind loop starts
|
|
2272
|
-
await this.checkInventory().catch(() => {});
|
|
2273
|
-
this.grindLoop();
|
|
2274
2267
|
resolve();
|
|
2275
2268
|
});
|
|
2276
2269
|
|
|
@@ -2395,7 +2388,7 @@ async function start(apiKey, apiUrl) {
|
|
|
2395
2388
|
console.log(` ${checks.join(' ')}`);
|
|
2396
2389
|
console.log('');
|
|
2397
2390
|
|
|
2398
|
-
//
|
|
2391
|
+
// Phase 1: Login all accounts (staggered to avoid 429s)
|
|
2399
2392
|
const BATCH_SIZE = 5;
|
|
2400
2393
|
const BATCH_DELAY_MS = 3000;
|
|
2401
2394
|
for (let i = 0; i < accounts.length; i++) {
|
|
@@ -2405,12 +2398,22 @@ async function start(apiKey, apiUrl) {
|
|
|
2405
2398
|
workerMap.set(accounts[i].id, worker);
|
|
2406
2399
|
await worker.start();
|
|
2407
2400
|
if ((i + 1) % BATCH_SIZE === 0 && i + 1 < accounts.length) {
|
|
2408
|
-
log('info', `${c.dim}
|
|
2401
|
+
log('info', `${c.dim}Logged in ${i + 1}/${accounts.length}, next batch in ${BATCH_DELAY_MS / 1000}s...${c.reset}`);
|
|
2409
2402
|
await new Promise(r => setTimeout(r, BATCH_DELAY_MS));
|
|
2410
2403
|
hintGC();
|
|
2411
2404
|
}
|
|
2412
2405
|
}
|
|
2413
2406
|
|
|
2407
|
+
// Phase 2: Run inventory on ALL accounts (must complete before any grinding)
|
|
2408
|
+
log('info', `${c.dim}Checking inventory for all ${workers.length} accounts...${c.reset}`);
|
|
2409
|
+
await Promise.all(workers.map(w => w.checkInventory().catch(() => {})));
|
|
2410
|
+
log('success', `${c.dim}All inventories checked. Starting grind loops...${c.reset}`);
|
|
2411
|
+
|
|
2412
|
+
// Phase 3: Start all grind loops
|
|
2413
|
+
for (const w of workers) {
|
|
2414
|
+
if (!shutdownCalled) w.grindLoop();
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2414
2417
|
startTime = Date.now();
|
|
2415
2418
|
dashboardStarted = true;
|
|
2416
2419
|
setDashboardActive(true);
|