dankgrinder 6.8.1 → 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 +173 -30
- 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
|
|
|
@@ -3015,7 +3167,8 @@ async function start(apiKey, apiUrl) {
|
|
|
3015
3167
|
}
|
|
3016
3168
|
|
|
3017
3169
|
const before = workers.length;
|
|
3018
|
-
workers
|
|
3170
|
+
// Keep ALL workers visible — never remove from array (user wants to see gaps)
|
|
3171
|
+
// Only clean up workerMap entries for accounts fully removed from API
|
|
3019
3172
|
if (workers.length !== before) scheduleRender();
|
|
3020
3173
|
} catch {}
|
|
3021
3174
|
}, 10_000);
|
|
@@ -3124,6 +3277,7 @@ function setupKeyboardShortcuts() {
|
|
|
3124
3277
|
|
|
3125
3278
|
// Ctrl+C or q = quit
|
|
3126
3279
|
if (k === '\u0003' || k === 'q') {
|
|
3280
|
+
process.stdout.write(c.show);
|
|
3127
3281
|
console.log(`\n\n ${c.yellow}Shutting down gracefully...${c.reset}`);
|
|
3128
3282
|
process.emit('SIGINT');
|
|
3129
3283
|
return;
|
|
@@ -3131,51 +3285,40 @@ function setupKeyboardShortcuts() {
|
|
|
3131
3285
|
|
|
3132
3286
|
// p = pause all accounts
|
|
3133
3287
|
if (k === 'p') {
|
|
3134
|
-
let
|
|
3135
|
-
workers.forEach(w => { if (w.running && !w.paused) { w.paused = true;
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
console.log(` ${rgb(139, 92, 246)}╰${c.reset}${c.dim}${'─'.repeat(40)}${c.reset}${rgb(139, 92, 246)}╯${c.reset}`);
|
|
3288
|
+
let count = 0;
|
|
3289
|
+
workers.forEach(w => { if (w.running && !w.paused) { w.paused = true; count++; } });
|
|
3290
|
+
recentLogs.push(`>> PAUSED ${count} accounts (press R to resume)`);
|
|
3291
|
+
scheduleRender();
|
|
3139
3292
|
return;
|
|
3140
3293
|
}
|
|
3141
3294
|
|
|
3142
3295
|
// r = resume all accounts
|
|
3143
3296
|
if (k === 'r') {
|
|
3144
|
-
let
|
|
3145
|
-
workers.forEach(w => { if (w.paused) { w.paused = false;
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
console.log(` ${rgb(139, 92, 246)}╰${c.reset}${c.dim}${'─'.repeat(40)}${c.reset}${rgb(139, 92, 246)}╯${c.reset}`);
|
|
3297
|
+
let count = 0;
|
|
3298
|
+
workers.forEach(w => { if (w.paused) { w.paused = false; count++; } });
|
|
3299
|
+
recentLogs.push(`>> RESUMED ${count} accounts`);
|
|
3300
|
+
scheduleRender();
|
|
3149
3301
|
return;
|
|
3150
3302
|
}
|
|
3151
3303
|
|
|
3152
|
-
// s = show status summary
|
|
3304
|
+
// s = show status summary (pushed to log feed)
|
|
3153
3305
|
if (k === 's') {
|
|
3154
3306
|
const active = workers.filter(w => w.running && !w.paused).length;
|
|
3155
3307
|
const paused = workers.filter(w => w.paused).length;
|
|
3156
|
-
const
|
|
3308
|
+
const invalid = workers.filter(w => w._tokenInvalid).length;
|
|
3309
|
+
const offline = workers.filter(w => !w.running && !w._tokenInvalid).length;
|
|
3157
3310
|
const recovering = workers.filter(w => w._recoveryAttempts > 0 && w._errorCooldownUntil > Date.now()).length;
|
|
3158
3311
|
const totalEarn = workers.reduce((s, w) => s + (w.stats.coins || 0), 0);
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.green}● ${active} active${c.reset} ${c.yellow}⏸ ${paused} paused${c.reset} ${c.red}○ ${offline} offline${c.reset} ${c.yellow}↻ ${recovering} recovering${c.reset} ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3163
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.dim}Total earnings:${c.reset} ${rgb(52, 211, 153)}⏣ ${totalEarn.toLocaleString()}${c.reset} ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3164
|
-
console.log(` ${rgb(139, 92, 246)}╚${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╝${c.reset}`);
|
|
3312
|
+
recentLogs.push(`>> STATUS: ${active} active, ${paused} paused, ${invalid} invalid, ${offline} offline, ${recovering} recovering`);
|
|
3313
|
+
recentLogs.push(`>> EARNINGS: +${formatCoins(totalEarn)} this session | BALANCE: ${formatCoins(totalBalance)}`);
|
|
3314
|
+
scheduleRender();
|
|
3165
3315
|
return;
|
|
3166
3316
|
}
|
|
3167
3317
|
|
|
3168
|
-
// ? = show help
|
|
3318
|
+
// ? or h = show help
|
|
3169
3319
|
if (k === '?' || k === 'h') {
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
console.log(` ${rgb(139, 92, 246)}╠${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╣${c.reset}`);
|
|
3173
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}p${c.reset} Pause all accounts ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3174
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}r${c.reset} Resume all accounts ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3175
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}s${c.reset} Show status summary ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3176
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}q${c.reset} Quit gracefully ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3177
|
-
console.log(` ${rgb(139, 92, 246)}║${c.reset} ${c.white}?${c.reset} Show this help ${rgb(139, 92, 246)}║${c.reset}`);
|
|
3178
|
-
console.log(` ${rgb(139, 92, 246)}╚${c.reset}${c.dim}${'═'.repeat(50)}${c.reset}${rgb(139, 92, 246)}╝${c.reset}`);
|
|
3320
|
+
recentLogs.push('>> SHORTCUTS: P=pause R=resume S=status Q=quit ?=help');
|
|
3321
|
+
scheduleRender();
|
|
3179
3322
|
return;
|
|
3180
3323
|
}
|
|
3181
3324
|
});
|