dankgrinder 6.42.0 → 6.46.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/inventory.js +1 -1
- package/lib/grinder.js +120 -181
- package/package.json +1 -1
|
@@ -198,7 +198,7 @@ async function runInventory({ channel, waitForDankMemer, client, accountId, redi
|
|
|
198
198
|
LOG.cmd(`${c.white}${c.bold}pls inv${c.reset}`);
|
|
199
199
|
|
|
200
200
|
await channel.send('pls inv');
|
|
201
|
-
let response = await waitForDankMemer(
|
|
201
|
+
let response = await waitForDankMemer(15000);
|
|
202
202
|
|
|
203
203
|
if (!response) {
|
|
204
204
|
LOG.warn('[inv] No response');
|
package/lib/grinder.js
CHANGED
|
@@ -2026,16 +2026,6 @@ class AccountWorker {
|
|
|
2026
2026
|
return;
|
|
2027
2027
|
}
|
|
2028
2028
|
|
|
2029
|
-
// Died flag from crime/search handler (death detected in the command response)
|
|
2030
|
-
if (cmdResult.died) {
|
|
2031
|
-
this.log('error', `${cmdName} → DIED! Checking lifesaver count...`);
|
|
2032
|
-
// The DM will come separately with the actual death details
|
|
2033
|
-
// For now, be cautious — set a short cooldown and let DM listener handle the rest
|
|
2034
|
-
await this.setCooldown('crime', 300); // 5 min cooldown to check DMs
|
|
2035
|
-
await this.setCooldown('search', 300);
|
|
2036
|
-
return;
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
2029
|
// Premium-only command detection — disable permanently
|
|
2040
2030
|
if (resultLower.includes('only available on premium') || resultLower.includes('premium') ||
|
|
2041
2031
|
resultLower.includes('buy the ability to use this command') ||
|
|
@@ -2625,6 +2615,18 @@ class AccountWorker {
|
|
|
2625
2615
|
return;
|
|
2626
2616
|
}
|
|
2627
2617
|
|
|
2618
|
+
// Startup delay: don't send commands for the first 30s after grindLoop() starts.
|
|
2619
|
+
// This prevents flooding Dank Memer during the Phase 2 inventory check which
|
|
2620
|
+
// sends pls inv for all accounts simultaneously after login.
|
|
2621
|
+
if (this._startupDelayUntil && now < this._startupDelayUntil) {
|
|
2622
|
+
const waitMs = this._startupDelayUntil - now;
|
|
2623
|
+
this.setStatus('warming up...');
|
|
2624
|
+
item.nextRunAt = now + waitMs + 1000;
|
|
2625
|
+
if (this.commandQueue) this.commandQueue.push(item);
|
|
2626
|
+
this.tickTimeout = setTimeout(() => this.tick(), waitMs + 1000);
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
|
|
2628
2630
|
this.busy = true;
|
|
2629
2631
|
|
|
2630
2632
|
// ── Run command (with interactive retry) ───────────────────
|
|
@@ -2765,6 +2767,11 @@ class AccountWorker {
|
|
|
2765
2767
|
this.failStreak = 0;
|
|
2766
2768
|
this.cycleCount = 0;
|
|
2767
2769
|
this.lastCommandRun = 0;
|
|
2770
|
+
// Delay first command by 30s to avoid competing with Phase 2 inventory check
|
|
2771
|
+
// which sends pls inv for all accounts simultaneously after login.
|
|
2772
|
+
// Without this, the grind loop floods Dank Memer with commands during the
|
|
2773
|
+
// login surge, triggering rate-limits that cause Phase 2 inventory to fail.
|
|
2774
|
+
this._startupDelayUntil = Date.now() + 30000;
|
|
2768
2775
|
await this._loadLearnedCooldowns();
|
|
2769
2776
|
this.commandQueue = await this.buildCommandQueue();
|
|
2770
2777
|
this.lastHealthCheck = Date.now();
|
|
@@ -3165,19 +3172,38 @@ async function start(apiKey, apiUrl) {
|
|
|
3165
3172
|
// Init rawLogger Redis (uses same URL — logs all raw gateway data)
|
|
3166
3173
|
if (REDIS_URL) {
|
|
3167
3174
|
rawLogger.init(REDIS_URL).catch(() => {});
|
|
3168
|
-
//
|
|
3175
|
+
// Live DM listener: detect deaths and level-ups in real-time across all accounts
|
|
3169
3176
|
rawLogger.onDmEvent((event, raw) => {
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3177
|
+
const dmChannelId = raw.channel_id;
|
|
3178
|
+
|
|
3179
|
+
// Find which worker owns this DM channel
|
|
3180
|
+
const worker = workers.find(w => w._dmChannelId === dmChannelId);
|
|
3181
|
+
if (!worker) return;
|
|
3182
|
+
|
|
3183
|
+
if (event.type === 'death') {
|
|
3184
|
+
const lsLeft = event.lifesaversLeft;
|
|
3185
|
+
|
|
3186
|
+
if (lsLeft === 0) {
|
|
3187
|
+
// 0 lifesavers — disable crime/search immediately
|
|
3188
|
+
worker.log?.('error', `DEATH in DMs! 0 lifesavers — disabling crime/search`);
|
|
3189
|
+
worker.setCooldown?.('crime', 86400);
|
|
3190
|
+
worker.setCooldown?.('search', 86400);
|
|
3191
|
+
worker._lifesavers = 0;
|
|
3192
|
+
sendWebhook?.('DEATH ALERT (DM)', `**${worker.username}** died! **0 lifesavers!**\nCrime/search auto-disabled.`, 0xef4444);
|
|
3193
|
+
} else if (lsLeft > 0) {
|
|
3194
|
+
// Lifesaver(s) used — update count in real-time
|
|
3195
|
+
worker._lifesavers = lsLeft;
|
|
3196
|
+
worker.log?.('warn', `Lifesaver used! ${lsLeft} remaining.`);
|
|
3197
|
+
if (lsLeft <= 2) {
|
|
3198
|
+
sendWebhook?.('LOW LIFESAVERS', `**${worker.username}** died! Only **${lsLeft}** lifesaver(s) left!`, 0xfbbf24);
|
|
3178
3199
|
}
|
|
3179
3200
|
}
|
|
3180
|
-
|
|
3201
|
+
} else if (event.type === 'levelup') {
|
|
3202
|
+
// Level up — update in-memory level
|
|
3203
|
+
if (event.to > 0) {
|
|
3204
|
+
worker._level = event.to;
|
|
3205
|
+
worker.log?.('info', `Level up! Now level ${event.to}.`);
|
|
3206
|
+
}
|
|
3181
3207
|
}
|
|
3182
3208
|
});
|
|
3183
3209
|
checks.push(`${rgb(52, 211, 153)}✓${c.reset} ${c.white}RawLog${c.reset}`);
|
|
@@ -3191,39 +3217,21 @@ async function start(apiKey, apiUrl) {
|
|
|
3191
3217
|
console.log(` ${checks.join(' ')}`);
|
|
3192
3218
|
console.log('');
|
|
3193
3219
|
|
|
3194
|
-
// ── Phase 1: Login with per-
|
|
3220
|
+
// ── Phase 1: Login — inline table with per-row updates ─────────
|
|
3195
3221
|
const startupTw = process.stdout.columns || 90;
|
|
3196
|
-
const colNum = 4;
|
|
3197
|
-
const colSts = 3;
|
|
3222
|
+
const colNum = 4;
|
|
3223
|
+
const colSts = 3;
|
|
3198
3224
|
const colName = Math.min(24, Math.max(12, Math.floor(startupTw * 0.25)));
|
|
3199
3225
|
const colGuild = Math.min(18, Math.max(8, Math.floor(startupTw * 0.2)));
|
|
3200
3226
|
const colCmds = 8;
|
|
3201
3227
|
const loginVis = colNum + colSts + colName + colGuild + colCmds + 10;
|
|
3202
3228
|
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
let loginLines = [];
|
|
3211
|
-
loginLines.push(` ${'─'.repeat(loginVis)}`);
|
|
3212
|
-
for (let i = 0; i < loginStates.length; i++) {
|
|
3213
|
-
const s = loginStates[i];
|
|
3214
|
-
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3215
|
-
const name = s.name.substring(0, colName).padEnd(colName);
|
|
3216
|
-
const guild = c.dim + '···'.padEnd(colGuild) + c.reset;
|
|
3217
|
-
const cmds = c.dim + '···'.padEnd(colCmds) + c.reset;
|
|
3218
|
-
loginLines.push(` ${num} ${c.dim}··${c.reset} ${name} ${guild} ${cmds}`);
|
|
3219
|
-
}
|
|
3220
|
-
loginLines.push(` ${'─'.repeat(loginVis)}`);
|
|
3221
|
-
for (const l of loginLines) console.log(l);
|
|
3222
|
-
|
|
3223
|
-
// Dynamically capture the starting row of the login table via DSR
|
|
3224
|
-
let loginBaseRow = 1;
|
|
3225
|
-
const captureLoginRow = () => new Promise(resolve => {
|
|
3226
|
-
process.stdout.write(MARKER);
|
|
3229
|
+
// Use DSR to find starting row, then use explicit row numbers for all table writes.
|
|
3230
|
+
// This avoids relying on cursor tracking via \n which varies by terminal.
|
|
3231
|
+
let tableTopRow = 1;
|
|
3232
|
+
let pendingSet = new Set(Array.from({ length: accounts.length }, (_, i) => i));
|
|
3233
|
+
const captureTopRow = () => new Promise(resolve => {
|
|
3234
|
+
process.stdout.write('\x1b[6n');
|
|
3227
3235
|
const chunks = [];
|
|
3228
3236
|
const handler = (chunk) => {
|
|
3229
3237
|
chunks.push(chunk);
|
|
@@ -3231,51 +3239,52 @@ async function start(apiKey, apiUrl) {
|
|
|
3231
3239
|
const m = raw.match(/\x1b\[(\d+);\d+R/);
|
|
3232
3240
|
if (m) {
|
|
3233
3241
|
process.stdin.removeListener('data', handler);
|
|
3234
|
-
|
|
3242
|
+
tableTopRow = parseInt(m[1], 10);
|
|
3235
3243
|
resolve();
|
|
3236
3244
|
}
|
|
3237
3245
|
};
|
|
3238
3246
|
process.stdin.on('data', handler);
|
|
3239
3247
|
setTimeout(resolve, 50);
|
|
3240
3248
|
});
|
|
3241
|
-
await
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
const
|
|
3249
|
+
await captureTopRow();
|
|
3250
|
+
|
|
3251
|
+
// Absolute row numbers for all table elements (calculated from captured top row)
|
|
3252
|
+
const borderTopRow = tableTopRow; // border
|
|
3253
|
+
const dataStartRow = tableTopRow + 1; // first account row
|
|
3254
|
+
const borderBotRow = tableTopRow + accounts.length + 1; // bottom border
|
|
3255
|
+
const bottomRow = borderBotRow + 1; // cursor final position after table
|
|
3256
|
+
|
|
3257
|
+
// Print initial table using explicit row positioning
|
|
3258
|
+
process.stdout.write(`\x1b[${borderTopRow};1H ${'─'.repeat(loginVis)}`);
|
|
3259
|
+
for (let i = 0; i < accounts.length; i++) {
|
|
3260
|
+
const row = dataStartRow + i;
|
|
3261
|
+
const name = (accounts[i].label || accounts[i].id || '?').substring(0, colName).padEnd(colName);
|
|
3262
|
+
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3263
|
+
process.stdout.write(`\x1b[${row};1H ${num} ${c.dim}··${c.reset} ${name} ${c.dim}${'···'.padEnd(colGuild)}${c.reset} ${c.dim}${'···'.padEnd(colCmds)}${c.reset}\x1b[K`);
|
|
3264
|
+
}
|
|
3265
|
+
process.stdout.write(`\x1b[${borderBotRow};1H ${'─'.repeat(loginVis)}\x1b[K`);
|
|
3245
3266
|
|
|
3267
|
+
// Spinner: updates rows inline using absolute row numbers
|
|
3246
3268
|
const drawLoginSpinners = () => {
|
|
3247
|
-
for (
|
|
3248
|
-
if (!loginPending[i]) continue;
|
|
3269
|
+
for (const i of pendingSet) {
|
|
3249
3270
|
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3271
|
+
const name = (accounts[i].label || accounts[i].id || '?').substring(0, colName).padEnd(colName);
|
|
3250
3272
|
const num = `${c.dim}${(i + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
const cmds = c.dim + '···'.padEnd(colCmds) + c.reset;
|
|
3254
|
-
const row = loginBaseRow + 1 + i; // +1 skips the top border line
|
|
3255
|
-
moveToRow(row);
|
|
3256
|
-
process.stdout.write(` ${num} ${rgb(139, 92, 246)}${spin}${c.reset} ${name} ${guild} ${cmds}\x1b[K`);
|
|
3257
|
-
}
|
|
3258
|
-
// Move cursor back to bottom to avoid overwriting the bottom border
|
|
3259
|
-
const lastRow = loginBaseRow + 1 + accounts.length + 1;
|
|
3260
|
-
moveToRow(lastRow);
|
|
3273
|
+
process.stdout.write(`\x1b[${dataStartRow + i};1H ${num} ${rgb(139, 92, 246)}${spin}${c.reset} ${name} ${c.dim}${'logging in...'.substring(0, colGuild)}${c.reset} ${c.dim}${'···'.padEnd(colCmds)}${c.reset}\x1b[K`);
|
|
3274
|
+
}
|
|
3261
3275
|
};
|
|
3262
|
-
const
|
|
3263
|
-
|
|
3264
|
-
const finalizeLoginLine = (idx, worker) => {
|
|
3265
|
-
if (!loginPending[idx]) return;
|
|
3266
|
-
loginPending[idx] = false;
|
|
3267
|
-
const s = loginStates[idx];
|
|
3268
|
-
s.done = true;
|
|
3269
|
-
s.worker = worker;
|
|
3276
|
+
const loginSpinner = setInterval(drawLoginSpinners, 80);
|
|
3270
3277
|
|
|
3278
|
+
const finalizeLoginRow = (idx, worker) => {
|
|
3279
|
+
if (!pendingSet.has(idx)) return;
|
|
3280
|
+
pendingSet.delete(idx);
|
|
3271
3281
|
const num = `${c.dim}${(idx + 1).toString().padStart(colNum - 1)}${c.reset}`;
|
|
3272
|
-
const name = (worker.username ||
|
|
3282
|
+
const name = (worker.username || accounts[idx].label || accounts[idx].id || '?').substring(0, colName).padEnd(colName);
|
|
3273
3283
|
let sts, guild, cmds;
|
|
3274
3284
|
if (worker._tokenInvalid) {
|
|
3275
3285
|
sts = `${rgb(239, 68, 68)}✗${c.reset}`;
|
|
3276
3286
|
guild = 'INVALID'.padEnd(colGuild);
|
|
3277
3287
|
cmds = '···'.padEnd(colCmds);
|
|
3278
|
-
s.failed = true;
|
|
3279
3288
|
} else if (worker.channel) {
|
|
3280
3289
|
sts = `${rgb(52, 211, 153)}✓${c.reset}`;
|
|
3281
3290
|
const gn = (worker.channel.guild?.name || worker.channel.guild?.id || 'DM').substring(0, colGuild);
|
|
@@ -3286,9 +3295,7 @@ async function start(apiKey, apiUrl) {
|
|
|
3286
3295
|
guild = 'timeout'.padEnd(colGuild);
|
|
3287
3296
|
cmds = '···'.padEnd(colCmds);
|
|
3288
3297
|
}
|
|
3289
|
-
|
|
3290
|
-
moveToRow(row);
|
|
3291
|
-
process.stdout.write(` ${num} ${sts} ${name} ${c.dim}${guild}${c.reset} ${c.dim}${cmds}${c.reset}\x1b[K`);
|
|
3298
|
+
process.stdout.write(`\x1b[${dataStartRow + idx};1H ${num} ${sts} ${name} ${c.dim}${guild}${c.reset} ${c.dim}${cmds}${c.reset}\x1b[K`);
|
|
3292
3299
|
};
|
|
3293
3300
|
|
|
3294
3301
|
const parsedGapMin = Number.parseInt(String(process.env.LOGIN_GAP_MIN_MS || '50'), 10);
|
|
@@ -3306,58 +3313,34 @@ async function start(apiKey, apiUrl) {
|
|
|
3306
3313
|
const worker = new AccountWorker(acc, i + idx);
|
|
3307
3314
|
workers.push(worker);
|
|
3308
3315
|
workerMap.set(acc.id, worker);
|
|
3309
|
-
loginStates[i + idx].worker = worker;
|
|
3310
3316
|
await worker.start();
|
|
3311
|
-
|
|
3317
|
+
finalizeLoginRow(i + idx, worker);
|
|
3312
3318
|
}));
|
|
3313
3319
|
if (i + BATCH_SIZE < accounts.length) await new Promise(r => setTimeout(r, randomLoginGap()));
|
|
3314
3320
|
hintGC();
|
|
3315
3321
|
}
|
|
3316
3322
|
|
|
3317
|
-
clearInterval(
|
|
3318
|
-
const loginDone = workers.filter(w => !w._tokenInvalid && w.channel).length;
|
|
3323
|
+
clearInterval(loginSpinner);
|
|
3319
3324
|
const invalidWorkers = workers.filter(w => w._tokenInvalid);
|
|
3320
3325
|
const timedOutWorkers = workers.filter(w => !w.channel && !w._tokenInvalid);
|
|
3321
|
-
|
|
3322
|
-
|
|
3326
|
+
const activeWorkers = workers.filter(w => !w._tokenInvalid);
|
|
3327
|
+
const loginDone = activeWorkers.filter(w => w.channel).length;
|
|
3328
|
+
// Clear bottom border row, move to new line, print login complete
|
|
3329
|
+
process.stdout.write(`\x1b[${borderBotRow};1H\x1b[2K\n ${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}\n`);
|
|
3323
3330
|
if (invalidWorkers.length > 0) {
|
|
3324
|
-
log(
|
|
3325
|
-
for (const w of invalidWorkers) log('error', ` ✗ ${w.account.label || w.account.id} — token is invalid or expired`);
|
|
3326
|
-
console.log('');
|
|
3331
|
+
console.log(` ${rgb(239, 68, 68)}✗${c.reset} ${c.bold}${c.red}${invalidWorkers.length} INVALID token(s):${c.reset} ${invalidWorkers.map(w => w.account.label || w.account.id).join(', ')}`);
|
|
3327
3332
|
}
|
|
3328
3333
|
if (timedOutWorkers.length > 0) log('warn', `${timedOutWorkers.length} account(s) timed out during login (will retry in background)`);
|
|
3334
|
+
console.log('');
|
|
3329
3335
|
|
|
3330
|
-
|
|
3336
|
+
// ── Phase 2: Inventory check — clean sequential table ─────────
|
|
3331
3337
|
|
|
3332
|
-
// ── Phase 2: Inventory check — spinner for pending count, results inline ─────────
|
|
3333
3338
|
const iColNum = 4;
|
|
3334
3339
|
const iColName = Math.min(22, Math.max(12, Math.floor(startupTw * 0.22)));
|
|
3335
3340
|
const iColItems = 8;
|
|
3336
3341
|
const iColVal = 16;
|
|
3337
3342
|
const invVis = 7 + iColNum + iColName + iColItems + iColVal + 12;
|
|
3338
3343
|
|
|
3339
|
-
// Print a unique marker, query its position, then overwrite it with the table
|
|
3340
|
-
process.stdout.write(MARKER);
|
|
3341
|
-
let invBaseRow = 1;
|
|
3342
|
-
const captureRow = () => new Promise(resolve => {
|
|
3343
|
-
const chunks = [];
|
|
3344
|
-
const handler = (chunk) => {
|
|
3345
|
-
chunks.push(chunk);
|
|
3346
|
-
const raw = chunks.join('');
|
|
3347
|
-
const m = raw.match(/\x1b\[(\d+);\d+R/);
|
|
3348
|
-
if (m) {
|
|
3349
|
-
process.stdin.removeListener('data', handler);
|
|
3350
|
-
invBaseRow = parseInt(m[1], 10) + 1; // +1: first account row is after marker
|
|
3351
|
-
resolve();
|
|
3352
|
-
}
|
|
3353
|
-
};
|
|
3354
|
-
process.stdin.on('data', handler);
|
|
3355
|
-
setTimeout(resolve, 50);
|
|
3356
|
-
});
|
|
3357
|
-
await captureRow();
|
|
3358
|
-
|
|
3359
|
-
// Now print the inventory table starting at invBaseRow
|
|
3360
|
-
const invMoveToRow = (row) => process.stdout.write(`\x1b[${row};1H`);
|
|
3361
3344
|
console.log(` ${'─'.repeat(invVis)}`);
|
|
3362
3345
|
for (let i = 0; i < activeWorkers.length; i++) {
|
|
3363
3346
|
const w = activeWorkers[i];
|
|
@@ -3367,40 +3350,31 @@ async function start(apiKey, apiUrl) {
|
|
|
3367
3350
|
}
|
|
3368
3351
|
console.log(` ${'─'.repeat(invVis)}`);
|
|
3369
3352
|
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
const bar = rgb(34, 211, 238) + '█'.repeat(filled) + rgb(50, 50, 70) + '░'.repeat(barW - filled) + c.reset;
|
|
3378
|
-
const pctStr = `${Math.round(pct * 100)}%`;
|
|
3379
|
-
invMoveToRow(invBaseRow);
|
|
3380
|
-
process.stdout.write(` ${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} \x1b[K`);
|
|
3381
|
-
};
|
|
3382
|
-
const invSpinnerInterval = setInterval(drawInvProgress, 80);
|
|
3353
|
+
const invResults = await Promise.all(activeWorkers.map(async (w, i) => {
|
|
3354
|
+
try {
|
|
3355
|
+
return await w.checkInventory({ force: true, requireComplete: true, maxAttempts: 5, silent: true });
|
|
3356
|
+
} catch {
|
|
3357
|
+
return { ok: false };
|
|
3358
|
+
}
|
|
3359
|
+
}));
|
|
3383
3360
|
|
|
3384
|
-
|
|
3361
|
+
let invDone = 0, invFailed = 0;
|
|
3362
|
+
// Re-print table with results
|
|
3363
|
+
console.log(` ${'─'.repeat(invVis)}`);
|
|
3364
|
+
for (let i = 0; i < activeWorkers.length; i++) {
|
|
3365
|
+
const invRes = invResults[i] || { ok: false };
|
|
3366
|
+
const w = activeWorkers[i];
|
|
3385
3367
|
const num = `${c.dim}${(i + 1).toString().padStart(iColNum - 1)}${c.reset}`;
|
|
3386
3368
|
const name = (w.username || w.account.label || '?').substring(0, iColName).padEnd(iColName);
|
|
3387
|
-
let invRes;
|
|
3388
|
-
try { invRes = await w.checkInventory({ force: true, requireComplete: true, maxAttempts: 3, silent: true }); }
|
|
3389
|
-
catch { invRes = { ok: false }; }
|
|
3390
|
-
invPending--;
|
|
3391
3369
|
const items = invRes?.ok ? (invRes.result?.items?.length || 0) : 0;
|
|
3392
3370
|
const val = invRes?.ok ? (invRes.result?.totalValue || 0) : 0;
|
|
3393
3371
|
const sts = invRes?.ok ? `${rgb(52, 211, 153)}✓${c.reset}` : `${rgb(239, 68, 68)}✗${c.reset}`;
|
|
3394
3372
|
const itemStr = `${items}`.padEnd(iColItems);
|
|
3395
3373
|
const valStr = invRes?.ok ? `${c.green}⏣${val.toLocaleString()}${c.reset}` : `${c.dim}···${c.reset}`;
|
|
3396
|
-
|
|
3397
|
-
invMoveToRow(row);
|
|
3398
|
-
process.stdout.write(` ${num} ${sts} ${name} ${itemStr} ${valStr.padEnd(iColVal + 5)}\x1b[K`);
|
|
3374
|
+
console.log(` ${num} ${sts} ${name} ${itemStr} ${valStr.padEnd(iColVal + 5)}`);
|
|
3399
3375
|
if (invRes?.ok) invDone++; else invFailed++;
|
|
3400
|
-
}
|
|
3401
|
-
|
|
3402
|
-
clearInterval(invSpinnerInterval);
|
|
3403
|
-
process.stdout.write(`\r\x1b[2K`);
|
|
3376
|
+
}
|
|
3377
|
+
console.log(` ${'─'.repeat(invVis)}`);
|
|
3404
3378
|
|
|
3405
3379
|
if (invFailed > 0) {
|
|
3406
3380
|
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}`);
|
|
@@ -3410,7 +3384,7 @@ async function start(apiKey, apiUrl) {
|
|
|
3410
3384
|
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}`);
|
|
3411
3385
|
console.log('');
|
|
3412
3386
|
|
|
3413
|
-
// ── Phase 2.5: Balance check —
|
|
3387
|
+
// ── Phase 2.5: Balance check — clean sequential table ─────────
|
|
3414
3388
|
const bColNum = 4;
|
|
3415
3389
|
const bColName = Math.min(22, Math.max(12, Math.floor(startupTw * 0.22)));
|
|
3416
3390
|
const bColWallet = 12;
|
|
@@ -3419,27 +3393,6 @@ async function start(apiKey, apiUrl) {
|
|
|
3419
3393
|
const bColLs = 4;
|
|
3420
3394
|
const balVis = 7 + bColNum + bColName + bColWallet + bColBank + bColTotal + bColLs + 14;
|
|
3421
3395
|
|
|
3422
|
-
// Capture starting row for balance phase
|
|
3423
|
-
process.stdout.write(MARKER);
|
|
3424
|
-
let balBaseRow = 1;
|
|
3425
|
-
const balCaptureRow = () => new Promise(resolve => {
|
|
3426
|
-
const chunks = [];
|
|
3427
|
-
const handler = (chunk) => {
|
|
3428
|
-
chunks.push(chunk);
|
|
3429
|
-
const raw = chunks.join('');
|
|
3430
|
-
const m = raw.match(/\x1b\[(\d+);\d+R/);
|
|
3431
|
-
if (m) {
|
|
3432
|
-
process.stdin.removeListener('data', handler);
|
|
3433
|
-
balBaseRow = parseInt(m[1], 10) + 1;
|
|
3434
|
-
resolve();
|
|
3435
|
-
}
|
|
3436
|
-
};
|
|
3437
|
-
process.stdin.on('data', handler);
|
|
3438
|
-
setTimeout(resolve, 50);
|
|
3439
|
-
});
|
|
3440
|
-
await balCaptureRow();
|
|
3441
|
-
|
|
3442
|
-
const balMoveToRow = (row) => process.stdout.write(`\x1b[${row};1H`);
|
|
3443
3396
|
console.log(` ${'─'.repeat(balVis)}`);
|
|
3444
3397
|
for (let i = 0; i < activeWorkers.length; i++) {
|
|
3445
3398
|
const w = activeWorkers[i];
|
|
@@ -3449,22 +3402,14 @@ async function start(apiKey, apiUrl) {
|
|
|
3449
3402
|
}
|
|
3450
3403
|
console.log(` ${'─'.repeat(balVis)}`);
|
|
3451
3404
|
|
|
3452
|
-
|
|
3453
|
-
const drawBalProgress = () => {
|
|
3454
|
-
if (balPending === 0) return;
|
|
3455
|
-
const spin = BRAILLE_SPIN[Math.floor(Date.now() / 80) % BRAILLE_SPIN.length];
|
|
3456
|
-
const pct = activeWorkers.length > 0 ? ((activeWorkers.length - balPending) / activeWorkers.length) : 0;
|
|
3457
|
-
const barW = Math.min(20, startupTw - 40);
|
|
3458
|
-
const filled = Math.round(pct * barW);
|
|
3459
|
-
const bar = rgb(251, 191, 36) + '█'.repeat(filled) + rgb(50, 50, 70) + '░'.repeat(barW - filled) + c.reset;
|
|
3460
|
-
balMoveToRow(balBaseRow);
|
|
3461
|
-
process.stdout.write(` ${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} \x1b[K`);
|
|
3462
|
-
};
|
|
3463
|
-
const balSpinnerInterval = setInterval(drawBalProgress, 80);
|
|
3464
|
-
|
|
3465
|
-
await Promise.all(activeWorkers.map(async (w, i) => {
|
|
3405
|
+
await Promise.all(activeWorkers.map(async (w) => {
|
|
3466
3406
|
try { await w.checkBalance(true); } catch {}
|
|
3467
|
-
|
|
3407
|
+
}));
|
|
3408
|
+
|
|
3409
|
+
// Re-print table with results
|
|
3410
|
+
console.log(` ${'─'.repeat(balVis)}`);
|
|
3411
|
+
for (let i = 0; i < activeWorkers.length; i++) {
|
|
3412
|
+
const w = activeWorkers[i];
|
|
3468
3413
|
const num = `${c.dim}${(i + 1).toString().padStart(bColNum - 1)}${c.reset}`;
|
|
3469
3414
|
const name = (w.username || w.account.label || '?').substring(0, bColName).padEnd(bColName);
|
|
3470
3415
|
const wallet = w.stats?.balance || 0;
|
|
@@ -3474,14 +3419,9 @@ async function start(apiKey, apiUrl) {
|
|
|
3474
3419
|
const walletStr = `${c.green}⏣${wallet.toLocaleString()}${c.reset}`;
|
|
3475
3420
|
const bankStr = `${c.cyan}⏣${bank.toLocaleString()}${c.reset}`;
|
|
3476
3421
|
const totalStr = `${c.bold}⏣${(wallet + bank).toLocaleString()}${c.reset}`;
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
balDone++;
|
|
3481
|
-
}));
|
|
3482
|
-
|
|
3483
|
-
clearInterval(balSpinnerInterval);
|
|
3484
|
-
process.stdout.write(`\r\x1b[2K`);
|
|
3422
|
+
console.log(` ${num} ${rgb(52, 211, 153)}✓${c.reset} ${name} ${walletStr.padEnd(bColWallet + 4)} ${bankStr.padEnd(bColBank + 4)} ${totalStr.padEnd(bColTotal + 3)} ${lsColor}♥${ls}${c.reset}`);
|
|
3423
|
+
}
|
|
3424
|
+
console.log(` ${'─'.repeat(balVis)}`);
|
|
3485
3425
|
|
|
3486
3426
|
let totalWallet = 0, totalBank = 0, noLifesaverAccounts = [];
|
|
3487
3427
|
for (const w of activeWorkers) {
|
|
@@ -3495,7 +3435,6 @@ async function start(apiKey, apiUrl) {
|
|
|
3495
3435
|
}
|
|
3496
3436
|
console.log('');
|
|
3497
3437
|
|
|
3498
|
-
|
|
3499
3438
|
// Phase 2.75: Check DM history for deaths/level-ups (sequential, fast)
|
|
3500
3439
|
console.log(` ${rgb(139, 92, 246)}${BRAILLE_SPIN[0]}${c.reset} ${c.dim}Checking DM history...${c.reset}`);
|
|
3501
3440
|
let dmDeaths = 0, dmLevelUps = 0, dmNoLs = [];
|