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.
@@ -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: (msg) => log(`${c.dim}⊙${c.reset}`, msg),
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
- // ── Debug Logger ─────────────────────────────────────────────
348
- function logMsg(msg, label) {
349
- if (!msg) { LOG.debug(`[${label}] No message`); return; }
350
- // Content
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
- // Use absolute cursor positioning (row 1, col 1) to avoid ghost bar drift
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
- // Clear any trailing lines from previous (larger) render
463
- const maxClear = Math.max(prevLines - lines.length, 0) + 3;
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 before sending first command
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
- // All accounts run simultaneously stagger logins in batches to avoid 429s
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}Started ${i + 1}/${accounts.length} accounts, next batch in ${BATCH_DELAY_MS / 1000}s...${c.reset}`);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dankgrinder",
3
- "version": "5.0.3",
3
+ "version": "5.0.4",
4
4
  "description": "Dank Memer automation engine — grind coins while you sleep",
5
5
  "bin": {
6
6
  "dankgrinder": "bin/dankgrinder.js"