dankgrinder 6.8.2 → 6.14.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/blackjack.js +110 -51
- package/lib/commands/crime.js +8 -0
- package/lib/commands/fish.js +7 -0
- package/lib/commands/gamble.js +70 -54
- package/lib/commands/highlow.js +48 -28
- package/lib/commands/hunt.js +153 -32
- package/lib/commands/profile.js +5 -4
- package/lib/commands/search.js +8 -0
- package/lib/commands/utils.js +24 -8
- package/lib/commands/work.js +92 -4
- package/lib/grinder.js +153 -1
- package/lib/rawLogger.js +541 -0
- package/package.json +1 -1
package/lib/grinder.js
CHANGED
|
@@ -2,6 +2,7 @@ const { Client, Options } = require('discord.js-selfbot-v13');
|
|
|
2
2
|
const Redis = require('ioredis');
|
|
3
3
|
const commands = require('./commands');
|
|
4
4
|
const { setDashboardActive, isCV2, ensureCV2, stripAnsi } = require('./commands/utils');
|
|
5
|
+
const rawLogger = require('./rawLogger');
|
|
5
6
|
const {
|
|
6
7
|
BloomFilter, RingBuffer, TokenBucket, EMA, SlidingWindowCounter,
|
|
7
8
|
AhoCorasick, LRUCache, StringPool, AsyncBatchQueue, JitterBackoff,
|
|
@@ -597,6 +598,14 @@ function renderDashboard() {
|
|
|
597
598
|
balStr = `${D}⏣${' '.repeat(colBal - 3)}-${c.reset}`;
|
|
598
599
|
}
|
|
599
600
|
|
|
601
|
+
// ── Lifesaver indicator ──
|
|
602
|
+
const ls = wk._lifesavers;
|
|
603
|
+
let lsStr;
|
|
604
|
+
if (ls === 0) lsStr = `${R}♥0${c.reset}`;
|
|
605
|
+
else if (ls != null && ls <= 2) lsStr = `${Y}♥${ls}${c.reset}`;
|
|
606
|
+
else if (ls != null) lsStr = `${G}♥${ls}${c.reset}`;
|
|
607
|
+
else lsStr = `${D}♥?${c.reset}`;
|
|
608
|
+
|
|
600
609
|
// ── Earned (fixed visible width) ──
|
|
601
610
|
const earnNum = wk.stats.coins || 0;
|
|
602
611
|
let earnStr;
|
|
@@ -612,7 +621,7 @@ function renderDashboard() {
|
|
|
612
621
|
const earnBarFill = earnNum > 0 ? Math.min(colBar, Math.max(1, Math.floor(Math.log10(earnNum + 1)))) : 0;
|
|
613
622
|
const earnBar = progressBar(earnBarFill, colBar, colBar, [52, 211, 153], [40, 40, 55]);
|
|
614
623
|
|
|
615
|
-
lines.push(bRow(` ${D}${origNum}${c.reset} ${stsIcon} ${medalStr}${nameStr} ${balStr} ${earnStr} ${earnBar} ${actLabel}`));
|
|
624
|
+
lines.push(bRow(` ${D}${origNum}${c.reset} ${stsIcon} ${medalStr}${nameStr} ${balStr} ${lsStr} ${earnStr} ${earnBar} ${actLabel}`));
|
|
616
625
|
}
|
|
617
626
|
|
|
618
627
|
// Overflow summary
|
|
@@ -1400,6 +1409,24 @@ class AccountWorker {
|
|
|
1400
1409
|
// Final result on same line
|
|
1401
1410
|
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}`;
|
|
1402
1411
|
process.stdout.write(`\x1b[2K\r${resultLine}\n`);
|
|
1412
|
+
|
|
1413
|
+
// Extract lifesaver count from inventory and cache in Redis
|
|
1414
|
+
if (result.items && redis) {
|
|
1415
|
+
const lsItem = result.items.find(i =>
|
|
1416
|
+
/life\s*saver/i.test(i.name) || /lifesaver/i.test(i.name)
|
|
1417
|
+
);
|
|
1418
|
+
const lsCount = lsItem ? lsItem.qty : 0;
|
|
1419
|
+
this._lifesavers = lsCount;
|
|
1420
|
+
try {
|
|
1421
|
+
await redis.set(`dkg:lifesavers:${this.account.id}`, String(lsCount), 'EX', 86400);
|
|
1422
|
+
if (lsCount === 0) {
|
|
1423
|
+
await redis.set(`raw:alert:no-lifesaver:${this.channel?.id}`, '1', 'EX', 86400);
|
|
1424
|
+
} else {
|
|
1425
|
+
await redis.del(`raw:alert:no-lifesaver:${this.channel?.id}`);
|
|
1426
|
+
}
|
|
1427
|
+
} catch {}
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1403
1430
|
try {
|
|
1404
1431
|
await fetch(`${API_URL}/api/grinder/inventory`, {
|
|
1405
1432
|
method: 'POST',
|
|
@@ -1629,6 +1656,7 @@ class AccountWorker {
|
|
|
1629
1656
|
balance: wallet,
|
|
1630
1657
|
bank_balance: bank,
|
|
1631
1658
|
total_balance: wallet + bank,
|
|
1659
|
+
lifesavers: this._lifesavers ?? null,
|
|
1632
1660
|
}),
|
|
1633
1661
|
});
|
|
1634
1662
|
} catch { /* silent */ }
|
|
@@ -1660,6 +1688,27 @@ class AccountWorker {
|
|
|
1660
1688
|
if (shutdownCalled || !this.running) return;
|
|
1661
1689
|
this.stats.commands++;
|
|
1662
1690
|
|
|
1691
|
+
// ── Lifesaver protection: skip crime/search if 0 lifesavers ──
|
|
1692
|
+
if (cmdName === 'crime' || cmdName === 'search') {
|
|
1693
|
+
const noLifesaver = await rawLogger.hasNoLifesaverAlert(this.channel?.id);
|
|
1694
|
+
if (noLifesaver) {
|
|
1695
|
+
this.log('warn', `[${cmdName}] SKIPPED — no lifesavers! (death detected in DMs)`);
|
|
1696
|
+
await this.setCooldown(cmdName, 3600); // block for 1 hour
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
// Also check Redis key for lifesaver count
|
|
1700
|
+
if (redis) {
|
|
1701
|
+
try {
|
|
1702
|
+
const lsCount = await redis.get(`dkg:lifesavers:${this.account.id}`);
|
|
1703
|
+
if (lsCount === '0') {
|
|
1704
|
+
this.log('warn', `[${cmdName}] SKIPPED — 0 lifesavers cached`);
|
|
1705
|
+
await this.setCooldown(cmdName, 3600);
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
} catch {}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1663
1712
|
const cmdOpts = {
|
|
1664
1713
|
channel: this.channel,
|
|
1665
1714
|
waitForDankMemer: (timeout) => this.waitForDankMemer(timeout),
|
|
@@ -1763,6 +1812,42 @@ class AccountWorker {
|
|
|
1763
1812
|
return;
|
|
1764
1813
|
}
|
|
1765
1814
|
|
|
1815
|
+
// ── Death / lifesaver detection in command responses ──
|
|
1816
|
+
if (resultLower.includes('you died') || resultLower.includes('lifesaver protected')) {
|
|
1817
|
+
this.log('error', `DEATH DETECTED during ${cmdName}!`);
|
|
1818
|
+
// Check for lifesaver count in the response
|
|
1819
|
+
const lsMatch = result.match(/(\d+)\s*life\s*saver/i);
|
|
1820
|
+
const lsCount = lsMatch ? parseInt(lsMatch[1]) : -1;
|
|
1821
|
+
if (redis) {
|
|
1822
|
+
if (lsCount === 0) {
|
|
1823
|
+
// 0 lifesavers — DISABLE crime/search immediately
|
|
1824
|
+
await redis.set(`dkg:lifesavers:${this.account.id}`, '0', 'EX', 86400);
|
|
1825
|
+
await redis.set(`raw:alert:no-lifesaver:${this.channel?.id}`, '1', 'EX', 86400);
|
|
1826
|
+
this.log('error', `0 LIFESAVERS! Disabling crime/search for this account!`);
|
|
1827
|
+
await this.setCooldown('crime', 86400); // 24h
|
|
1828
|
+
await this.setCooldown('search', 86400);
|
|
1829
|
+
sendWebhook('DEATH ALERT', `**${this.username}** died during \`${cmdName}\`!\n**0 lifesavers remaining!**\nCrime/search disabled for 24h.`, 0xef4444);
|
|
1830
|
+
} else if (lsCount > 0) {
|
|
1831
|
+
await redis.set(`dkg:lifesavers:${this.account.id}`, String(lsCount), 'EX', 86400);
|
|
1832
|
+
this.log('warn', `Lifesaver used! ${lsCount} remaining.`);
|
|
1833
|
+
if (lsCount <= 2) {
|
|
1834
|
+
sendWebhook('LOW LIFESAVERS', `**${this.username}** has only **${lsCount}** lifesaver(s) left!`, 0xfbbf24);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// Died flag from crime/search handler (death detected in the command response)
|
|
1842
|
+
if (cmdResult.died) {
|
|
1843
|
+
this.log('error', `${cmdName} → DIED! Checking lifesaver count...`);
|
|
1844
|
+
// The DM will come separately with the actual death details
|
|
1845
|
+
// For now, be cautious — set a short cooldown and let DM listener handle the rest
|
|
1846
|
+
await this.setCooldown('crime', 300); // 5 min cooldown to check DMs
|
|
1847
|
+
await this.setCooldown('search', 300);
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1766
1851
|
// Premium-only command detection — disable for 24h
|
|
1767
1852
|
if (resultLower.includes('only available on premium') || resultLower.includes('premium') ||
|
|
1768
1853
|
resultLower.includes('buy the ability to use this command')) {
|
|
@@ -2101,6 +2186,8 @@ class AccountWorker {
|
|
|
2101
2186
|
|
|
2102
2187
|
// Set up error/disconnect handlers for auto-recovery
|
|
2103
2188
|
this._attachRecoveryListeners();
|
|
2189
|
+
rawLogger.attachRawLogger(this.client, { channelId: this.account.channel_id });
|
|
2190
|
+
rawLogger.attachDmLogger(this.client);
|
|
2104
2191
|
|
|
2105
2192
|
await this.client.login(this.account.discord_token);
|
|
2106
2193
|
this.channel = await this.client.channels.fetch(this.account.channel_id).catch(() => null);
|
|
@@ -2554,6 +2641,11 @@ class AccountWorker {
|
|
|
2554
2641
|
clearTimeout(timeoutId);
|
|
2555
2642
|
this.username = this.client.user.tag || this.username;
|
|
2556
2643
|
this.avatarUrl = this.client.user.displayAvatarURL?.({ format: 'png', dynamic: true, size: 128 }) || null;
|
|
2644
|
+
|
|
2645
|
+
// Attach raw gateway logger for CV2 component capture
|
|
2646
|
+
rawLogger.attachRawLogger(this.client, { channelId: this.account.channel_id });
|
|
2647
|
+
rawLogger.attachDmLogger(this.client);
|
|
2648
|
+
|
|
2557
2649
|
// Report status non-blocking
|
|
2558
2650
|
fetch(`${API_URL}/api/grinder/status`, {
|
|
2559
2651
|
method: 'POST',
|
|
@@ -2795,6 +2887,27 @@ async function start(apiKey, apiUrl) {
|
|
|
2795
2887
|
const checks = [];
|
|
2796
2888
|
checks.push(`${rgb(52, 211, 153)}✓${c.reset} ${c.white}API${c.reset}`);
|
|
2797
2889
|
if (REDIS_URL) checks.push(redis ? `${rgb(52, 211, 153)}✓${c.reset} ${c.white}Redis${c.reset}` : `${rgb(251, 191, 36)}○${c.reset} ${c.dim}Redis (connecting...)${c.reset}`);
|
|
2890
|
+
|
|
2891
|
+
// Init rawLogger Redis (uses same URL — logs all raw gateway data)
|
|
2892
|
+
if (REDIS_URL) {
|
|
2893
|
+
rawLogger.init(REDIS_URL).catch(() => {});
|
|
2894
|
+
// Listen for DM death events across all accounts
|
|
2895
|
+
rawLogger.onDmEvent((event, raw) => {
|
|
2896
|
+
if (event.type === 'death' && event.lifesaversLeft === 0) {
|
|
2897
|
+
const channelId = raw.channel_id;
|
|
2898
|
+
// Find which worker uses this DM channel and disable their crime/search
|
|
2899
|
+
for (const w of workers) {
|
|
2900
|
+
if (w.client?.user?.dmChannel?.id === channelId || w.channel?.id) {
|
|
2901
|
+
w.log?.('error', `DEATH in DMs! 0 lifesavers — disabling crime/search`);
|
|
2902
|
+
w.setCooldown?.('crime', 86400);
|
|
2903
|
+
w.setCooldown?.('search', 86400);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
sendWebhook?.('DEATH ALERT (DM)', `Account died! **0 lifesavers!**\nCrime/search auto-disabled.`, 0xef4444);
|
|
2907
|
+
}
|
|
2908
|
+
});
|
|
2909
|
+
checks.push(`${rgb(52, 211, 153)}✓${c.reset} ${c.white}RawLog${c.reset}`);
|
|
2910
|
+
}
|
|
2798
2911
|
if (hasZlib) checks.push(`${rgb(52, 211, 153)}✓${c.reset} ${c.white}zlib${c.reset}`);
|
|
2799
2912
|
if (WEBHOOK_URL) checks.push(`${rgb(52, 211, 153)}✓${c.reset} ${c.white}Webhook${c.reset}`);
|
|
2800
2913
|
if (CLUSTER_ENABLED) {
|
|
@@ -2934,6 +3047,45 @@ async function start(apiKey, apiUrl) {
|
|
|
2934
3047
|
|
|
2935
3048
|
console.log(` ${rgb(52, 211, 153)}✓${c.reset} ${c.bold}Inventory complete${c.reset} ${rgb(52, 211, 153)}${invDone}/${total}${c.reset} ${c.dim}all clear${c.reset}`);
|
|
2936
3049
|
console.log('');
|
|
3050
|
+
|
|
3051
|
+
// Phase 2.5: Check balance for ALL accounts + show lifesaver status
|
|
3052
|
+
console.log(` ${rgb(139, 92, 246)}${BRAILLE_SPIN[0]}${c.reset} ${c.dim}Checking balance for ${c.reset}${c.bold}${activeWorkers.length}${c.reset}${c.dim} accounts...${c.reset}`);
|
|
3053
|
+
|
|
3054
|
+
let balDone = 0;
|
|
3055
|
+
const balProgressInterval = setInterval(() => {
|
|
3056
|
+
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3057
|
+
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}${total}${c.reset} `);
|
|
3058
|
+
}, 80);
|
|
3059
|
+
|
|
3060
|
+
await Promise.all(activeWorkers.map(async (w) => {
|
|
3061
|
+
try {
|
|
3062
|
+
await w.checkBalance();
|
|
3063
|
+
} catch {}
|
|
3064
|
+
balDone++;
|
|
3065
|
+
}));
|
|
3066
|
+
|
|
3067
|
+
clearInterval(balProgressInterval);
|
|
3068
|
+
process.stdout.write(`\r${c.clearLine}`);
|
|
3069
|
+
|
|
3070
|
+
// Show balance + lifesaver summary for each account
|
|
3071
|
+
let totalWallet = 0, totalBank = 0, noLifesaverAccounts = [];
|
|
3072
|
+
for (const w of activeWorkers) {
|
|
3073
|
+
const wallet = w.stats?.balance || 0;
|
|
3074
|
+
const bank = w.stats?.bankBalance || 0;
|
|
3075
|
+
const ls = w._lifesavers ?? '?';
|
|
3076
|
+
totalWallet += wallet;
|
|
3077
|
+
totalBank += bank;
|
|
3078
|
+
const lsColor = ls === 0 ? rgb(239, 68, 68) : ls <= 2 ? rgb(251, 191, 36) : rgb(52, 211, 153);
|
|
3079
|
+
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}`);
|
|
3080
|
+
if (ls === 0) noLifesaverAccounts.push(w.username);
|
|
3081
|
+
}
|
|
3082
|
+
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}`);
|
|
3083
|
+
|
|
3084
|
+
if (noLifesaverAccounts.length > 0) {
|
|
3085
|
+
console.log(` ${rgb(239, 68, 68)}⚠${c.reset} ${c.bold}${c.red}WARNING: ${noLifesaverAccounts.length} account(s) have 0 LIFESAVERS!${c.reset} Crime/Search disabled for: ${noLifesaverAccounts.join(', ')}`);
|
|
3086
|
+
}
|
|
3087
|
+
console.log('');
|
|
3088
|
+
|
|
2937
3089
|
console.log(` ${rgb(139, 92, 246)}${c.bold}>>>${c.reset} ${gradientText('Starting grind loops...', [139, 92, 246], [52, 211, 153])}`);
|
|
2938
3090
|
console.log('');
|
|
2939
3091
|
|