dankgrinder 6.19.0 → 6.25.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/crime.js +9 -1
- package/lib/commands/search.js +5 -2
- package/lib/grinder.js +305 -127
- package/lib/rawLogger.js +44 -7
- package/package.json +1 -1
package/lib/commands/crime.js
CHANGED
|
@@ -16,11 +16,19 @@ const {
|
|
|
16
16
|
const { Trie, VoseAlias, LRUCache } = require('../structures');
|
|
17
17
|
|
|
18
18
|
const SAFE_CRIME_OPTIONS = Object.freeze([
|
|
19
|
-
|
|
19
|
+
// 100% safe (from logs analysis)
|
|
20
|
+
'identity theft', 'fraud', 'littering',
|
|
21
|
+
// 67% safe
|
|
22
|
+
'dui',
|
|
23
|
+
// Other potentially safe options
|
|
24
|
+
'tax evasion', 'cybercrime', 'hacking',
|
|
20
25
|
'money laundering', 'tax fraud', 'insurance fraud', 'scam',
|
|
21
26
|
]);
|
|
22
27
|
|
|
23
28
|
const RISKY_CRIME_OPTIONS = Object.freeze([
|
|
29
|
+
// 0% safe (from logs)
|
|
30
|
+
'cyber bullying', 'trespassing', 'shoplifting',
|
|
31
|
+
// Known dangerous
|
|
24
32
|
'murder', 'arson', 'assault', 'kidnap', 'terrorism',
|
|
25
33
|
]);
|
|
26
34
|
|
package/lib/commands/search.js
CHANGED
|
@@ -16,9 +16,12 @@ const {
|
|
|
16
16
|
const { VoseAlias, Trie, EMA, LRUCache } = require('../structures');
|
|
17
17
|
|
|
18
18
|
const SAFE_SEARCH_LOCATIONS = Object.freeze([
|
|
19
|
+
// 100% safe (from logs analysis)
|
|
20
|
+
'shoe', 'washer', 'attic', 'pocket',
|
|
21
|
+
// Other safe locations
|
|
19
22
|
'sofa', 'mailbox', 'dog', 'car', 'dresser', 'laundromat', 'bed',
|
|
20
|
-
'couch', 'pantry', 'fridge', 'kitchen', 'bathroom',
|
|
21
|
-
'closet', '
|
|
23
|
+
'couch', 'pantry', 'fridge', 'kitchen', 'bathroom',
|
|
24
|
+
'closet', 'vacuum', 'toilet', 'sink', 'shower',
|
|
22
25
|
'tree', 'grass', 'bushes', 'garden', 'park', 'backyard',
|
|
23
26
|
]);
|
|
24
27
|
|
package/lib/grinder.js
CHANGED
|
@@ -458,7 +458,7 @@ function renderDashboard() {
|
|
|
458
458
|
lines.push(bRow(` ${c.bold}${titleGrad}${c.reset} ${D}v${PKG_VERSION}${c.reset} ${G}${spin}${c.reset}`));
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
lines.push(bRow(` ${D}v${PKG_VERSION}${c.reset} ${G}${spin}${c.reset}`));
|
|
461
|
+
lines.push(bRow(` ${D}v${PKG_VERSION}${c.reset} ${G}${spin}${c.reset} ${Y}◷${c.reset} ${D}UP${c.reset} ${c.bold}${Y}${formatUptime()}${c.reset}`));
|
|
462
462
|
|
|
463
463
|
// Subtitle info
|
|
464
464
|
const activeCount = workers.filter(w => w.running && !w.paused && !w.dashboardPaused).length;
|
|
@@ -487,40 +487,62 @@ function renderDashboard() {
|
|
|
487
487
|
lines.push(bEmpty);
|
|
488
488
|
|
|
489
489
|
// ═══════════════════════════════════════════════════════════════
|
|
490
|
-
// STATS PANEL
|
|
490
|
+
// STATS PANEL — left: metrics (fixed labels) | right: big trend
|
|
491
491
|
// ═══════════════════════════════════════════════════════════════
|
|
492
492
|
lines.push(bSep);
|
|
493
493
|
lines.push(bEmpty);
|
|
494
494
|
|
|
495
|
-
// Earnings sparkline data
|
|
496
495
|
const now = Date.now();
|
|
497
496
|
if (now - lastEarningsSample > 8000) { earningsHistory.push(totalCoins); lastEarningsSample = now; }
|
|
498
|
-
const sparkW = Math.min(30, Math.floor(iw * 0.3));
|
|
499
|
-
const spark = drawSparkline(earningsHistory.toArray(), sparkW);
|
|
500
|
-
|
|
501
|
-
// Row 1: Balance + Earned
|
|
502
497
|
const elapsedHrs = (Date.now() - startTime) / 3_600_000;
|
|
503
498
|
const perHr = elapsedHrs > 0.01 ? Math.round(totalCoins / elapsedHrs) : 0;
|
|
504
|
-
const peakFlag = isNewHigh ? ` ${R}${c.bold}* NEW HIGH *${c.reset}` : '';
|
|
505
|
-
|
|
506
|
-
lines.push(bRow(` ${Au}⟐${c.reset} ${D}BALANCE${c.reset} ${c.bold}${Au}⏣ ${formatCoins(totalBalance)}${c.reset} ${G}▲${c.reset} ${D}EARNED${c.reset} ${c.bold}${G}+⏣ ${formatCoins(totalCoins)}${c.reset} ${D}(${c.reset}${G}${formatCoins(perHr)}/h${c.reset}${D})${c.reset}${peakFlag}`));
|
|
507
|
-
|
|
508
|
-
// Row 2: Peak + Trend sparkline
|
|
509
|
-
lines.push(bRow(` ${O}★${c.reset} ${D}PEAK${c.reset} ${c.bold}${O}⏣ ${formatCoins(sessionPeakCoins)}${c.reset} ${A}~${c.reset} ${D}TREND${c.reset} ${spark}`));
|
|
510
499
|
|
|
511
|
-
//
|
|
500
|
+
// ── Compute all metric values ──────────────────────────────────
|
|
512
501
|
const cpmVal = globalCmdRate.getRate().toFixed(1);
|
|
513
502
|
const srColor = successRate >= 95 ? G : successRate >= 80 ? Y : R;
|
|
514
|
-
const
|
|
515
|
-
const srBar = progressBar(successRate, 100, srBarW, successRate >= 95 ? [52, 211, 153] : successRate >= 80 ? [251, 191, 36] : [239, 68, 68]);
|
|
516
|
-
lines.push(bRow(` ${B}◆${c.reset} ${D}CMDS${c.reset} ${c.bold}${B}${totalCommands}${c.reset} ${srColor}${successRate}%${c.reset} ${srBar} ${Cy}${cpmVal}${c.reset}${D}/min${c.reset} ${Y}◷${c.reset} ${D}UP${c.reset} ${c.bold}${Y}${formatUptime()}${c.reset}`));
|
|
517
|
-
|
|
518
|
-
// Row 4: Memory
|
|
503
|
+
const srBar = progressBar(successRate, 100, 10, successRate >= 95 ? [52, 211, 153] : successRate >= 80 ? [251, 191, 36] : [239, 68, 68]);
|
|
519
504
|
const memMB = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
|
|
520
505
|
const memCol = memMB > 900 ? [239, 68, 68] : memMB > 600 ? [251, 191, 36] : [52, 211, 153];
|
|
521
|
-
const
|
|
522
|
-
const
|
|
523
|
-
|
|
506
|
+
const memBar = progressBar(memMB, 1024, 10, memCol, [40, 40, 55]);
|
|
507
|
+
const perHrColor = perHr >= 0 ? G : R;
|
|
508
|
+
const perHrSign = perHr >= 0 ? '+' : '';
|
|
509
|
+
const newHighFlag = isNewHigh ? ` ${R}${c.bold}★ NEW HIGH ★${c.reset}` : '';
|
|
510
|
+
|
|
511
|
+
// ── Big trend sparkline (right column) ──────────────────────
|
|
512
|
+
const sparkW = Math.max(36, Math.floor(iw * 0.55));
|
|
513
|
+
const spark = drawSparkline(earningsHistory.toArray(), sparkW);
|
|
514
|
+
|
|
515
|
+
// ── Build each row: left metrics (fixed) | right trend ───────
|
|
516
|
+
const leftW = 36; // chars for left column content
|
|
517
|
+
|
|
518
|
+
// Helper: build a stat row with fixed-width label
|
|
519
|
+
const statRow = (icon, label, val) => {
|
|
520
|
+
const raw = `${icon} ${label} ${val}`;
|
|
521
|
+
const padded = raw + ' '.repeat(Math.max(0, leftW - raw.replace(RE, '').length));
|
|
522
|
+
return padded;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// Row 1: Balance
|
|
526
|
+
const balVal = `${Au}⏣${c.reset}${c.bold} ${formatCoins(totalBalance)}${c.reset}`;
|
|
527
|
+
const balRaw = `${Au}⟐${c.reset} ${D}BALANCE${c.reset} ${balVal}`;
|
|
528
|
+
lines.push(bRow(` ${balRaw.padEnd(leftW + 4)}${A}~${c.reset} ${D}TREND${c.reset} ${spark}`));
|
|
529
|
+
|
|
530
|
+
// Row 2: Earned
|
|
531
|
+
const earnVal = `${perHrColor}${c.bold}${perHrSign}⏣ ${formatCoins(perHr)}/h${c.reset}`;
|
|
532
|
+
const earnRaw = `${G}▲${c.reset} ${D}EARNED${c.reset} ${c.bold}${G}${perHrSign}⏣ ${formatCoins(totalCoins)}${c.reset}${newHighFlag}`;
|
|
533
|
+
lines.push(bRow(` ${earnRaw.padEnd(leftW + 4)}${D}──┬──${c.reset} ${c.dim}earned over session${c.reset}`));
|
|
534
|
+
|
|
535
|
+
// Row 3: Peak
|
|
536
|
+
const peakRaw = `${O}★${c.reset} ${D}PEAK${c.reset} ${c.bold}${O}⏣ ${formatCoins(sessionPeakCoins)}${c.reset}`;
|
|
537
|
+
lines.push(bRow(` ${peakRaw.padEnd(leftW + 4)}${D} │${c.reset} ${c.dim}⏣ ${formatCoins(Math.abs(perHr))}/h${c.reset}`));
|
|
538
|
+
|
|
539
|
+
// Row 4: Commands
|
|
540
|
+
const cmdsRaw = `${B}◆${c.reset} ${D}CMDS${c.reset} ${c.bold}${B}${totalCommands}${c.reset} ${srColor}${successRate}%${c.reset} ${srBar} ${Cy}${cpmVal}${c.reset}${D}/min${c.reset}`;
|
|
541
|
+
lines.push(bRow(` ${cmdsRaw.padEnd(leftW + 4)}${D} │${c.reset}`));
|
|
542
|
+
|
|
543
|
+
// Row 5: Memory
|
|
544
|
+
const memRaw = `${D}≡${c.reset} ${D}MEM${c.reset} ${rgb(memCol[0], memCol[1], memCol[2])}${c.bold}${memMB}MB${c.reset} ${memBar}`;
|
|
545
|
+
lines.push(bRow(` ${memRaw.padEnd(leftW + 4)}${D} │${c.reset}`));
|
|
524
546
|
|
|
525
547
|
lines.push(bEmpty);
|
|
526
548
|
|
|
@@ -630,9 +652,9 @@ function renderDashboard() {
|
|
|
630
652
|
else if (ls != null) lsStr = `${G}♥${ls}${c.reset}`;
|
|
631
653
|
else lsStr = `${D}♥?${c.reset}`;
|
|
632
654
|
|
|
633
|
-
// ── Level indicator ──
|
|
655
|
+
// ── Level indicator (fixed width so value changes don't jitter) ──
|
|
634
656
|
const lvl = wk._level || 0;
|
|
635
|
-
const lvlStr = lvl > 0 ? `${Cy}L${lvl}${c.reset}` : `${D}L
|
|
657
|
+
const lvlStr = lvl > 0 ? `${Cy}L${String(lvl).padStart(3)}${c.reset}` : `${D}L???${c.reset}`;
|
|
636
658
|
|
|
637
659
|
// ── Earned (fixed visible width) ──
|
|
638
660
|
const earnNum = wk.stats.coins || 0;
|
|
@@ -1401,6 +1423,7 @@ class AccountWorker {
|
|
|
1401
1423
|
startupProgress = null,
|
|
1402
1424
|
requireComplete = false,
|
|
1403
1425
|
maxAttempts = 1,
|
|
1426
|
+
silent = false,
|
|
1404
1427
|
} = options;
|
|
1405
1428
|
if (this._invRunning) return { ok: false, skipped: 'busy' };
|
|
1406
1429
|
if (!force && this._lastInvCheck && Date.now() - this._lastInvCheck < 300_000) return { ok: false, skipped: 'recent' };
|
|
@@ -1414,7 +1437,7 @@ class AccountWorker {
|
|
|
1414
1437
|
const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
|
|
1415
1438
|
const attemptLabel = tries > 1 ? ` [try ${attempt}/${tries}]` : '';
|
|
1416
1439
|
const progressLine = `${baseLabel}${c.bold} ${this.username}${c.reset}${attemptLabel}`;
|
|
1417
|
-
process.stdout.write(`\x1b[2K\r${progressLine}`);
|
|
1440
|
+
if (!silent) process.stdout.write(`\x1b[2K\r${progressLine}`);
|
|
1418
1441
|
|
|
1419
1442
|
try {
|
|
1420
1443
|
const result = await commands.runInventory({
|
|
@@ -1424,9 +1447,10 @@ class AccountWorker {
|
|
|
1424
1447
|
accountId: this.account.id,
|
|
1425
1448
|
redis,
|
|
1426
1449
|
onPageProgress: ({ page, total }) => {
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1450
|
+
if (!silent) {
|
|
1451
|
+
const erase = '\x1b[2K\r';
|
|
1452
|
+
process.stdout.write(`${erase}${baseLabel} ${c.bold}${this.username}${c.reset} · page ${page}/${total}${attemptLabel}`);
|
|
1453
|
+
}
|
|
1430
1454
|
},
|
|
1431
1455
|
});
|
|
1432
1456
|
|
|
@@ -1435,8 +1459,10 @@ class AccountWorker {
|
|
|
1435
1459
|
}
|
|
1436
1460
|
|
|
1437
1461
|
// Final result on same line
|
|
1438
|
-
|
|
1439
|
-
|
|
1462
|
+
if (!silent) {
|
|
1463
|
+
const resultLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.green}${result.items?.length || 0} items${c.reset}, ⏣ ${c.green}${(result.totalValue || 0).toLocaleString()}${c.reset} net${attemptLabel}`;
|
|
1464
|
+
process.stdout.write(`\x1b[2K\r${resultLine}\n`);
|
|
1465
|
+
}
|
|
1440
1466
|
|
|
1441
1467
|
// Extract lifesaver count from inventory and cache in Redis
|
|
1442
1468
|
if (result.items && redis) {
|
|
@@ -1480,7 +1506,7 @@ class AccountWorker {
|
|
|
1480
1506
|
if (attempt < tries) {
|
|
1481
1507
|
const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
|
|
1482
1508
|
const retryLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.yellow}attempt ${attempt}/${tries} failed${c.reset} — retrying...`;
|
|
1483
|
-
process.stdout.write(`\x1b[2K\r${retryLine}\n`);
|
|
1509
|
+
if (!silent) process.stdout.write(`\x1b[2K\r${retryLine}\n`);
|
|
1484
1510
|
await new Promise((r) => setTimeout(r, 1500 + Math.floor(Math.random() * 1500)));
|
|
1485
1511
|
continue;
|
|
1486
1512
|
}
|
|
@@ -1491,7 +1517,7 @@ class AccountWorker {
|
|
|
1491
1517
|
} catch (e) {
|
|
1492
1518
|
const baseLabel = startupProgress ? `[inv] ${startupProgress.current}/${startupProgress.total}` : '[inv]';
|
|
1493
1519
|
const failLine = `${baseLabel} ${c.bold}${this.username}${c.reset}: ${c.red}failed${c.reset} — ${e.message}`;
|
|
1494
|
-
process.stdout.write(`\x1b[2K\r${failLine}\n`);
|
|
1520
|
+
if (!silent) process.stdout.write(`\x1b[2K\r${failLine}\n`);
|
|
1495
1521
|
return { ok: false, error: e.message };
|
|
1496
1522
|
} finally {
|
|
1497
1523
|
this._invRunning = false;
|
|
@@ -1499,7 +1525,7 @@ class AccountWorker {
|
|
|
1499
1525
|
}
|
|
1500
1526
|
}
|
|
1501
1527
|
|
|
1502
|
-
async checkBalance() {
|
|
1528
|
+
async checkBalance(silent = false) {
|
|
1503
1529
|
const prefix = this.account.use_slash ? '/' : 'pls';
|
|
1504
1530
|
const sentAt = Date.now();
|
|
1505
1531
|
|
|
@@ -1594,7 +1620,7 @@ class AccountWorker {
|
|
|
1594
1620
|
|
|
1595
1621
|
this.stats.balance = wallet;
|
|
1596
1622
|
this.stats.bankBalance = bank;
|
|
1597
|
-
this.log('bal', `Wallet: ${c.bold}${c.green}⏣ ${wallet.toLocaleString()}${c.reset} Bank: ${c.bold}${c.cyan}⏣ ${bank.toLocaleString()}${c.reset} Total: ${c.bold}⏣ ${(wallet + bank).toLocaleString()}${c.reset}
|
|
1623
|
+
if (!silent) this.log('bal', `Wallet: ${c.bold}${c.green}⏣ ${wallet.toLocaleString()}${c.reset} Bank: ${c.bold}${c.cyan}⏣ ${bank.toLocaleString()}${c.reset} Total: ${c.bold}⏣ ${(wallet + bank).toLocaleString()}${c.reset}`);
|
|
1598
1624
|
|
|
1599
1625
|
// Store in Redis for persistence
|
|
1600
1626
|
if (redis) {
|
|
@@ -1619,13 +1645,17 @@ class AccountWorker {
|
|
|
1619
1645
|
} catch { /* silent */ }
|
|
1620
1646
|
}
|
|
1621
1647
|
|
|
1622
|
-
// ── Check DM History for deaths/level-ups
|
|
1648
|
+
// ── Check DM History for deaths/level-ups (with retry) ─────
|
|
1623
1649
|
async checkDmHistory() {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1650
|
+
const maxRetries = 3;
|
|
1651
|
+
const delays = [1000, 2000, 4000];
|
|
1652
|
+
let lastError;
|
|
1653
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1654
|
+
try {
|
|
1655
|
+
const dankUser = await this.client.users.fetch(DANK_MEMER_ID);
|
|
1656
|
+
const dm = await dankUser.createDM();
|
|
1657
|
+
this._dmChannelId = dm.id;
|
|
1658
|
+
const recent = await dm.messages.fetch({ limit: 20 });
|
|
1629
1659
|
|
|
1630
1660
|
let deaths = 0, levelUps = 0, currentLevel = 0, lastLifesaverCount = -1;
|
|
1631
1661
|
for (const [, msg] of recent) {
|
|
@@ -1679,11 +1709,16 @@ class AccountWorker {
|
|
|
1679
1709
|
}
|
|
1680
1710
|
}
|
|
1681
1711
|
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1712
|
+
return { deaths, levelUps, currentLevel, lifesavers: lastLifesaverCount, dmChannelId: dm.id };
|
|
1713
|
+
} catch (e) {
|
|
1714
|
+
lastError = e;
|
|
1715
|
+
if (attempt < maxRetries - 1) {
|
|
1716
|
+
await new Promise(r => setTimeout(r, delays[attempt]));
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1686
1719
|
}
|
|
1720
|
+
this.log('debug', `DM check failed after ${maxRetries} attempts: ${lastError.message}`);
|
|
1721
|
+
return { deaths: 0, levelUps: 0, currentLevel: 0, lifesavers: -1 };
|
|
1687
1722
|
}
|
|
1688
1723
|
|
|
1689
1724
|
// ── Run Single Command ──────────────────────────────────────
|
|
@@ -2993,25 +3028,82 @@ async function start(apiKey, apiUrl) {
|
|
|
2993
3028
|
console.log(` ${checks.join(' ')}`);
|
|
2994
3029
|
console.log('');
|
|
2995
3030
|
|
|
2996
|
-
// ──
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3031
|
+
// ── Per-account inline login UI ──────────────────────────────
|
|
3032
|
+
// Track login state per account for inline rendering
|
|
3033
|
+
const loginStates = accounts.map((acc, i) => ({
|
|
3034
|
+
name: acc.label || acc.id || '?',
|
|
3035
|
+
done: false,
|
|
3036
|
+
failed: false,
|
|
3037
|
+
worker: null,
|
|
3038
|
+
workerIdx: i,
|
|
3039
|
+
}));
|
|
3040
|
+
|
|
3041
|
+
// Column widths (visible chars)
|
|
3042
|
+
const terminalW = process.stdout.columns || 90;
|
|
3043
|
+
const colNum = 4; // " # "
|
|
3044
|
+
const colSts = 3; // "ST "
|
|
3045
|
+
const colName = Math.min(24, Math.max(12, Math.floor(terminalW * 0.25)));
|
|
3046
|
+
const colGuild = Math.min(20, Math.max(8, Math.floor(terminalW * 0.2)));
|
|
3047
|
+
const colCmds = 10; // " 20 cmds"
|
|
3048
|
+
const totalVis = colNum + colSts + colName + colGuild + colCmds + 8;
|
|
3049
|
+
|
|
3050
|
+
// Print header + all account lines (initial pending state)
|
|
3051
|
+
console.log(` ${'─'.repeat(totalVis)}`);
|
|
3052
|
+
for (let i = 0; i < loginStates.length; i++) {
|
|
3053
|
+
const s = loginStates[i];
|
|
3054
|
+
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset} `;
|
|
3055
|
+
const name = s.name.substring(0, colName).padEnd(colName);
|
|
3056
|
+
console.log(`${num}${c.dim}PN${c.reset} ${name} ${c.dim}${'···'.padEnd(colGuild)}${c.reset} ${c.dim}···${c.reset}`);
|
|
3057
|
+
}
|
|
3058
|
+
console.log(` ${'─'.repeat(totalVis)}`);
|
|
3059
|
+
|
|
3060
|
+
// Draw pending spinners
|
|
3061
|
+
const drawLoginSpinners = () => {
|
|
3062
|
+
for (let i = 0; i < loginStates.length; i++) {
|
|
3063
|
+
const s = loginStates[i];
|
|
3064
|
+
if (s.done || s.failed) continue;
|
|
3065
|
+
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3066
|
+
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset} `;
|
|
3067
|
+
const name = s.name.substring(0, colName).padEnd(colName);
|
|
3068
|
+
const guild = 'logging in...'.substring(0, colGuild).padEnd(colGuild);
|
|
3069
|
+
process.stdout.write(`\x1b[${i + 2};0H\x1b[2K ${num}${rgb(139, 92, 246)}${spin}${c.reset} ${name} ${c.dim}${guild}${c.reset} ${c.dim}···${c.reset}`);
|
|
3070
|
+
}
|
|
3071
|
+
process.stdout.write(`\x1b[${loginStates.length + 3};0H`);
|
|
3007
3072
|
};
|
|
3073
|
+
const loginSpinnerInterval = setInterval(drawLoginSpinners, 80);
|
|
3074
|
+
|
|
3075
|
+
// Update a line when an account finishes
|
|
3076
|
+
const finalizeLoginLine = (idx, worker) => {
|
|
3077
|
+
const s = loginStates[idx];
|
|
3078
|
+
if (s.done || s.failed) return;
|
|
3079
|
+
s.done = true;
|
|
3080
|
+
s.worker = worker;
|
|
3081
|
+
|
|
3082
|
+
const num = `${c.dim}${(idx + 1).toString().padStart(colNum - 1)}${c.reset} `;
|
|
3083
|
+
const name = (worker.username || s.name || '?').substring(0, colName).padEnd(colName);
|
|
3084
|
+
|
|
3085
|
+
let sts, guild, cmds;
|
|
3086
|
+
if (worker._tokenInvalid) {
|
|
3087
|
+
sts = `${rgb(239, 68, 68)}✗${c.reset}`;
|
|
3088
|
+
guild = 'INVALID'.padEnd(colGuild);
|
|
3089
|
+
cmds = '···';
|
|
3090
|
+
s.failed = true;
|
|
3091
|
+
} else if (worker.channel) {
|
|
3092
|
+
sts = `${rgb(52, 211, 153)}✓${c.reset}`;
|
|
3093
|
+
const gn = (worker.channel.guild?.name || worker.channel.guild?.id || 'DM').substring(0, colGuild);
|
|
3094
|
+
guild = gn.padEnd(colGuild);
|
|
3095
|
+
cmds = `${worker.stats?.commands || 0}cmds`;
|
|
3096
|
+
} else {
|
|
3097
|
+
sts = `${rgb(251, 146, 60)}⏳${c.reset}`;
|
|
3098
|
+
guild = 'timeout'.padEnd(colGuild);
|
|
3099
|
+
cmds = '···';
|
|
3100
|
+
}
|
|
3008
3101
|
|
|
3009
|
-
|
|
3010
|
-
|
|
3102
|
+
const line = ` ${num}${sts} ${name} ${guild} ${cmds}`;
|
|
3103
|
+
process.stdout.write(`\x1b[${idx + 2};0H\x1b[2K${line}`);
|
|
3104
|
+
};
|
|
3011
3105
|
|
|
3012
|
-
// Phase 1: Login
|
|
3013
|
-
const LOGIN_PROGRESS_EVERY = 10;
|
|
3014
|
-
// Reduced delays: 50-150ms between logins (faster startup for 1k+ accounts)
|
|
3106
|
+
// Phase 1: Login in batches of 10
|
|
3015
3107
|
const parsedGapMin = Number.parseInt(String(process.env.LOGIN_GAP_MIN_MS || '50'), 10);
|
|
3016
3108
|
const parsedGapMax = Number.parseInt(String(process.env.LOGIN_GAP_MAX_MS || '150'), 10);
|
|
3017
3109
|
const LOGIN_GAP_MIN_MS = Number.isFinite(parsedGapMin) && parsedGapMin >= 0 ? parsedGapMin : 50;
|
|
@@ -3022,16 +3114,12 @@ async function start(apiKey, apiUrl) {
|
|
|
3022
3114
|
return LOGIN_GAP_MIN_MS + Math.floor(Math.random() * (LOGIN_GAP_MAX_MS - LOGIN_GAP_MIN_MS + 1));
|
|
3023
3115
|
};
|
|
3024
3116
|
|
|
3025
|
-
// Parallel login in batches of 10 to avoid rate limits while being fast
|
|
3026
|
-
// Within each batch, stagger logins by 100-600ms to avoid gateway flood
|
|
3027
3117
|
const BATCH_SIZE = 10;
|
|
3028
3118
|
for (let i = 0; i < accounts.length; i += BATCH_SIZE) {
|
|
3029
3119
|
if (shutdownCalled) break;
|
|
3030
3120
|
const batch = accounts.slice(i, Math.min(i + BATCH_SIZE, accounts.length));
|
|
3031
3121
|
|
|
3032
|
-
// Staggered parallel login: fire each login with a small jitter delay
|
|
3033
3122
|
await Promise.all(batch.map(async (acc, idx) => {
|
|
3034
|
-
// Stagger within batch: 0ms for first, 100-600ms for subsequent
|
|
3035
3123
|
if (idx > 0) {
|
|
3036
3124
|
const jitter = 100 + Math.floor(Math.random() * 500);
|
|
3037
3125
|
await new Promise(r => setTimeout(r, jitter));
|
|
@@ -3039,30 +3127,29 @@ async function start(apiKey, apiUrl) {
|
|
|
3039
3127
|
const worker = new AccountWorker(acc, i + idx);
|
|
3040
3128
|
workers.push(worker);
|
|
3041
3129
|
workerMap.set(acc.id, worker);
|
|
3130
|
+
loginStates[i + idx].worker = worker;
|
|
3042
3131
|
await worker.start();
|
|
3043
|
-
|
|
3132
|
+
finalizeLoginLine(i + idx, worker);
|
|
3044
3133
|
}));
|
|
3045
3134
|
|
|
3046
|
-
// Small gap between batches
|
|
3047
3135
|
if (i + BATCH_SIZE < accounts.length) {
|
|
3048
|
-
|
|
3049
|
-
await new Promise(r => setTimeout(r, gapMs));
|
|
3136
|
+
await new Promise(r => setTimeout(r, randomLoginGap()));
|
|
3050
3137
|
}
|
|
3051
3138
|
|
|
3052
3139
|
hintGC();
|
|
3053
3140
|
}
|
|
3054
3141
|
|
|
3055
|
-
clearInterval(
|
|
3056
|
-
|
|
3057
|
-
process.stdout.write(`\r${c.clearLine}`);
|
|
3058
|
-
console.log(` ${rgb(52, 211, 153)}✓${c.reset} ${c.bold}Login complete${c.reset} ${rgb(52, 211, 153)}${loginDone}/${accounts.length}${c.reset} ${c.dim}accounts connected${c.reset}`);
|
|
3059
|
-
console.log('');
|
|
3142
|
+
clearInterval(loginSpinnerInterval);
|
|
3143
|
+
process.stdout.write(`\x1b[${loginStates.length + 3};0H`);
|
|
3060
3144
|
|
|
3061
|
-
//
|
|
3145
|
+
// Final summary
|
|
3146
|
+
const loginDone = workers.filter(w => !w._tokenInvalid && w.channel).length;
|
|
3062
3147
|
const invalidWorkers = workers.filter(w => w._tokenInvalid);
|
|
3063
3148
|
const timedOutWorkers = workers.filter(w => !w.channel && !w._tokenInvalid);
|
|
3149
|
+
console.log(` ${rgb(52, 211, 153)}✓${c.reset} ${c.bold}Login complete${c.reset} ${rgb(52, 211, 153)}${loginDone}${c.reset}${c.dim}/${c.reset}${c.white}${accounts.length}${c.reset} ${c.dim}accounts connected${c.reset}`);
|
|
3150
|
+
console.log('');
|
|
3151
|
+
|
|
3064
3152
|
if (invalidWorkers.length > 0) {
|
|
3065
|
-
console.log('');
|
|
3066
3153
|
log('warn', `${rgb(239, 68, 68)}${invalidWorkers.length} account(s) have INVALID tokens:${c.reset}`);
|
|
3067
3154
|
for (const w of invalidWorkers) {
|
|
3068
3155
|
log('error', ` ✗ ${w.account.label || w.account.id} — token is invalid or expired`);
|
|
@@ -3076,85 +3163,176 @@ async function start(apiKey, apiUrl) {
|
|
|
3076
3163
|
// Filter out workers with invalid tokens from grinding
|
|
3077
3164
|
const activeWorkers = workers.filter(w => !w._tokenInvalid);
|
|
3078
3165
|
|
|
3079
|
-
// Phase 2:
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3166
|
+
// ── Phase 2: Inventory check with per-account inline rendering ─────────────────
|
|
3167
|
+
const invStates = activeWorkers.map((w, i) => ({
|
|
3168
|
+
name: w.username || w.account.label || '?',
|
|
3169
|
+
idx: i,
|
|
3170
|
+
done: false,
|
|
3171
|
+
failed: false,
|
|
3172
|
+
items: 0,
|
|
3173
|
+
value: 0,
|
|
3174
|
+
attempt: 0,
|
|
3175
|
+
worker: w,
|
|
3176
|
+
}));
|
|
3177
|
+
|
|
3178
|
+
const tw2 = process.stdout.columns || 90;
|
|
3179
|
+
const iColNum = 4;
|
|
3180
|
+
const iColName = Math.min(22, Math.max(10, Math.floor(tw2 * 0.22)));
|
|
3181
|
+
const iColItems = 8;
|
|
3182
|
+
const iColVal = 14;
|
|
3183
|
+
const iColTries = 10;
|
|
3184
|
+
const iTotalVis = iColNum + iColName + iColItems + iColVal + iColTries + 10;
|
|
3185
|
+
|
|
3186
|
+
console.log(` ${'─'.repeat(iTotalVis)}`);
|
|
3187
|
+
for (let i = 0; i < invStates.length; i++) {
|
|
3188
|
+
const s = invStates[i];
|
|
3189
|
+
const num = `${c.dim}${(i + 1).toString().padStart(iColNum - 1)}${c.reset} `;
|
|
3190
|
+
const name = s.name.substring(0, iColName).padEnd(iColName);
|
|
3191
|
+
console.log(`${num}${c.dim}··${c.reset} ${name} ${c.dim}${'checking...'.padEnd(iColItems)}${c.reset} ${c.dim}${'··'.padEnd(iColVal)}${c.reset} ${c.dim}${''.padEnd(iColTries)}${c.reset}`);
|
|
3192
|
+
}
|
|
3193
|
+
console.log(` ${'─'.repeat(iTotalVis)}`);
|
|
3194
|
+
|
|
3195
|
+
let invDone = 0, invFailed = 0;
|
|
3196
|
+
|
|
3197
|
+
const drawInvSpinners = () => {
|
|
3198
|
+
for (let i = 0; i < invStates.length; i++) {
|
|
3199
|
+
const s = invStates[i];
|
|
3200
|
+
if (s.done || s.failed) continue;
|
|
3201
|
+
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3202
|
+
const num = `${c.dim}${(i + 1).toString().padStart(iColNum - 1)}${c.reset} `;
|
|
3203
|
+
const name = s.name.substring(0, iColName).padEnd(iColName);
|
|
3204
|
+
const items = 'checking'.substring(0, iColItems).padEnd(iColItems);
|
|
3205
|
+
const val = ''.padEnd(iColVal);
|
|
3206
|
+
const tries = s.attempt > 0 ? `try ${s.attempt}/3` : '';
|
|
3207
|
+
process.stdout.write(`\x1b[${i + 2};0H\x1b[2K ${num}${rgb(34, 211, 238)}${spin}${c.reset} ${name} ${c.dim}${items}${c.reset} ${c.dim}${val}${c.reset} ${c.dim}${tries}${c.reset}`);
|
|
3208
|
+
}
|
|
3209
|
+
process.stdout.write(`\x1b[${invStates.length + 3};0H`);
|
|
3095
3210
|
};
|
|
3211
|
+
const invSpinnerInterval = setInterval(drawInvSpinners, 80);
|
|
3212
|
+
|
|
3213
|
+
const finalizeInvLine = (idx, invRes) => {
|
|
3214
|
+
const s = invStates[idx];
|
|
3215
|
+
if (s.done || s.failed) return;
|
|
3216
|
+
if (invRes?.ok) {
|
|
3217
|
+
s.done = true;
|
|
3218
|
+
invDone++;
|
|
3219
|
+
} else {
|
|
3220
|
+
s.failed = true;
|
|
3221
|
+
invFailed++;
|
|
3222
|
+
}
|
|
3096
3223
|
|
|
3097
|
-
|
|
3224
|
+
const num = `${c.dim}${(idx + 1).toString().padStart(iColNum - 1)}${c.reset} `;
|
|
3225
|
+
const name = s.name.substring(0, iColName).padEnd(iColName);
|
|
3226
|
+
const items = `${invRes?.result?.items?.length || 0}`.padEnd(iColItems);
|
|
3227
|
+
const val = invRes?.ok
|
|
3228
|
+
? `${c.green}⏣${(invRes.result?.totalValue || 0).toLocaleString()}${c.reset}`.padEnd(iColVal + 3)
|
|
3229
|
+
: `${c.dim}··${c.reset}`.padEnd(iColVal);
|
|
3230
|
+
const sts = invRes?.ok ? `${rgb(52, 211, 153)}✓${c.reset}` : `${rgb(239, 68, 68)}✗${c.reset}`;
|
|
3231
|
+
|
|
3232
|
+
const line = ` ${num}${sts} ${name} ${items} ${val}`;
|
|
3233
|
+
process.stdout.write(`\x1b[${idx + 2};0H\x1b[2K${line}`);
|
|
3234
|
+
};
|
|
3098
3235
|
|
|
3099
3236
|
await Promise.all(activeWorkers.map(async (w, i) => {
|
|
3100
3237
|
try {
|
|
3101
|
-
const invRes = await w.checkInventory({
|
|
3102
|
-
|
|
3103
|
-
startupProgress: { current: i + 1, total },
|
|
3104
|
-
requireComplete: true,
|
|
3105
|
-
maxAttempts: 3,
|
|
3106
|
-
});
|
|
3107
|
-
if (invRes?.ok) invDone++;
|
|
3108
|
-
else invFailed++;
|
|
3238
|
+
const invRes = await w.checkInventory({ force: true, requireComplete: true, maxAttempts: 3, silent: true });
|
|
3239
|
+
finalizeInvLine(i, invRes);
|
|
3109
3240
|
} catch {
|
|
3110
|
-
|
|
3241
|
+
finalizeInvLine(i, { ok: false });
|
|
3111
3242
|
}
|
|
3112
3243
|
}));
|
|
3113
3244
|
|
|
3114
|
-
clearInterval(
|
|
3115
|
-
process.stdout.write(`\
|
|
3245
|
+
clearInterval(invSpinnerInterval);
|
|
3246
|
+
process.stdout.write(`\x1b[${invStates.length + 3};0H`);
|
|
3116
3247
|
|
|
3117
|
-
// Final summary
|
|
3118
3248
|
if (invFailed > 0) {
|
|
3119
|
-
console.log(` ${rgb(239, 68, 68)}✗${c.reset} ${c.bold}Inventory incomplete${c.reset} ${rgb(52, 211, 153)}${invDone}${c.reset}${c.dim}/${c.reset}${c.white}${
|
|
3249
|
+
console.log(` ${rgb(239, 68, 68)}✗${c.reset} ${c.bold}Inventory incomplete${c.reset} ${rgb(52, 211, 153)}${invDone}${c.reset}${c.dim}/${c.reset}${c.white}${activeWorkers.length}${c.reset} ${c.dim}done, ${rgb(239, 68, 68)}${invFailed} failed${c.reset}`);
|
|
3120
3250
|
log('error', `${c.red}Not starting grind loops — ${invFailed} accounts failed inventory.${c.reset}`);
|
|
3121
3251
|
return;
|
|
3122
3252
|
}
|
|
3123
3253
|
|
|
3124
|
-
console.log(` ${rgb(52, 211, 153)}✓${c.reset} ${c.bold}Inventory complete${c.reset} ${rgb(52, 211, 153)}${invDone}/${
|
|
3254
|
+
console.log(` ${rgb(52, 211, 153)}✓${c.reset} ${c.bold}Inventory complete${c.reset} ${rgb(52, 211, 153)}${invDone}/${activeWorkers.length}${c.reset} ${c.dim}all clear${c.reset}`);
|
|
3125
3255
|
console.log('');
|
|
3126
3256
|
|
|
3127
|
-
// Phase 2.5:
|
|
3128
|
-
|
|
3257
|
+
// ── Phase 2.5: Balance check with inline per-account rendering ─────────────────
|
|
3258
|
+
const balStates = activeWorkers.map((w, i) => ({
|
|
3259
|
+
name: w.username || w.account.label || '?',
|
|
3260
|
+
idx: i,
|
|
3261
|
+
done: false,
|
|
3262
|
+
wallet: 0,
|
|
3263
|
+
bank: 0,
|
|
3264
|
+
worker: w,
|
|
3265
|
+
}));
|
|
3266
|
+
|
|
3267
|
+
const bColNum = 4;
|
|
3268
|
+
const bColName = Math.min(22, Math.max(10, Math.floor(tw2 * 0.22)));
|
|
3269
|
+
const bColWallet = 14;
|
|
3270
|
+
const bColBank = 14;
|
|
3271
|
+
const bColTotal = 14;
|
|
3272
|
+
const bColLs = 4;
|
|
3273
|
+
const bTotalVis = bColNum + bColName + bColWallet + bColBank + bColTotal + bColLs + 12;
|
|
3274
|
+
|
|
3275
|
+
console.log(` ${'─'.repeat(bTotalVis)}`);
|
|
3276
|
+
for (let i = 0; i < balStates.length; i++) {
|
|
3277
|
+
const s = balStates[i];
|
|
3278
|
+
const num = `${c.dim}${(i + 1).toString().padStart(bColNum - 1)}${c.reset} `;
|
|
3279
|
+
const name = s.name.substring(0, bColName).padEnd(bColName);
|
|
3280
|
+
console.log(`${num}${c.dim}··${c.reset} ${name} ${c.dim}${'checking'.padEnd(bColWallet)}${c.reset} ${c.dim}${'··'.padEnd(bColBank)}${c.reset} ${c.dim}${'··'.padEnd(bColTotal)}${c.reset} ${c.dim}♥?${c.reset}`);
|
|
3281
|
+
}
|
|
3282
|
+
console.log(` ${'─'.repeat(bTotalVis)}`);
|
|
3129
3283
|
|
|
3130
3284
|
let balDone = 0;
|
|
3131
|
-
const balProgressInterval = setInterval(() => {
|
|
3132
|
-
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3133
|
-
process.stdout.write(`\r ${rgb(34, 211, 238)}${spin}${c.reset} ${c.dim}Balance...${c.reset} ${c.bold}${rgb(52, 211, 153)}${balDone}${c.reset}${c.dim}/${c.reset}${c.white}${activeWorkers.length}${c.reset} `);
|
|
3134
|
-
}, 80);
|
|
3135
3285
|
|
|
3136
|
-
|
|
3286
|
+
const drawBalSpinners = () => {
|
|
3287
|
+
for (let i = 0; i < balStates.length; i++) {
|
|
3288
|
+
const s = balStates[i];
|
|
3289
|
+
if (s.done) continue;
|
|
3290
|
+
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3291
|
+
const num = `${c.dim}${(i + 1).toString().padStart(bColNum - 1)}${c.reset} `;
|
|
3292
|
+
const name = s.name.substring(0, bColName).padEnd(bColName);
|
|
3293
|
+
process.stdout.write(`\x1b[${i + 2};0H\x1b[2K ${num}${rgb(251, 191, 36)}${spin}${c.reset} ${name} ${c.dim}${'checking'.padEnd(bColWallet)}${c.reset} ${c.dim}${'··'.padEnd(bColBank)}${c.reset} ${c.dim}${'··'.padEnd(bColTotal)}${c.reset} ${c.dim}♥?${c.reset}`);
|
|
3294
|
+
}
|
|
3295
|
+
process.stdout.write(`\x1b[${balStates.length + 3};0H`);
|
|
3296
|
+
};
|
|
3297
|
+
const balSpinnerInterval = setInterval(drawBalSpinners, 80);
|
|
3298
|
+
|
|
3299
|
+
const finalizeBalLine = (idx, w) => {
|
|
3300
|
+
const s = balStates[idx];
|
|
3301
|
+
if (s.done) return;
|
|
3302
|
+
s.done = true;
|
|
3303
|
+
s.wallet = w.stats?.balance || 0;
|
|
3304
|
+
s.bank = w.stats?.bankBalance || 0;
|
|
3305
|
+
balDone++;
|
|
3306
|
+
|
|
3307
|
+
const num = `${c.dim}${(idx + 1).toString().padStart(bColNum - 1)}${c.reset} `;
|
|
3308
|
+
const name = (w.username || s.name || '?').substring(0, bColName).padEnd(bColName);
|
|
3309
|
+
const ls = w._lifesavers ?? '?';
|
|
3310
|
+
const lsColor = ls === 0 ? rgb(239, 68, 68) : ls <= 2 ? rgb(251, 191, 36) : rgb(52, 211, 153);
|
|
3311
|
+
const wallet = `${c.green}⏣${(s.wallet).toLocaleString()}${c.reset}`.padEnd(bColWallet + 3);
|
|
3312
|
+
const bank = `${c.cyan}⏣${(s.bank).toLocaleString()}${c.reset}`.padEnd(bColBank + 3);
|
|
3313
|
+
const total = `${c.bold}⏣${(s.wallet + s.bank).toLocaleString()}${c.reset}`.padEnd(bColTotal + 3);
|
|
3314
|
+
|
|
3315
|
+
const line = ` ${num}${rgb(52, 211, 153)}✓${c.reset} ${name} ${wallet} ${bank} ${total} ${lsColor}♥${ls}${c.reset}`;
|
|
3316
|
+
process.stdout.write(`\x1b[${idx + 2};0H\x1b[2K${line}`);
|
|
3317
|
+
};
|
|
3318
|
+
|
|
3137
3319
|
await Promise.all(activeWorkers.map(async w => {
|
|
3138
3320
|
try {
|
|
3139
|
-
await w.checkBalance();
|
|
3140
|
-
balDone++;
|
|
3321
|
+
await w.checkBalance(true); // silent: don't spam console during inline rendering
|
|
3141
3322
|
} catch {}
|
|
3323
|
+
const idx = balStates.findIndex(s => s.worker === w);
|
|
3324
|
+
if (idx >= 0) finalizeBalLine(idx, w);
|
|
3142
3325
|
}));
|
|
3143
3326
|
|
|
3144
|
-
clearInterval(
|
|
3145
|
-
process.stdout.write(`\
|
|
3327
|
+
clearInterval(balSpinnerInterval);
|
|
3328
|
+
process.stdout.write(`\x1b[${balStates.length + 3};0H`);
|
|
3146
3329
|
|
|
3147
|
-
//
|
|
3330
|
+
// Balance summary
|
|
3148
3331
|
let totalWallet = 0, totalBank = 0, noLifesaverAccounts = [];
|
|
3149
|
-
for (const
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
totalWallet += wallet;
|
|
3154
|
-
totalBank += bank;
|
|
3155
|
-
const lsColor = ls === 0 ? rgb(239, 68, 68) : ls <= 2 ? rgb(251, 191, 36) : rgb(52, 211, 153);
|
|
3156
|
-
console.log(` ${c.dim}├${c.reset} ${c.bold}${w.username}${c.reset} Wallet: ${c.green}⏣ ${wallet.toLocaleString()}${c.reset} Bank: ${c.cyan}⏣ ${bank.toLocaleString()}${c.reset} Total: ${c.bold}⏣ ${(wallet + bank).toLocaleString()}${c.reset} LS: ${lsColor}${ls}${c.reset}`);
|
|
3157
|
-
if (ls === 0) noLifesaverAccounts.push(w.username);
|
|
3332
|
+
for (const s of balStates) {
|
|
3333
|
+
totalWallet += s.wallet;
|
|
3334
|
+
totalBank += s.bank;
|
|
3335
|
+
if (s.worker._lifesavers === 0) noLifesaverAccounts.push(s.name);
|
|
3158
3336
|
}
|
|
3159
3337
|
console.log(` ${rgb(52, 211, 153)}✓${c.reset} ${c.bold}Balance${c.reset} Total: ${c.bold}${c.green}⏣ ${(totalWallet + totalBank).toLocaleString()}${c.reset} ${c.dim}(wallet: ⏣ ${totalWallet.toLocaleString()} + bank: ⏣ ${totalBank.toLocaleString()})${c.reset}`);
|
|
3160
3338
|
|
package/lib/rawLogger.js
CHANGED
|
@@ -152,18 +152,30 @@ function detectCommand(d) {
|
|
|
152
152
|
if (cv2Text.includes('fishing') || cv2Text.includes('fisherfolk')) return 'fish';
|
|
153
153
|
if (cv2Text.includes('deposit') || cv2Text.includes('bank account')) return 'deposit';
|
|
154
154
|
if (cv2Text.includes('begging') || cv2Text.includes('imagine begging')) return 'beg';
|
|
155
|
-
if (cv2Text.includes('hunting') || cv2Text.includes('went hunting') || cv2Text.includes('hunting rifle')) return 'hunt';
|
|
156
|
-
if (cv2Text.includes('digging') || cv2Text.includes('found nothing while') || cv2Text.includes('you dig')) return 'dig';
|
|
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';
|
|
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
158
|
if (cv2Text.includes('weekly')) return 'weekly';
|
|
159
159
|
if (cv2Text.includes('daily')) return 'daily';
|
|
160
160
|
if (cv2Text.includes('inventory')) return 'inventory';
|
|
161
161
|
if (cv2Text.includes('profile') || cv2Text.includes('level:')) return 'profile';
|
|
162
|
+
if (cv2Text.includes('balances') && cv2Text.includes('global rank')) return 'balance';
|
|
163
|
+
|
|
164
|
+
// Check content text (plain message content)
|
|
165
|
+
const contentText = (d.content || '').toLowerCase();
|
|
166
|
+
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';
|
|
169
|
+
if (contentText.includes('you ran an ad for') && contentText.includes('received')) return 'stream';
|
|
170
|
+
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';
|
|
162
172
|
|
|
163
173
|
// Check embed text
|
|
164
174
|
const embedText = extractEmbedText(d.embeds).toLowerCase();
|
|
165
175
|
// Gambling
|
|
166
176
|
if (embedText.includes('high') && embedText.includes('low') && embedText.includes('secret number')) return 'highlow';
|
|
177
|
+
if (embedText.includes('you won') && embedText.includes('your hint was') && embedText.includes('the hidden number was')) return 'highlow';
|
|
178
|
+
if (embedText.includes('you lost') && embedText.includes('your hint was') && embedText.includes('the hidden number was')) return 'highlow';
|
|
167
179
|
if (embedText.includes('blackjack') || embedText.includes('dealer')) return 'blackjack';
|
|
168
180
|
if (embedText.includes('roulette')) return 'roulette';
|
|
169
181
|
if (embedText.includes('spinning') && embedText.includes('slots')) return 'slots';
|
|
@@ -175,10 +187,18 @@ function detectCommand(d) {
|
|
|
175
187
|
if (embedText.includes('what crime do you want')) return 'crime';
|
|
176
188
|
if (embedText.includes('where do you want to search')) return 'search';
|
|
177
189
|
if (embedText.includes('you searched') || embedText.includes('searched the')) return 'search';
|
|
190
|
+
if (embedText.includes('committed') && (embedText.includes('trespassing') || embedText.includes('identity theft') || embedText.includes('fraud') || embedText.includes('shoplifting') || embedText.includes('dui') || embedText.includes('tax evasion') || embedText.includes('littering') || embedText.includes('cyber bullying') || embedText.includes('grand theft auto') || embedText.includes('drug distribution') || embedText.includes('bank robbing') || embedText.includes('arson') || embedText.includes('murder') || embedText.includes('vandalism') || embedText.includes('jaywalking') || embedText.includes('piracy') || embedText.includes('breaking and entering'))) return 'crime';
|
|
178
191
|
if (embedText.includes('you committed') || embedText.includes('went outside')) return 'crime';
|
|
192
|
+
if (embedText.includes('stole a developer') || embedText.includes('got confused about what trespassing')) return 'crime';
|
|
193
|
+
// Search results (person names) - also check for beg results
|
|
194
|
+
if ((embedText.includes('oh you poor soul') || embedText.includes('take this') || embedText.includes('sure take') || embedText.includes('here\'s a thought') || embedText.includes('nope, nothing') || embedText.includes('no u') || embedText.includes('coins? in this economy')) && (embedText.includes('###') || embedText.includes('charlie chaplin') || embedText.includes('shrek') || embedText.includes('elton john') || embedText.includes('alexa') || embedText.includes('confucius') || embedText.includes('doctor strange') || embedText.includes('rick astley') || embedText.includes('toby turner') || embedText.includes('oprah') || embedText.includes('bruce lee') || embedText.includes('david attenborough') || embedText.includes('honey badger'))) {
|
|
195
|
+
// Check if it's a beg result (has life saver or specific beg text)
|
|
196
|
+
if (embedText.includes('life saver') || embedText.includes('lifesaver')) return 'beg';
|
|
197
|
+
return 'search';
|
|
198
|
+
}
|
|
179
199
|
// Hunt / dig
|
|
180
|
-
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('
|
|
181
|
-
if (embedText.includes('digging') || embedText.includes('you dig') || embedText.includes('
|
|
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';
|
|
182
202
|
// Work — match both minigame prompt AND completion
|
|
183
203
|
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';
|
|
184
204
|
if (embedText.includes('you were given') && embedText.includes('shift')) return 'work';
|
|
@@ -187,11 +207,20 @@ function detectCommand(d) {
|
|
|
187
207
|
// Postmemes
|
|
188
208
|
if (embedText.includes('pick a meme') || embedText.includes('meme posting')) return 'postmemes';
|
|
189
209
|
// Stream
|
|
190
|
-
if (embedText.includes('stream manager') || embedText.includes('go live') || embedText.includes('what game do you want to stream')) return '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';
|
|
211
|
+
if (embedText.includes('you can\'t interact with your stream') || embedText.includes('stream can last')) return 'stream';
|
|
191
212
|
// Deposit
|
|
192
213
|
if (embedText.includes('deposited') && embedText.includes('bank balance')) return 'deposit';
|
|
214
|
+
// Balance
|
|
215
|
+
if (embedText.includes('balances') && embedText.includes('global rank') && embedText.includes('net worth')) return 'balance';
|
|
193
216
|
// Trivia
|
|
194
|
-
if (embedText.includes('you have 10 seconds to answer') || embedText.includes('trivia')) return 'trivia';
|
|
217
|
+
if (embedText.includes('you have 10 seconds to answer') || embedText.includes('you have 12 seconds to answer') || embedText.includes('you have 15 seconds to answer') || embedText.includes('trivia') || embedText.includes('difficulty') && embedText.includes('category') && (embedText.includes('correct answer was') || embedText.includes('you got that answer correct'))) return 'trivia';
|
|
218
|
+
if (embedText.includes('who in pulp fiction') || embedText.includes('what was') || embedText.includes('which of')) return 'trivia';
|
|
219
|
+
// Cooldown messages
|
|
220
|
+
if (embedText.includes('you can work again at') || embedText.includes('you can use this command again')) return 'cooldown';
|
|
221
|
+
if (embedText.includes('amount needs to be greater than 0')) return 'cooldown';
|
|
222
|
+
// Premium/upgrade messages
|
|
223
|
+
if (embedText.includes('you can buy the ability to use this command')) return 'premium';
|
|
195
224
|
// Profile / level
|
|
196
225
|
if (embedText.includes('level:') && embedText.includes('experience:')) return 'profile';
|
|
197
226
|
// Shop
|
|
@@ -218,7 +247,15 @@ function parseRawPacket(d, event) {
|
|
|
218
247
|
const embedText = extractEmbedText(d.embeds);
|
|
219
248
|
const isCV2 = !!(d.flags & 32768);
|
|
220
249
|
const isEphemeral = !!(d.flags & 64);
|
|
221
|
-
|
|
250
|
+
|
|
251
|
+
// For UPDATE events, try to preserve the original command classification
|
|
252
|
+
let command = detectCommand(d);
|
|
253
|
+
if (event === 'UPDATE' && command === 'unknown') {
|
|
254
|
+
const existing = memStore.get(d.id);
|
|
255
|
+
if (existing && existing.command && existing.command !== 'unknown') {
|
|
256
|
+
command = existing.command;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
222
259
|
|
|
223
260
|
return {
|
|
224
261
|
id: d.id,
|