dankgrinder 8.42.0 → 8.43.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/grinder.js +4 -6
- package/lib/ui.js +61 -98
- package/package.json +1 -1
package/lib/grinder.js
CHANGED
|
@@ -721,14 +721,11 @@ class AccountWorker {
|
|
|
721
721
|
log(type, msg) {
|
|
722
722
|
const stripped = msg.replace(/\x1b\[[0-9;]*m/g, '');
|
|
723
723
|
if (type !== 'debug') this.lastStatus = stripped.substring(0, 28);
|
|
724
|
-
// Route through UI
|
|
725
|
-
const icons = { info: '.', success: '[OK]', error: '[X]', warn: '[!]', cmd: '>', coin: '$', buy: '#', bal: '*', debug: '.' };
|
|
726
|
-
const icon = icons[type] || icons.info;
|
|
727
|
-
const text = `${icon} ${this.username} ${msg}`;
|
|
724
|
+
// Route through UI — msg already has context, no need to prepend username
|
|
728
725
|
if (typeof ui !== 'undefined') {
|
|
729
|
-
try { ui.log(this.idx,
|
|
726
|
+
try { ui.log(this.idx, msg); } catch { process.stdout.write(msg + '\n'); }
|
|
730
727
|
} else {
|
|
731
|
-
process.stdout.write(
|
|
728
|
+
process.stdout.write(msg + '\n');
|
|
732
729
|
}
|
|
733
730
|
}
|
|
734
731
|
|
|
@@ -2512,6 +2509,7 @@ class AccountWorker {
|
|
|
2512
2509
|
clearTimeout(timeoutId);
|
|
2513
2510
|
this.username = this.client.user.tag || this.username;
|
|
2514
2511
|
this.avatarUrl = this.client.user.displayAvatarURL?.({ format: 'png', dynamic: true, size: 128 }) || null;
|
|
2512
|
+
this.loggedIn = true; // so statusColor/statusText show correct state
|
|
2515
2513
|
|
|
2516
2514
|
// Attach raw gateway logger for CV2 component capture
|
|
2517
2515
|
rawLogger.attachRawLogger(this.client, { channelId: this.account.channel_id });
|
package/lib/ui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI Live Dashboard —
|
|
2
|
+
* CLI Live Dashboard — clean box at top, events stream below.
|
|
3
3
|
* Box drawn once. Events appended line-by-line. No flicker.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -8,37 +8,11 @@ let _workers = [];
|
|
|
8
8
|
let _isShuttingDown = () => false;
|
|
9
9
|
let _version = '0.0.0';
|
|
10
10
|
let _live = false;
|
|
11
|
-
let _boxHeight = 0; // rows consumed by the box
|
|
12
11
|
|
|
13
12
|
// ── Spinner frames ────────────────────────────────────────────
|
|
14
13
|
const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
15
14
|
function spinnerFrame() { return SPINNER[Math.floor(Date.now() / 150) % SPINNER.length]; }
|
|
16
15
|
|
|
17
|
-
// ── Big ASCII art banner ──────────────────────────────────────
|
|
18
|
-
const BANNER_LINES = [
|
|
19
|
-
' ██████╗ ██╗ ██╗███╗ ██╗ ██████╗ ███████╗ ██████╗ ███╗ ██╗',
|
|
20
|
-
' ██╔══██╗██║ ██║████╗ ██║██╔════╝ ██╔════╝██╔═══██╗████╗ ██║',
|
|
21
|
-
' ██║ ██║██║ ██║██╔██╗ ██║██║ ███╗█████╗ ██║ ██║██╔██╗ ██║',
|
|
22
|
-
' ██║ ██║██║ ██║██║╚██╗██║██║ ██║██╔══╝ ██║ ██║██║╚██╗██║',
|
|
23
|
-
' ██████╔╝╚██████╔╝██║ ╚████║╚██████╔╝███████╗╚██████╔╝██║ ╚████║',
|
|
24
|
-
' ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝',
|
|
25
|
-
' ██╗██╗ ██╗███╗ ██╗ ██████╗ ██╗ ███████╗██████╗ ',
|
|
26
|
-
' ██║██║ ██║████╗ ██║██╔═══██╗██║ ██╔════╝██╔══██╗',
|
|
27
|
-
' ███████╗███████╗ ██║██║ ██║██╔██╗ ██║██║ ██║██║ █████╗ ██████╔╝',
|
|
28
|
-
' ╚════██║╚════██║ ██║██║ ██║██║╚██╗██║██║ ██║██║ ██╔══╝ ██╔══██╗',
|
|
29
|
-
' ██║ ██║ ██║╚██████╔╝██║ ╚████║╚██████╔╝███████╗███████╗██║ ██║',
|
|
30
|
-
' ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝',
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
function gradientLine(text, r1, g1, b1, r2, g2, b2) {
|
|
34
|
-
let out = '';
|
|
35
|
-
for (let i = 0; i < text.length; i++) {
|
|
36
|
-
const t = text.length <= 1 ? 0 : i / (text.length - 1);
|
|
37
|
-
out += `\x1b[38;2;${Math.round(r1 + (r2 - r1) * t)};${Math.round(g1 + (g2 - g1) * t)};${Math.round(b1 + (b2 - b1) * t)}m${text[i]}`;
|
|
38
|
-
}
|
|
39
|
-
return out + '\x1b[0m';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
16
|
// ── Palette ───────────────────────────────────────────────────
|
|
43
17
|
const PALETTE = [
|
|
44
18
|
'\x1b[38;2;77;212;238m', '\x1b[38;2;255;194;77m', '\x1b[38;2;130;210;100m',
|
|
@@ -76,7 +50,7 @@ function fmtUptime() {
|
|
|
76
50
|
|
|
77
51
|
// ── Status helpers ─────────────────────────────────────────────
|
|
78
52
|
function statusColor(w) {
|
|
79
|
-
if (!w.
|
|
53
|
+
if (!w.loggedIn || !w.channel) return c.red;
|
|
80
54
|
if (w.paused || w.dashboardPaused) return c.yellow;
|
|
81
55
|
if (w.busy || w._invRunning || w._sellRunning) return c.yellow;
|
|
82
56
|
if (Date.now() < w.globalCooldownUntil) return c.blue;
|
|
@@ -84,7 +58,7 @@ function statusColor(w) {
|
|
|
84
58
|
}
|
|
85
59
|
|
|
86
60
|
function statusText(w) {
|
|
87
|
-
if (!w.
|
|
61
|
+
if (!w.loggedIn || !w.channel) return 'CONNECTING';
|
|
88
62
|
if (w.paused) return 'PAUSED';
|
|
89
63
|
if (w.dashboardPaused) return 'HELD';
|
|
90
64
|
if (w._alert?.type === 'death') return 'DEAD';
|
|
@@ -99,75 +73,66 @@ function statusText(w) {
|
|
|
99
73
|
|
|
100
74
|
// ── Layout ────────────────────────────────────────────────────
|
|
101
75
|
function layout() {
|
|
102
|
-
const W = Math.min(process.stdout.columns ||
|
|
76
|
+
const W = Math.min(process.stdout.columns || 100, 120);
|
|
103
77
|
const rows = process.stdout.rows || 40;
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
const footerH = 2; // totals + hr
|
|
108
|
-
const maxAccounts = Math.min(_workers.length, Math.max(3, rows - bannerH - statusH - headerH - footerH - 5));
|
|
109
|
-
return { W, bannerH, statusH, headerH, footerH, maxAccounts };
|
|
78
|
+
// Box: header(2) + title(1) + gap(1) + statusBar(1) + divider(1) + headerRow(1) + hr(1) + maxAccounts + totals(2) + divider(1)
|
|
79
|
+
const maxAccounts = Math.min(_workers.length, Math.max(3, rows - 16));
|
|
80
|
+
return { W, maxAccounts };
|
|
110
81
|
}
|
|
111
82
|
|
|
112
83
|
// ── Draw the fixed box ────────────────────────────────────────
|
|
113
84
|
function draw() {
|
|
114
|
-
const { W,
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
// ── Save cursor
|
|
118
|
-
process.stdout.write('\x1b[s');
|
|
119
|
-
process.stdout.write('\x1b[2J\x1b[H');
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// ── Status bar inside box ──
|
|
132
|
-
const running = _workers.filter(w => w.running && w.channel && !w.paused && !w.dashboardPaused).length;
|
|
85
|
+
const { W, maxAccounts } = layout();
|
|
86
|
+
const inner = W - 2;
|
|
87
|
+
|
|
88
|
+
// ── Save cursor + clear screen ──
|
|
89
|
+
process.stdout.write('\x1b[s'); // save cursor
|
|
90
|
+
process.stdout.write('\x1b[2J\x1b[H'); // clear screen + home
|
|
91
|
+
|
|
92
|
+
// ── Title ──
|
|
93
|
+
process.stdout.write(`\x1b[38;2;77;212;238m┌${'─'.repeat(inner)}┐\x1b[0m\n`);
|
|
94
|
+
const title = ` ${c.bold}${c.cyan}DANK${c.reset}${c.bold}${c.white}GRINDER${c.reset} ${c.dim}v${_version}${c.reset} `;
|
|
95
|
+
const dots = ' '.repeat(Math.max(0, inner - stripAnsi(title) - 10));
|
|
96
|
+
const upStr = `${DIM}${fmtUptime()}${c.reset}`;
|
|
97
|
+
const upPad = ' '.repeat(Math.max(0, inner - stripAnsi(title) - stripAnsi(dots) - stripAnsi(upStr) - 2));
|
|
98
|
+
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m${title}${dots}${upStr}${upPad}\x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
99
|
+
|
|
100
|
+
// ── Status bar ──
|
|
101
|
+
const running = _workers.filter(w => w.loggedIn && w.channel && !w.paused && !w.dashboardPaused).length;
|
|
133
102
|
const paused = _workers.filter(w => w.paused || w.dashboardPaused).length;
|
|
134
|
-
const errors = _workers.filter(w => !w.
|
|
135
|
-
const
|
|
136
|
-
`${c.
|
|
137
|
-
`${c.
|
|
138
|
-
`${
|
|
139
|
-
`${c.green}·${c.reset}${running}`,
|
|
140
|
-
`${c.yellow}~${c.reset}${paused}`,
|
|
141
|
-
errors > 0 ? `${c.red}E${c.reset}${errors}` : null,
|
|
103
|
+
const errors = _workers.filter(w => !w.loggedIn || !w.channel).length;
|
|
104
|
+
const statusParts = [
|
|
105
|
+
`${c.green}●${c.reset} ${running} online`,
|
|
106
|
+
paused > 0 ? `${c.yellow}~${c.reset} ${paused} paused` : null,
|
|
107
|
+
errors > 0 ? `${c.red}E${c.reset} ${errors} error` : null,
|
|
142
108
|
`${DIM}Ctrl+C${c.reset}`,
|
|
143
|
-
].filter(Boolean)
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
process.stdout.write(`\x1b[38;2;77;212;238m
|
|
109
|
+
].filter(Boolean);
|
|
110
|
+
const statusStr = statusParts.join(' ');
|
|
111
|
+
const statusPad = ' '.repeat(Math.max(0, inner - stripAnsi(statusStr)));
|
|
112
|
+
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m ${statusStr}${statusPad}\x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
113
|
+
process.stdout.write(`\x1b[38;2;77;212;238m├${'─'.repeat(inner)}┤\x1b[0m\n`);
|
|
147
114
|
|
|
148
115
|
// ── Table header ──
|
|
149
|
-
const col = {
|
|
150
|
-
|
|
151
|
-
};
|
|
152
|
-
const nameExtra = Math.max(0, W - 4 - col.st - col.name - col.last - col.cmds - col.ok - col.earned - 14);
|
|
116
|
+
const col = { st: 9, name: 18, last: 24, cmds: 6, ok: 5, earned: 10 };
|
|
117
|
+
const nameExtra = Math.max(0, inner - col.st - col.name - col.last - col.cmds - col.ok - col.earned - 8);
|
|
153
118
|
col.name += nameExtra;
|
|
154
119
|
|
|
155
120
|
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m `);
|
|
156
121
|
process.stdout.write(`${c.bold}#${c.reset} `);
|
|
157
122
|
process.stdout.write(`${c.bold}${padR('STATUS', col.st)}${c.reset} `);
|
|
158
123
|
process.stdout.write(`${c.bold}${padR('ACCOUNT', col.name)}${c.reset} `);
|
|
159
|
-
process.stdout.write(`${c.bold}${padR('LAST
|
|
124
|
+
process.stdout.write(`${c.bold}${padR('LAST COMMAND', col.last)}${c.reset} `);
|
|
160
125
|
process.stdout.write(`${c.bold}${padL('CMDS', col.cmds)}${c.reset} `);
|
|
161
126
|
process.stdout.write(`${c.bold}${padL('OK%', col.ok)}${c.reset} `);
|
|
162
127
|
process.stdout.write(`${c.bold}${padL('EARNED', col.earned)}${c.reset} `);
|
|
163
128
|
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
164
|
-
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m ${'─'.repeat(
|
|
129
|
+
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m ${'─'.repeat(inner)} \x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
165
130
|
|
|
166
131
|
// ── Account rows ──
|
|
167
132
|
const sorted = [..._workers].sort((a, b) => {
|
|
168
|
-
if (!a.
|
|
169
|
-
const aA = a.
|
|
170
|
-
const bA = b.
|
|
133
|
+
if (!a.loggedIn !== !b.loggedIn) return !a.loggedIn ? 1 : -1;
|
|
134
|
+
const aA = a.loggedIn && a.channel && !a.paused && !a.dashboardPaused;
|
|
135
|
+
const bA = b.loggedIn && b.channel && !b.paused && !b.dashboardPaused;
|
|
171
136
|
if (aA !== bA) return bA ? 1 : -1;
|
|
172
137
|
return (b.stats.commands || 0) - (a.stats.commands || 0);
|
|
173
138
|
});
|
|
@@ -195,7 +160,8 @@ function draw() {
|
|
|
195
160
|
}
|
|
196
161
|
|
|
197
162
|
if (sorted.length > maxAccounts) {
|
|
198
|
-
|
|
163
|
+
const extra = sorted.length - maxAccounts;
|
|
164
|
+
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m ${DIM}+ ${extra} more accounts${' '.repeat(Math.max(0, inner - 20 - String(extra).length))}${c.reset} \x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
199
165
|
}
|
|
200
166
|
|
|
201
167
|
// ── Totals ──
|
|
@@ -208,7 +174,7 @@ function draw() {
|
|
|
208
174
|
const rate = totalCmds > 0 ? Math.round((totalOk / totalCmds) * 100) : 0;
|
|
209
175
|
const memMB = Math.round((process.memoryUsage?.rss?.() ?? process.memoryUsage().rss) / 1048576);
|
|
210
176
|
|
|
211
|
-
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m ${'─'.repeat(
|
|
177
|
+
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m ${'─'.repeat(inner)} \x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
212
178
|
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m `);
|
|
213
179
|
process.stdout.write(`${c.bold}Σ${c.reset} `);
|
|
214
180
|
process.stdout.write(`${DIM}${padL(_workers.length, 2)} acc${c.reset} `);
|
|
@@ -216,18 +182,19 @@ function draw() {
|
|
|
216
182
|
process.stdout.write(`${' '.repeat(col.last)} `);
|
|
217
183
|
process.stdout.write(`${padL(totalCmds, col.cmds)} `);
|
|
218
184
|
process.stdout.write(`${padL(rate, col.ok)}% `);
|
|
219
|
-
|
|
220
|
-
|
|
185
|
+
if (totalCoins > 0) {
|
|
186
|
+
process.stdout.write(`${c.green}${padL('+' + totalCoins.toLocaleString(), col.earned)}${c.reset} `);
|
|
187
|
+
} else {
|
|
188
|
+
process.stdout.write(`${DIM}${padL('—', col.earned)}${c.reset} `);
|
|
189
|
+
}
|
|
190
|
+
process.stdout.write(`${DIM}${fmtUptime()} | ${memMB}MB${c.reset} `.padEnd(inner));
|
|
221
191
|
process.stdout.write(`\x1b[38;2;77;212;238m│\x1b[0m\n`);
|
|
222
192
|
|
|
223
|
-
// ── Bottom
|
|
224
|
-
process.stdout.write(`\x1b[38;2;77;212;238m
|
|
193
|
+
// ── Bottom ──
|
|
194
|
+
process.stdout.write(`\x1b[38;2;77;212;238m└${'─'.repeat(inner)}┘\x1b[0m\n`);
|
|
225
195
|
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// Restore cursor to just below the box
|
|
230
|
-
process.stdout.write(`\x1b[u`); // restore cursor
|
|
196
|
+
// Restore cursor to after the box
|
|
197
|
+
process.stdout.write('\x1b[u');
|
|
231
198
|
}
|
|
232
199
|
|
|
233
200
|
// ── Strip ANSI for length calc ───────────────────────────────
|
|
@@ -237,7 +204,7 @@ function stripAnsi(s) {
|
|
|
237
204
|
|
|
238
205
|
// ── Event tracking ────────────────────────────────────────────
|
|
239
206
|
let _eventLines = []; // [accountIdx] = [{text, ts}]
|
|
240
|
-
const MAX_EVENTS =
|
|
207
|
+
const MAX_EVENTS = 3;
|
|
241
208
|
|
|
242
209
|
// ── Public API ────────────────────────────────────────────────
|
|
243
210
|
|
|
@@ -253,10 +220,9 @@ function init({ workers, isShuttingDown }) {
|
|
|
253
220
|
function drawBanner(version) { _version = version; }
|
|
254
221
|
function start() {}
|
|
255
222
|
function stop() { _live = false; process.stdout.write(c.reset + '\n'); }
|
|
256
|
-
|
|
257
|
-
// Enable live mode — from this point, log() appends below the box
|
|
258
223
|
function setLive(val) { _live = val; }
|
|
259
224
|
|
|
225
|
+
// log: append event below the box (no redraw of box)
|
|
260
226
|
function log(accountIdx, msg) {
|
|
261
227
|
const now = new Date();
|
|
262
228
|
const ts = `${padL(now.getHours(), 2, '0')}:${padL(now.getMinutes(), 2, '0')}:${padL(now.getSeconds(), 2, '0')}`;
|
|
@@ -270,14 +236,11 @@ function log(accountIdx, msg) {
|
|
|
270
236
|
|
|
271
237
|
if (!_live) return; // during login: just track, don't output yet
|
|
272
238
|
|
|
273
|
-
// ── Append event
|
|
274
|
-
const col2 = accountIdx >= 0 ? wc(accountIdx) :
|
|
275
|
-
const name = accountIdx >= 0 ? trunc(_workers[accountIdx]?.username || '?',
|
|
276
|
-
|
|
277
|
-
process.stdout.write(
|
|
278
|
-
process.stdout.write(`${col2}${padR(name, 12)}${c.reset} `);
|
|
279
|
-
process.stdout.write(`${DIM}[${ts}]${c.reset} ${msg}`);
|
|
280
|
-
process.stdout.write(`\n`);
|
|
239
|
+
// ── Append event cleanly below the box ──
|
|
240
|
+
const col2 = accountIdx >= 0 ? wc(accountIdx) : c.cyan;
|
|
241
|
+
const name = accountIdx >= 0 ? trunc(_workers[accountIdx]?.username || '?', 14) : 'GLOBAL';
|
|
242
|
+
const text = DIM + name + c.reset + ' ' + DIM + ts + c.reset + ' ' + msg;
|
|
243
|
+
process.stdout.write(text + '\n');
|
|
281
244
|
}
|
|
282
245
|
|
|
283
246
|
function logGlobal(msg) {
|