dankgrinder 6.25.0 → 6.34.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/farm.js +581 -504
- package/lib/grinder.js +145 -238
- package/package.json +1 -1
package/lib/grinder.js
CHANGED
|
@@ -487,7 +487,7 @@ function renderDashboard() {
|
|
|
487
487
|
lines.push(bEmpty);
|
|
488
488
|
|
|
489
489
|
// ═══════════════════════════════════════════════════════════════
|
|
490
|
-
// STATS PANEL — left: metrics
|
|
490
|
+
// STATS PANEL — left: all metrics | right: big trend + rate
|
|
491
491
|
// ═══════════════════════════════════════════════════════════════
|
|
492
492
|
lines.push(bSep);
|
|
493
493
|
lines.push(bEmpty);
|
|
@@ -497,7 +497,7 @@ function renderDashboard() {
|
|
|
497
497
|
const elapsedHrs = (Date.now() - startTime) / 3_600_000;
|
|
498
498
|
const perHr = elapsedHrs > 0.01 ? Math.round(totalCoins / elapsedHrs) : 0;
|
|
499
499
|
|
|
500
|
-
// ── Compute
|
|
500
|
+
// ── Compute metric values ─────────────────────────────────────
|
|
501
501
|
const cpmVal = globalCmdRate.getRate().toFixed(1);
|
|
502
502
|
const srColor = successRate >= 95 ? G : successRate >= 80 ? Y : R;
|
|
503
503
|
const srBar = progressBar(successRate, 100, 10, successRate >= 95 ? [52, 211, 153] : successRate >= 80 ? [251, 191, 36] : [239, 68, 68]);
|
|
@@ -508,41 +508,33 @@ function renderDashboard() {
|
|
|
508
508
|
const perHrSign = perHr >= 0 ? '+' : '';
|
|
509
509
|
const newHighFlag = isNewHigh ? ` ${R}${c.bold}★ NEW HIGH ★${c.reset}` : '';
|
|
510
510
|
|
|
511
|
-
// ── Big trend sparkline (
|
|
512
|
-
const sparkW = Math.max(
|
|
511
|
+
// ── Big trend sparkline (takes ~40% of inner width) ─────────
|
|
512
|
+
const sparkW = Math.max(28, Math.floor(iw * 0.4));
|
|
513
513
|
const spark = drawSparkline(earningsHistory.toArray(), sparkW);
|
|
514
|
+
const sparkLabel = `${A}~${c.reset} ${D}TREND${c.reset}`;
|
|
514
515
|
|
|
515
|
-
// ──
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const raw = `${icon} ${label} ${val}`;
|
|
521
|
-
const padded = raw + ' '.repeat(Math.max(0, leftW - raw.replace(RE, '').length));
|
|
522
|
-
return padded;
|
|
516
|
+
// ── Left metric rows (each left-aligned, ANSI-aware padding) ─
|
|
517
|
+
// Helper: ANSI-strip-aware pad — strip ANSI then pad the visible content
|
|
518
|
+
const padRow = (content, totalVis) => {
|
|
519
|
+
const vis = content.replace(RE, '').length;
|
|
520
|
+
return content + ' '.repeat(Math.max(0, totalVis - vis));
|
|
523
521
|
};
|
|
524
522
|
|
|
525
|
-
//
|
|
526
|
-
const
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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}`));
|
|
523
|
+
const leftTotal = iw - sparkW - 10; // reserve space for spark + gap
|
|
524
|
+
const lRow1 = `${Au}⟐${c.reset} ${D}BALANCE${c.reset} ${c.bold}${Au}⏣${c.reset} ${formatCoins(totalBalance)}`;
|
|
525
|
+
const lRow2 = `${G}▲${c.reset} ${D}EARNED${c.reset} ${c.bold}${G}${perHrSign}⏣${c.reset} ${formatCoins(totalCoins)}${newHighFlag}`;
|
|
526
|
+
const lRow3 = `${O}★${c.reset} ${D}PEAK${c.reset} ${c.bold}${O}⏣${c.reset} ${formatCoins(sessionPeakCoins)}`;
|
|
527
|
+
const lRow4 = `${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}`;
|
|
528
|
+
const lRow5 = `${D}≡${c.reset} ${D}MEM${c.reset} ${rgb(memCol[0], memCol[1], memCol[2])}${c.bold}${memMB}MB${c.reset} ${memBar}`;
|
|
538
529
|
|
|
539
|
-
//
|
|
540
|
-
const
|
|
541
|
-
lines.push(bRow(` ${cmdsRaw.padEnd(leftW + 4)}${D} │${c.reset}`));
|
|
530
|
+
// Build right column label
|
|
531
|
+
const rRate = `${perHrColor}${perHrSign}⏣${c.reset} ${formatCoins(Math.abs(perHr))}/h`;
|
|
542
532
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
lines.push(bRow(` ${
|
|
533
|
+
lines.push(bRow(` ${padRow(lRow1, leftTotal)} ${sparkLabel} ${spark}`));
|
|
534
|
+
lines.push(bRow(` ${padRow(lRow2, leftTotal)} ${D}────────${c.reset} ${c.dim}earned${c.reset}`));
|
|
535
|
+
lines.push(bRow(` ${padRow(lRow3, leftTotal)} ${D} ${c.reset} ${rRate}`));
|
|
536
|
+
lines.push(bRow(` ${padRow(lRow4, leftTotal)} ${D} ${c.reset}`));
|
|
537
|
+
lines.push(bRow(` ${padRow(lRow5, leftTotal)} ${D} ${c.reset}`));
|
|
546
538
|
|
|
547
539
|
lines.push(bEmpty);
|
|
548
540
|
|
|
@@ -3028,102 +3020,90 @@ async function start(apiKey, apiUrl) {
|
|
|
3028
3020
|
console.log(` ${checks.join(' ')}`);
|
|
3029
3021
|
console.log('');
|
|
3030
3022
|
|
|
3031
|
-
// ──
|
|
3032
|
-
|
|
3023
|
+
// ── Phase 1: Login with per-account inline rendering ─────────────────────────
|
|
3024
|
+
const startupTw = process.stdout.columns || 90;
|
|
3025
|
+
const colNum = 4; // " #"
|
|
3026
|
+
const colSts = 3; // "ST"
|
|
3027
|
+
const colName = Math.min(24, Math.max(12, Math.floor(startupTw * 0.25)));
|
|
3028
|
+
const colGuild = Math.min(18, Math.max(8, Math.floor(startupTw * 0.2)));
|
|
3029
|
+
const colCmds = 8;
|
|
3030
|
+
const loginVis = colNum + colSts + colName + colGuild + colCmds + 10;
|
|
3031
|
+
|
|
3033
3032
|
const loginStates = accounts.map((acc, i) => ({
|
|
3034
3033
|
name: acc.label || acc.id || '?',
|
|
3035
3034
|
done: false,
|
|
3036
3035
|
failed: false,
|
|
3037
3036
|
worker: null,
|
|
3038
|
-
workerIdx: i,
|
|
3039
3037
|
}));
|
|
3040
3038
|
|
|
3041
|
-
|
|
3042
|
-
|
|
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)}`);
|
|
3039
|
+
let loginLines = [];
|
|
3040
|
+
loginLines.push(` ${'─'.repeat(loginVis)}`);
|
|
3052
3041
|
for (let i = 0; i < loginStates.length; i++) {
|
|
3053
3042
|
const s = loginStates[i];
|
|
3054
|
-
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset}
|
|
3043
|
+
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3055
3044
|
const name = s.name.substring(0, colName).padEnd(colName);
|
|
3056
|
-
|
|
3045
|
+
const guild = c.dim + '···'.padEnd(colGuild) + c.reset;
|
|
3046
|
+
const cmds = c.dim + '···'.padEnd(colCmds) + c.reset;
|
|
3047
|
+
loginLines.push(` ${num} ${c.dim}··${c.reset} ${name} ${guild} ${cmds}`);
|
|
3057
3048
|
}
|
|
3058
|
-
|
|
3049
|
+
loginLines.push(` ${'─'.repeat(loginVis)}`);
|
|
3050
|
+
for (const l of loginLines) console.log(l);
|
|
3051
|
+
loginLines = null;
|
|
3059
3052
|
|
|
3060
|
-
|
|
3053
|
+
let loginPending = new Array(accounts.length).fill(true);
|
|
3061
3054
|
const drawLoginSpinners = () => {
|
|
3062
|
-
for (let i = 0; i <
|
|
3063
|
-
|
|
3064
|
-
if (s.done || s.failed) continue;
|
|
3055
|
+
for (let i = 0; i < loginPending.length; i++) {
|
|
3056
|
+
if (!loginPending[i]) continue;
|
|
3065
3057
|
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 =
|
|
3068
|
-
const guild = 'logging in...'.substring(0, colGuild).
|
|
3069
|
-
|
|
3058
|
+
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3059
|
+
const name = loginStates[i].name.substring(0, colName).padEnd(colName);
|
|
3060
|
+
const guild = c.dim + 'logging in...'.substring(0, colGuild) + c.reset;
|
|
3061
|
+
const cmds = c.dim + '···'.padEnd(colCmds) + c.reset;
|
|
3062
|
+
process.stdout.write(`\r\x1b[2K ${num} ${rgb(139, 92, 246)}${spin}${c.reset} ${name} ${guild} ${cmds}\x1b[K`);
|
|
3070
3063
|
}
|
|
3071
|
-
process.stdout.write(`\x1b[${loginStates.length + 3};0H`);
|
|
3072
3064
|
};
|
|
3073
3065
|
const loginSpinnerInterval = setInterval(drawLoginSpinners, 80);
|
|
3074
3066
|
|
|
3075
|
-
// Update a line when an account finishes
|
|
3076
3067
|
const finalizeLoginLine = (idx, worker) => {
|
|
3068
|
+
if (!loginPending[idx]) return;
|
|
3069
|
+
loginPending[idx] = false;
|
|
3077
3070
|
const s = loginStates[idx];
|
|
3078
|
-
if (s.done || s.failed) return;
|
|
3079
3071
|
s.done = true;
|
|
3080
3072
|
s.worker = worker;
|
|
3081
3073
|
|
|
3082
|
-
const num = `${c.dim}${(idx + 1).toString().padStart(colNum - 1)}${c.reset}
|
|
3074
|
+
const num = `${c.dim}${(idx + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3083
3075
|
const name = (worker.username || s.name || '?').substring(0, colName).padEnd(colName);
|
|
3084
|
-
|
|
3085
3076
|
let sts, guild, cmds;
|
|
3086
3077
|
if (worker._tokenInvalid) {
|
|
3087
3078
|
sts = `${rgb(239, 68, 68)}✗${c.reset}`;
|
|
3088
3079
|
guild = 'INVALID'.padEnd(colGuild);
|
|
3089
|
-
cmds = '···';
|
|
3080
|
+
cmds = '···'.padEnd(colCmds);
|
|
3090
3081
|
s.failed = true;
|
|
3091
3082
|
} else if (worker.channel) {
|
|
3092
3083
|
sts = `${rgb(52, 211, 153)}✓${c.reset}`;
|
|
3093
3084
|
const gn = (worker.channel.guild?.name || worker.channel.guild?.id || 'DM').substring(0, colGuild);
|
|
3094
3085
|
guild = gn.padEnd(colGuild);
|
|
3095
|
-
cmds = `${worker.stats?.commands || 0}
|
|
3086
|
+
cmds = `${worker.stats?.commands || 0}`.padEnd(colCmds);
|
|
3096
3087
|
} else {
|
|
3097
3088
|
sts = `${rgb(251, 146, 60)}⏳${c.reset}`;
|
|
3098
3089
|
guild = 'timeout'.padEnd(colGuild);
|
|
3099
|
-
cmds = '···';
|
|
3090
|
+
cmds = '···'.padEnd(colCmds);
|
|
3100
3091
|
}
|
|
3101
|
-
|
|
3102
|
-
const line = ` ${num}${sts} ${name} ${guild} ${cmds}`;
|
|
3103
|
-
process.stdout.write(`\x1b[${idx + 2};0H\x1b[2K${line}`);
|
|
3092
|
+
process.stdout.write(`\r\x1b[2K ${num} ${sts} ${name} ${c.dim}${guild}${c.reset} ${c.dim}${cmds}${c.reset}\x1b[K`);
|
|
3104
3093
|
};
|
|
3105
3094
|
|
|
3106
|
-
// Phase 1: Login in batches of 10
|
|
3107
3095
|
const parsedGapMin = Number.parseInt(String(process.env.LOGIN_GAP_MIN_MS || '50'), 10);
|
|
3108
3096
|
const parsedGapMax = Number.parseInt(String(process.env.LOGIN_GAP_MAX_MS || '150'), 10);
|
|
3109
3097
|
const LOGIN_GAP_MIN_MS = Number.isFinite(parsedGapMin) && parsedGapMin >= 0 ? parsedGapMin : 50;
|
|
3110
|
-
const LOGIN_GAP_MAX_MS = Number.isFinite(parsedGapMax) && parsedGapMax >= LOGIN_GAP_MIN_MS ? parsedGapMax : Math.max(
|
|
3111
|
-
|
|
3112
|
-
const randomLoginGap = () => {
|
|
3113
|
-
if (LOGIN_GAP_MAX_MS <= LOGIN_GAP_MIN_MS) return LOGIN_GAP_MIN_MS;
|
|
3114
|
-
return LOGIN_GAP_MIN_MS + Math.floor(Math.random() * (LOGIN_GAP_MAX_MS - LOGIN_GAP_MIN_MS + 1));
|
|
3115
|
-
};
|
|
3098
|
+
const LOGIN_GAP_MAX_MS = Number.isFinite(parsedGapMax) && parsedGapMax >= LOGIN_GAP_MIN_MS ? parsedGapMax : Math.max(parsedGapMin, 150);
|
|
3099
|
+
const randomLoginGap = () => LOGIN_GAP_MAX_MS <= LOGIN_GAP_MIN_MS ? LOGIN_GAP_MIN_MS : LOGIN_GAP_MIN_MS + Math.floor(Math.random() * (LOGIN_GAP_MAX_MS - LOGIN_GAP_MIN_MS + 1));
|
|
3116
3100
|
|
|
3117
3101
|
const BATCH_SIZE = 10;
|
|
3118
3102
|
for (let i = 0; i < accounts.length; i += BATCH_SIZE) {
|
|
3119
3103
|
if (shutdownCalled) break;
|
|
3120
3104
|
const batch = accounts.slice(i, Math.min(i + BATCH_SIZE, accounts.length));
|
|
3121
|
-
|
|
3122
3105
|
await Promise.all(batch.map(async (acc, idx) => {
|
|
3123
|
-
if (idx > 0)
|
|
3124
|
-
const jitter = 100 + Math.floor(Math.random() * 500);
|
|
3125
|
-
await new Promise(r => setTimeout(r, jitter));
|
|
3126
|
-
}
|
|
3106
|
+
if (idx > 0) await new Promise(r => setTimeout(r, 100 + Math.floor(Math.random() * 500)));
|
|
3127
3107
|
const worker = new AccountWorker(acc, i + idx);
|
|
3128
3108
|
workers.push(worker);
|
|
3129
3109
|
workerMap.set(acc.id, worker);
|
|
@@ -3131,216 +3111,143 @@ async function start(apiKey, apiUrl) {
|
|
|
3131
3111
|
await worker.start();
|
|
3132
3112
|
finalizeLoginLine(i + idx, worker);
|
|
3133
3113
|
}));
|
|
3134
|
-
|
|
3135
|
-
if (i + BATCH_SIZE < accounts.length) {
|
|
3136
|
-
await new Promise(r => setTimeout(r, randomLoginGap()));
|
|
3137
|
-
}
|
|
3138
|
-
|
|
3114
|
+
if (i + BATCH_SIZE < accounts.length) await new Promise(r => setTimeout(r, randomLoginGap()));
|
|
3139
3115
|
hintGC();
|
|
3140
3116
|
}
|
|
3141
3117
|
|
|
3142
3118
|
clearInterval(loginSpinnerInterval);
|
|
3143
|
-
process.stdout.write(`\x1b[${loginStates.length + 3};0H`);
|
|
3144
|
-
|
|
3145
|
-
// Final summary
|
|
3146
3119
|
const loginDone = workers.filter(w => !w._tokenInvalid && w.channel).length;
|
|
3147
3120
|
const invalidWorkers = workers.filter(w => w._tokenInvalid);
|
|
3148
3121
|
const timedOutWorkers = workers.filter(w => !w.channel && !w._tokenInvalid);
|
|
3149
|
-
console.log(
|
|
3122
|
+
console.log(`\r\x1b[2K ${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
3123
|
console.log('');
|
|
3151
|
-
|
|
3152
3124
|
if (invalidWorkers.length > 0) {
|
|
3153
3125
|
log('warn', `${rgb(239, 68, 68)}${invalidWorkers.length} account(s) have INVALID tokens:${c.reset}`);
|
|
3154
|
-
for (const w of invalidWorkers) {
|
|
3155
|
-
log('error', ` ✗ ${w.account.label || w.account.id} — token is invalid or expired`);
|
|
3156
|
-
}
|
|
3126
|
+
for (const w of invalidWorkers) log('error', ` ✗ ${w.account.label || w.account.id} — token is invalid or expired`);
|
|
3157
3127
|
console.log('');
|
|
3158
3128
|
}
|
|
3159
|
-
if (timedOutWorkers.length > 0) {
|
|
3160
|
-
log('warn', `${timedOutWorkers.length} account(s) timed out during login (will retry in background)`);
|
|
3161
|
-
}
|
|
3129
|
+
if (timedOutWorkers.length > 0) log('warn', `${timedOutWorkers.length} account(s) timed out during login (will retry in background)`);
|
|
3162
3130
|
|
|
3163
|
-
// Filter out workers with invalid tokens from grinding
|
|
3164
3131
|
const activeWorkers = workers.filter(w => !w._tokenInvalid);
|
|
3165
3132
|
|
|
3166
|
-
// ── Phase 2: Inventory check
|
|
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;
|
|
3133
|
+
// ── Phase 2: Inventory check — spinner for pending count, results inline ─────────
|
|
3179
3134
|
const iColNum = 4;
|
|
3180
|
-
const iColName = Math.min(22, Math.max(
|
|
3135
|
+
const iColName = Math.min(22, Math.max(12, Math.floor(startupTw * 0.22)));
|
|
3181
3136
|
const iColItems = 8;
|
|
3182
|
-
const iColVal =
|
|
3183
|
-
const
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
const
|
|
3189
|
-
const
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
}
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
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`);
|
|
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
|
-
}
|
|
3223
|
-
|
|
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}`);
|
|
3137
|
+
const iColVal = 16;
|
|
3138
|
+
const invVis = 7 + iColNum + iColName + iColItems + iColVal + 12;
|
|
3139
|
+
|
|
3140
|
+
console.log(` ${'─'.repeat(invVis)}`);
|
|
3141
|
+
for (let i = 0; i < activeWorkers.length; i++) {
|
|
3142
|
+
const w = activeWorkers[i];
|
|
3143
|
+
const num = `${c.dim}${(i + 1).toString().padStart(iColNum - 1)}${c.reset}`;
|
|
3144
|
+
const name = (w.username || w.account.label || '?').substring(0, iColName).padEnd(iColName);
|
|
3145
|
+
console.log(` ${num} ${c.dim}··${c.reset} ${name} ${c.dim}${'checking...'.padEnd(iColItems)}${c.reset} ${c.dim}${'···'.padEnd(iColVal)}${c.reset}`);
|
|
3146
|
+
}
|
|
3147
|
+
console.log(` ${'─'.repeat(invVis)}`);
|
|
3148
|
+
|
|
3149
|
+
let invDone = 0, invFailed = 0, invPending = activeWorkers.length;
|
|
3150
|
+
const drawInvProgress = () => {
|
|
3151
|
+
if (invPending === 0) return;
|
|
3152
|
+
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3153
|
+
const pct = activeWorkers.length > 0 ? ((activeWorkers.length - invPending) / activeWorkers.length) : 0;
|
|
3154
|
+
const barW = Math.min(20, startupTw - 40);
|
|
3155
|
+
const filled = Math.round(pct * barW);
|
|
3156
|
+
const bar = rgb(34, 211, 238) + '█'.repeat(filled) + rgb(50, 50, 70) + '░'.repeat(barW - filled) + c.reset;
|
|
3157
|
+
const pctStr = `${Math.round(pct * 100)}%`;
|
|
3158
|
+
process.stdout.write(`\r\x1b[2K ${rgb(34, 211, 238)}${spin}${c.reset} ${c.dim}Inventory...${c.reset} ${bar} ${c.bold}${rgb(52, 211, 153)}${activeWorkers.length - invPending}${c.reset}${c.dim}/${c.reset}${c.white}${activeWorkers.length}${c.reset} ${c.dim}${pctStr}${c.reset} `);
|
|
3234
3159
|
};
|
|
3160
|
+
const invSpinnerInterval = setInterval(drawInvProgress, 80);
|
|
3235
3161
|
|
|
3236
3162
|
await Promise.all(activeWorkers.map(async (w, i) => {
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3163
|
+
const num = `${c.dim}${(i + 1).toString().padStart(iColNum - 1)}${c.reset}`;
|
|
3164
|
+
const name = (w.username || w.account.label || '?').substring(0, iColName).padEnd(iColName);
|
|
3165
|
+
let invRes;
|
|
3166
|
+
try { invRes = await w.checkInventory({ force: true, requireComplete: true, maxAttempts: 3, silent: true }); }
|
|
3167
|
+
catch { invRes = { ok: false }; }
|
|
3168
|
+
invPending--;
|
|
3169
|
+
const items = invRes?.ok ? (invRes.result?.items?.length || 0) : 0;
|
|
3170
|
+
const val = invRes?.ok ? (invRes.result?.totalValue || 0) : 0;
|
|
3171
|
+
const sts = invRes?.ok ? `${rgb(52, 211, 153)}✓${c.reset}` : `${rgb(239, 68, 68)}✗${c.reset}`;
|
|
3172
|
+
const itemStr = `${items}`.padEnd(iColItems);
|
|
3173
|
+
const valStr = invRes?.ok ? `${c.green}⏣${val.toLocaleString()}${c.reset}` : `${c.dim}···${c.reset}`;
|
|
3174
|
+
process.stdout.write(`\r\x1b[2K ${num} ${sts} ${name} ${itemStr} ${valStr.padEnd(iColVal + 5)}\x1b[K`);
|
|
3175
|
+
if (invRes?.ok) invDone++; else invFailed++;
|
|
3243
3176
|
}));
|
|
3244
3177
|
|
|
3245
3178
|
clearInterval(invSpinnerInterval);
|
|
3246
|
-
process.stdout.write(`\x1b[
|
|
3179
|
+
process.stdout.write(`\r\x1b[2K`);
|
|
3247
3180
|
|
|
3248
3181
|
if (invFailed > 0) {
|
|
3249
3182
|
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}`);
|
|
3250
3183
|
log('error', `${c.red}Not starting grind loops — ${invFailed} accounts failed inventory.${c.reset}`);
|
|
3251
3184
|
return;
|
|
3252
3185
|
}
|
|
3253
|
-
|
|
3254
3186
|
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}`);
|
|
3255
3187
|
console.log('');
|
|
3256
3188
|
|
|
3257
|
-
// ── Phase 2.5: Balance check
|
|
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
|
-
|
|
3189
|
+
// ── Phase 2.5: Balance check — inline table, single spinner for progress ─────────
|
|
3267
3190
|
const bColNum = 4;
|
|
3268
|
-
const bColName = Math.min(22, Math.max(
|
|
3269
|
-
const bColWallet =
|
|
3270
|
-
const bColBank =
|
|
3191
|
+
const bColName = Math.min(22, Math.max(12, Math.floor(startupTw * 0.22)));
|
|
3192
|
+
const bColWallet = 12;
|
|
3193
|
+
const bColBank = 12;
|
|
3271
3194
|
const bColTotal = 14;
|
|
3272
3195
|
const bColLs = 4;
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
console.log(` ${'─'.repeat(
|
|
3276
|
-
for (let i = 0; i <
|
|
3277
|
-
const
|
|
3278
|
-
const num = `${c.dim}${(i + 1).toString().padStart(bColNum - 1)}${c.reset}
|
|
3279
|
-
const name =
|
|
3280
|
-
console.log(
|
|
3281
|
-
}
|
|
3282
|
-
console.log(` ${'─'.repeat(
|
|
3283
|
-
|
|
3284
|
-
let balDone = 0;
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
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`);
|
|
3196
|
+
const balVis = 7 + bColNum + bColName + bColWallet + bColBank + bColTotal + bColLs + 14;
|
|
3197
|
+
|
|
3198
|
+
console.log(` ${'─'.repeat(balVis)}`);
|
|
3199
|
+
for (let i = 0; i < activeWorkers.length; i++) {
|
|
3200
|
+
const w = activeWorkers[i];
|
|
3201
|
+
const num = `${c.dim}${(i + 1).toString().padStart(bColNum - 1)}${c.reset}`;
|
|
3202
|
+
const name = (w.username || w.account.label || '?').substring(0, bColName).padEnd(bColName);
|
|
3203
|
+
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}`);
|
|
3204
|
+
}
|
|
3205
|
+
console.log(` ${'─'.repeat(balVis)}`);
|
|
3206
|
+
|
|
3207
|
+
let balDone = 0, balPending = activeWorkers.length;
|
|
3208
|
+
const drawBalProgress = () => {
|
|
3209
|
+
if (balPending === 0) return;
|
|
3210
|
+
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3211
|
+
const pct = activeWorkers.length > 0 ? ((activeWorkers.length - balPending) / activeWorkers.length) : 0;
|
|
3212
|
+
const barW = Math.min(20, startupTw - 40);
|
|
3213
|
+
const filled = Math.round(pct * barW);
|
|
3214
|
+
const bar = rgb(251, 191, 36) + '█'.repeat(filled) + rgb(50, 50, 70) + '░'.repeat(barW - filled) + c.reset;
|
|
3215
|
+
process.stdout.write(`\r\x1b[2K ${rgb(251, 191, 36)}${spin}${c.reset} ${c.dim}Balance...${c.reset} ${bar} ${c.bold}${rgb(52, 211, 153)}${activeWorkers.length - balPending}${c.reset}${c.dim}/${c.reset}${c.white}${activeWorkers.length}${c.reset} `);
|
|
3296
3216
|
};
|
|
3297
|
-
const balSpinnerInterval = setInterval(
|
|
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++;
|
|
3217
|
+
const balSpinnerInterval = setInterval(drawBalProgress, 80);
|
|
3306
3218
|
|
|
3307
|
-
|
|
3308
|
-
|
|
3219
|
+
await Promise.all(activeWorkers.map(async (w, i) => {
|
|
3220
|
+
try { await w.checkBalance(true); } catch {}
|
|
3221
|
+
balPending--;
|
|
3222
|
+
const num = `${c.dim}${(i + 1).toString().padStart(bColNum - 1)}${c.reset}`;
|
|
3223
|
+
const name = (w.username || w.account.label || '?').substring(0, bColName).padEnd(bColName);
|
|
3224
|
+
const wallet = w.stats?.balance || 0;
|
|
3225
|
+
const bank = w.stats?.bankBalance || 0;
|
|
3309
3226
|
const ls = w._lifesavers ?? '?';
|
|
3310
3227
|
const lsColor = ls === 0 ? rgb(239, 68, 68) : ls <= 2 ? rgb(251, 191, 36) : rgb(52, 211, 153);
|
|
3311
|
-
const
|
|
3312
|
-
const
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
process.stdout.write(`\x1b[${idx + 2};0H\x1b[2K${line}`);
|
|
3317
|
-
};
|
|
3318
|
-
|
|
3319
|
-
await Promise.all(activeWorkers.map(async w => {
|
|
3320
|
-
try {
|
|
3321
|
-
await w.checkBalance(true); // silent: don't spam console during inline rendering
|
|
3322
|
-
} catch {}
|
|
3323
|
-
const idx = balStates.findIndex(s => s.worker === w);
|
|
3324
|
-
if (idx >= 0) finalizeBalLine(idx, w);
|
|
3228
|
+
const walletStr = `${c.green}⏣${wallet.toLocaleString()}${c.reset}`;
|
|
3229
|
+
const bankStr = `${c.cyan}⏣${bank.toLocaleString()}${c.reset}`;
|
|
3230
|
+
const totalStr = `${c.bold}⏣${(wallet + bank).toLocaleString()}${c.reset}`;
|
|
3231
|
+
process.stdout.write(`\r\x1b[2K ${num} ${rgb(52, 211, 153)}✓${c.reset} ${name} ${walletStr.padEnd(bColWallet + 4)} ${bankStr.padEnd(bColBank + 4)} ${totalStr.padEnd(bColTotal + 3)} ${lsColor}♥${ls}${c.reset}\x1b[K`);
|
|
3232
|
+
balDone++;
|
|
3325
3233
|
}));
|
|
3326
3234
|
|
|
3327
3235
|
clearInterval(balSpinnerInterval);
|
|
3328
|
-
process.stdout.write(`\x1b[
|
|
3236
|
+
process.stdout.write(`\r\x1b[2K`);
|
|
3329
3237
|
|
|
3330
|
-
// Balance summary
|
|
3331
3238
|
let totalWallet = 0, totalBank = 0, noLifesaverAccounts = [];
|
|
3332
|
-
for (const
|
|
3333
|
-
totalWallet +=
|
|
3334
|
-
totalBank +=
|
|
3335
|
-
if (
|
|
3239
|
+
for (const w of activeWorkers) {
|
|
3240
|
+
totalWallet += w.stats?.balance || 0;
|
|
3241
|
+
totalBank += w.stats?.bankBalance || 0;
|
|
3242
|
+
if (w._lifesavers === 0) noLifesaverAccounts.push(w.username || w.account.label);
|
|
3336
3243
|
}
|
|
3337
3244
|
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}`);
|
|
3338
|
-
|
|
3339
3245
|
if (noLifesaverAccounts.length > 0) {
|
|
3340
3246
|
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(', ')}`);
|
|
3341
3247
|
}
|
|
3342
3248
|
console.log('');
|
|
3343
3249
|
|
|
3250
|
+
|
|
3344
3251
|
// Phase 2.75: Check DM history for deaths/level-ups (sequential, fast)
|
|
3345
3252
|
console.log(` ${rgb(139, 92, 246)}${BRAILLE_SPIN[0]}${c.reset} ${c.dim}Checking DM history...${c.reset}`);
|
|
3346
3253
|
let dmDeaths = 0, dmLevelUps = 0, dmNoLs = [];
|