dankgrinder 8.74.0 → 8.75.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/ui.js +178 -374
- package/package.json +1 -1
package/lib/ui.js
CHANGED
|
@@ -1,47 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
1
|
+
const logUpdate = require('log-update');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const figlet = require('figlet');
|
|
5
4
|
|
|
6
|
-
let _startTime = Date.now();
|
|
7
5
|
let _workers = [];
|
|
8
6
|
let _version = '0.0.0';
|
|
9
7
|
let _live = false;
|
|
10
|
-
let
|
|
8
|
+
let _startTime = Date.now();
|
|
9
|
+
let _events = [];
|
|
10
|
+
const MAX_EVENTS = 5;
|
|
11
|
+
let _refreshTimer = null;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let
|
|
13
|
+
function applyGradient(str) {
|
|
14
|
+
const lines = str.split('\n');
|
|
15
|
+
const maxL = Math.max(...lines.map(l => l.length));
|
|
16
|
+
let res = '';
|
|
17
|
+
lines.forEach(line => {
|
|
18
|
+
for (let c = 0; c < line.length; c++) {
|
|
19
|
+
let t = c / (maxL || 1);
|
|
20
|
+
res += chalk.rgb(
|
|
21
|
+
Math.floor(255 - (t * 255)),
|
|
22
|
+
Math.floor(t * 200),
|
|
23
|
+
Math.floor(t * 255) + 100 > 255 ? 255 : Math.floor(t * 255) + 100
|
|
24
|
+
)(line[c]);
|
|
25
|
+
}
|
|
26
|
+
res += '\n';
|
|
27
|
+
});
|
|
28
|
+
return res;
|
|
29
|
+
}
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let _bottomRow = 0;
|
|
31
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
32
|
+
function getSpinner() { return SPINNER_FRAMES[Math.floor(Date.now() / 80) % SPINNER_FRAMES.length]; }
|
|
20
33
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// 3 19 12 3 4 rest
|
|
24
|
-
const NUM_W = 3; // " # "
|
|
25
|
-
const ACC_W = 19; // dot + name (padded to 18, dot takes 1)
|
|
26
|
-
const BAL_W = 12; // "⏣95,230" padded
|
|
27
|
-
const LS_W = 3; // lifesavers
|
|
28
|
-
const LV_W = 4; // level
|
|
29
|
-
// ROW = '│ ' + # + ' ' + dot + name + ' ' + bal + ' ' + ls + ' ' + lv + ' ' + logs + ' │'
|
|
30
|
-
// = 2 + 2 + 1 + 1 + 18 + 1 + 12 + 1 + 3 + 1 + 4 + 1 + N + 2 = 50 + N
|
|
31
|
-
// LOGS_W computed dynamically
|
|
34
|
+
const LOAD_FRAMES = ['[= ]', '[== ]', '[=== ]', '[ ===]', '[ ==]', '[ =]', '[ ==]', '[ ===]', '[=== ]', '[== ]'];
|
|
35
|
+
function getLoader() { return LOAD_FRAMES[Math.floor(Date.now() / 150) % LOAD_FRAMES.length]; }
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
const ACC_COLORS = ['#ff0054', '#ffbd00', '#390099', '#9e0059', '#ff5400', '#00f5d4', '#00bbf9', '#fee440', '#f15bb5', '#9b5de5'];
|
|
38
|
+
function getAccountColor(str) {
|
|
39
|
+
let hash = 0;
|
|
40
|
+
for (let i = 0; i < str.length; i++) hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
41
|
+
return chalk.hex(ACC_COLORS[Math.abs(hash) % ACC_COLORS.length]);
|
|
42
|
+
}
|
|
36
43
|
|
|
37
|
-
// ── Status dot + color ───────────────────────────────────────────
|
|
38
44
|
const STATUS_DOT = {
|
|
39
|
-
online: { dot: '●', color: '
|
|
40
|
-
busy: { dot: '◐', color: '
|
|
41
|
-
paused: { dot: '○', color: '
|
|
42
|
-
dead: { dot: '✗', color: '
|
|
43
|
-
connect: { dot: '◯', color: '
|
|
45
|
+
online: { dot: '●', color: chalk.hex('#50ff78') },
|
|
46
|
+
busy: { dot: '◐', color: chalk.hex('#ffdc50') },
|
|
47
|
+
paused: { dot: '○', color: chalk.hex('#b4b4b4') },
|
|
48
|
+
dead: { dot: '✗', color: chalk.hex('#ff5064') },
|
|
49
|
+
connect: { dot: '◯', color: chalk.hex('#ffb450') }
|
|
44
50
|
};
|
|
51
|
+
|
|
52
|
+
function init({ workers }) { _workers = workers; _startTime = Date.now(); _events = []; _live = false; }
|
|
53
|
+
|
|
54
|
+
function drawBanner(version) {
|
|
55
|
+
_version = version || '0.0.0';
|
|
56
|
+
console.clear();
|
|
57
|
+
const titleStr = figlet.textSync('DANK GRINDER', { font: 'ANSI Shadow' });
|
|
58
|
+
console.log(chalk.bold(applyGradient(titleStr)));
|
|
59
|
+
console.log(chalk.bold.magenta(`v${_version} — ${getLoader()} Booting... `));
|
|
60
|
+
console.log();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fmtUptime() {
|
|
64
|
+
const s = Math.floor((Date.now() - _startTime) / 1000);
|
|
65
|
+
if (s < 60) return `${s}s`;
|
|
66
|
+
const m = Math.floor(s / 60);
|
|
67
|
+
const h = Math.floor(m / 60);
|
|
68
|
+
if (h > 0) return `${h}h ${m % 60}m`;
|
|
69
|
+
return `${m}m`;
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
function getDot(w) {
|
|
46
73
|
if (!w.channel) return STATUS_DOT.connect;
|
|
47
74
|
if (w.paused || w.dashboardPaused) return STATUS_DOT.paused;
|
|
@@ -51,173 +78,49 @@ function getDot(w) {
|
|
|
51
78
|
return STATUS_DOT.online;
|
|
52
79
|
}
|
|
53
80
|
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (n >= 1_000_000) return '+' + (n / 1_000_000).toFixed(1) + 'M';
|
|
77
|
-
if (n >= 1_000) return '+' + (n / 1_000).toFixed(1) + 'k';
|
|
78
|
-
if (n > 0) return '+' + n;
|
|
79
|
-
return '—';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function fmtBal(n) {
|
|
83
|
-
if (!n && n !== 0) return DIM + '—' + c.reset;
|
|
84
|
-
const s = n >= 1_000 ? n.toLocaleString() : String(n);
|
|
85
|
-
return c.green + '⏣' + s + c.reset;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function fmtLifesavers(w) {
|
|
89
|
-
const ls = w._lifesavers;
|
|
90
|
-
if (ls === undefined || ls === null) return DIM + '—' + c.reset;
|
|
91
|
-
if (ls === 0) return c.red + '0' + c.reset;
|
|
92
|
-
if (ls <= 2) return c.yellow + ls + c.reset;
|
|
93
|
-
return c.green + ls + c.reset;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function fmtLevel(w) {
|
|
97
|
-
const lv = w._level;
|
|
98
|
-
if (!lv) return DIM + '—' + c.reset;
|
|
99
|
-
return c.cyan + padL(lv, 4) + c.reset;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ── Layout ─────────────────────────────────────────────────────
|
|
103
|
-
function layout() {
|
|
104
|
-
_W = Math.min(process.stdout.columns || 110, 130);
|
|
105
|
-
_inner = _W - 2;
|
|
106
|
-
const rows = process.stdout.rows || 40;
|
|
107
|
-
// Main box: banner(6) + status(1) + divider(1) + header(1) + hr(1) + accounts + totals(2) + bottom(1) = 13 + accounts
|
|
108
|
-
_maxAccounts = Math.min(_workers.length, Math.max(3, rows - 16));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ── Get LOGS column width ─────────────────────────────────────────
|
|
112
|
-
function getLogsW() {
|
|
113
|
-
// ROW = │ + # + ' ' + dot+name + ' ' + bal + ' ' + ls + ' ' + lv + ' ' + logs + ' │ + \n
|
|
114
|
-
// 1 2 1 19 1 12 1 3 1 4 1 N 1 1 = 48+N
|
|
115
|
-
const FIXED = NUM_W + 1 + ACC_W + 1 + BAL_W + 1 + LS_W + 1 + LV_W + 1 + 2;
|
|
116
|
-
return Math.max(20, _inner - FIXED);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// ── Build one account row ─────────────────────────────────────────
|
|
120
|
-
function accountRow(w, wi) {
|
|
121
|
-
const LOG_W = getLogsW();
|
|
122
|
-
const dot = getDot(w);
|
|
123
|
-
const name = padR(trunc(w.username || '?', ACC_W - 1), ACC_W - 1); // -1 for dot
|
|
124
|
-
const bal = w.stats.balance !== undefined ? w.stats.balance : null;
|
|
125
|
-
const ls = fmtLifesavers(w);
|
|
126
|
-
const lv = fmtLevel(w);
|
|
127
|
-
|
|
128
|
-
// Current log: lastStatus or cooldown
|
|
129
|
-
let logText = w.lastStatus || 'idle';
|
|
130
|
-
if (w.globalCooldownUntil && Date.now() < w.globalCooldownUntil) {
|
|
131
|
-
const s = Math.ceil((w.globalCooldownUntil - Date.now()) / 1000);
|
|
132
|
-
logText = s > 60 ? `cooldown ${Math.ceil(s/60)}m` : `cooldown ${s}s`;
|
|
81
|
+
function addEvent(type, msg) {
|
|
82
|
+
const now = new Date();
|
|
83
|
+
const ts = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
|
|
84
|
+
const icons = { info: chalk.blue('ℹ'), warn: chalk.yellow('⚠'), error: chalk.red('✗'), success: chalk.green('✔') };
|
|
85
|
+
_events.unshift({ icon: icons[type] || icons.info, msg: chalk.bold(msg), ts });
|
|
86
|
+
if (_events.length > MAX_EVENTS) _events.pop();
|
|
87
|
+
if (_live) render();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function logGlobal(msg) { addEvent('info', msg); }
|
|
91
|
+
function log(accountIdx, msg) { if (!_live && accountIdx === -1) console.log(chalk.bold(msg)); }
|
|
92
|
+
function updateAccountRow() { }
|
|
93
|
+
|
|
94
|
+
// Precision width padding
|
|
95
|
+
function padFixed(str, len, isAnsiPrepared = false) {
|
|
96
|
+
if (!isAnsiPrepared) {
|
|
97
|
+
if (str.length > len) return str.substring(0, len - 1) + '…';
|
|
98
|
+
return str.padEnd(len, ' ');
|
|
99
|
+
} else {
|
|
100
|
+
const rawLen = str.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
101
|
+
if (rawLen < len) return str + ' '.repeat(len - rawLen);
|
|
102
|
+
return str; // Assume caller pre-truncated the inner text
|
|
133
103
|
}
|
|
134
|
-
if (w.paused || w.dashboardPaused) logText = 'paused';
|
|
135
|
-
if (w._alert?.type === 'death') logText = 'DEAD — lifesavers?';
|
|
136
|
-
const logPadded = padR(logText, LOG_W);
|
|
137
|
-
|
|
138
|
-
const numStr = padL(String(wi + 1), NUM_W - 1); // " 1" or "10"
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
`${B}│\x1b[0m` +
|
|
142
|
-
`${DIM}${numStr}${c.reset} ` +
|
|
143
|
-
`${dot.dot}${dot.color}${name}${c.reset} ` +
|
|
144
|
-
`${fmtBal(bal)} ` +
|
|
145
|
-
`${ls} ` +
|
|
146
|
-
`${lv} ` +
|
|
147
|
-
`${DIM}${logPadded}${c.reset} ` +
|
|
148
|
-
`${B}│\x1b[0m`
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ── Top border helper ─────────────────────────────────────────────
|
|
153
|
-
function hRule(char) {
|
|
154
|
-
process.stdout.write(`${B}│\x1b[0m${char.repeat(_inner)}${B}│`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ── Header row ───────────────────────────────────────────────────
|
|
158
|
-
function headerRow(LOG_W) {
|
|
159
|
-
return (
|
|
160
|
-
`${B}│\x1b[0m` +
|
|
161
|
-
`${c.bold}${padL('#', NUM_W - 1)}${c.reset} ` +
|
|
162
|
-
`${c.bold}${padR('ACCOUNT', ACC_W)}${c.reset} ` +
|
|
163
|
-
`${c.bold}${padL('BAL', BAL_W)}${c.reset} ` +
|
|
164
|
-
`${c.bold}${padL('LS', LS_W)}${c.reset} ` +
|
|
165
|
-
`${c.bold}${padL('LV', LV_W)}${c.reset} ` +
|
|
166
|
-
`${c.bold}${padR('LOGS', LOG_W)}${c.reset} ` +
|
|
167
|
-
`${B}│\x1b[0m`
|
|
168
|
-
);
|
|
169
104
|
}
|
|
170
105
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
let totalCoins = 0, totalBal = 0;
|
|
190
|
-
for (const w of _workers) { totalCoins += w.stats.coins || 0; totalBal += w.stats.balance || 0; }
|
|
191
|
-
const totalBalStr = totalBal > 0 ? c.green + '⏣' + totalBal.toLocaleString() + c.reset : DIM + '—' + c.reset;
|
|
192
|
-
const online = _workers.filter(w => w.channel && !w.paused && !w.dashboardPaused).length;
|
|
193
|
-
|
|
194
|
-
const titleLeft = `${c.bold}DANKGRINDER${c.reset} ${c.dim}v${_version}${c.reset}`;
|
|
195
|
-
const titleRight = `${c.green}●${c.reset} ${online} online ${totalBalStr} uptime ${up}`;
|
|
196
|
-
const totalW = stripAnsi(titleLeft).length + stripAnsi(titleRight).length;
|
|
197
|
-
const padding = Math.max(0, top - totalW);
|
|
198
|
-
const leftPad = Math.floor(padding / 2);
|
|
199
|
-
const rightPad = padding - leftPad;
|
|
200
|
-
|
|
201
|
-
row(r++); clrLine();
|
|
202
|
-
process.stdout.write(`${B}│\x1b[0m${' '.repeat(leftPad)}${titleLeft}${' '.repeat(rightPad)}${titleRight}${B}│`);
|
|
203
|
-
ln();
|
|
204
|
-
|
|
205
|
-
// ── Divider ──
|
|
206
|
-
row(r++); clrLine();
|
|
207
|
-
process.stdout.write(`${B}├${'─'.repeat(top)}┤`);
|
|
208
|
-
ln();
|
|
209
|
-
|
|
210
|
-
// ── Column header ──
|
|
211
|
-
row(r++); clrLine();
|
|
212
|
-
process.stdout.write(headerRow(LOG_W));
|
|
213
|
-
ln();
|
|
214
|
-
|
|
215
|
-
// ── HR ──
|
|
216
|
-
row(r++); clrLine();
|
|
217
|
-
process.stdout.write(`${B}│\x1b[0m${'─'.repeat(top)}${B}│`);
|
|
218
|
-
ln();
|
|
219
|
-
|
|
220
|
-
// ── Account rows ──
|
|
106
|
+
function render() {
|
|
107
|
+
if (!_live) return;
|
|
108
|
+
const cols = process.stdout.columns || 110;
|
|
109
|
+
const rows = process.stdout.rows || 30;
|
|
110
|
+
|
|
111
|
+
const titleStr = figlet.textSync('DANK GRINDER', { font: 'ANSI Shadow' });
|
|
112
|
+
let out = chalk.bold(applyGradient(titleStr));
|
|
113
|
+
|
|
114
|
+
const sep = chalk.hex('#4dd4ee').bold('━'.repeat(cols));
|
|
115
|
+
out += sep + '\n';
|
|
116
|
+
|
|
117
|
+
const wCols = { num: 4, name: 18, bal: 14, ls: 6, lv: 6 };
|
|
118
|
+
let logW = Math.max(15, cols - (wCols.num + wCols.name + wCols.bal + wCols.ls + wCols.lv + 12));
|
|
119
|
+
|
|
120
|
+
out += `${chalk.bold(' #').padEnd(wCols.num)} ${chalk.bold('ACCOUNT').padEnd(wCols.name)} ${chalk.bold('BAL').padEnd(wCols.bal)} ${chalk.bold('LS').padEnd(wCols.ls)} ${chalk.bold('LV').padEnd(wCols.lv)} ${chalk.bold('LOGS')} \n`;
|
|
121
|
+
out += sep + '\n';
|
|
122
|
+
|
|
123
|
+
const maxShown = Math.max(3, rows - 24);
|
|
221
124
|
const sorted = [..._workers].sort((a, b) => {
|
|
222
125
|
if (!a.channel !== !b.channel) return a.channel ? -1 : 1;
|
|
223
126
|
const aA = a.channel && !a.paused && !a.dashboardPaused;
|
|
@@ -225,191 +128,92 @@ function draw() {
|
|
|
225
128
|
if (aA !== bB) return aA ? -1 : 1;
|
|
226
129
|
return (b.stats.commands || 0) - (a.stats.commands || 0);
|
|
227
130
|
});
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
row(r++); clrLine();
|
|
236
|
-
process.stdout.write(accountRow(w, wi));
|
|
237
|
-
ln();
|
|
131
|
+
|
|
132
|
+
let totalCoins = 0, totalBal = 0, onlineCount = 0;
|
|
133
|
+
for (let i = 0; i < _workers.length; i++) {
|
|
134
|
+
const w = _workers[i];
|
|
135
|
+
totalCoins += w.stats.coins || 0;
|
|
136
|
+
totalBal += w.stats.balance || 0;
|
|
137
|
+
if (w.channel && !w.paused && !w.dashboardPaused) onlineCount++;
|
|
238
138
|
}
|
|
239
139
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
ln();
|
|
256
|
-
|
|
257
|
-
// ── Bottom border ──
|
|
258
|
-
_bottomRow = r;
|
|
259
|
-
row(r++); clrLine();
|
|
260
|
-
process.stdout.write(`${B}└${'─'.repeat(top)}┘`);
|
|
261
|
-
ln();
|
|
262
|
-
|
|
263
|
-
// ── Events box header ──
|
|
264
|
-
r++; // blank row
|
|
265
|
-
row(r++); clrLine();
|
|
266
|
-
process.stdout.write(`${B}┌${'─'.repeat(top)}┐`);
|
|
267
|
-
ln();
|
|
268
|
-
row(r++); clrLine();
|
|
269
|
-
process.stdout.write(`${B}│\x1b[0m ${c.bold}EVENTS${c.reset}${' '.repeat(top - 10)}${B}│`);
|
|
270
|
-
ln();
|
|
271
|
-
row(r++); clrLine();
|
|
272
|
-
process.stdout.write(`${B}├${'─'.repeat(top)}┤`);
|
|
273
|
-
ln();
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// ── Update ONE account row in-place ───────────────────────────────
|
|
277
|
-
function updateAccountRow(accountIdx) {
|
|
278
|
-
if (!_live) return;
|
|
279
|
-
const rowNum = _accountRows[accountIdx];
|
|
280
|
-
if (!rowNum) return;
|
|
281
|
-
const w = _workers[accountIdx];
|
|
282
|
-
if (!w) return;
|
|
283
|
-
row(rowNum); clrLine();
|
|
284
|
-
process.stdout.write(accountRow(w, accountIdx));
|
|
285
|
-
ln();
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ── Event log tracking ─────────────────────────────────────────────
|
|
289
|
-
const MAX_EVENTS = 8;
|
|
290
|
-
let _events = [];
|
|
291
|
-
|
|
292
|
-
function addEvent(type, msg) {
|
|
293
|
-
const now = new Date();
|
|
294
|
-
const ts = `${padL(now.getHours(), 2, '0')}:${padL(now.getMinutes(), 2, '0')}:${padL(now.getSeconds(), 2, '0')}`;
|
|
295
|
-
const icons = { info: 'ℹ', warn: '⚠', error: '✗', success: '✔', debug: '⌘' };
|
|
296
|
-
const colors = { info: c.cyan, warn: c.yellow, error: c.red, success: c.green, debug: DIM };
|
|
297
|
-
const icon = icons[type] || 'ℹ';
|
|
298
|
-
const col = colors[type] || c.cyan;
|
|
299
|
-
|
|
300
|
-
_events.unshift({ icon, col, msg, ts });
|
|
301
|
-
if (_events.length > MAX_EVENTS) _events.pop();
|
|
302
|
-
if (_live) drawEvents();
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// ── Draw events box ───────────────────────────────────────────────
|
|
306
|
-
function drawEvents() {
|
|
307
|
-
if (!_live) return;
|
|
308
|
-
const LOG_W = getLogsW();
|
|
309
|
-
const inner = _inner;
|
|
310
|
-
|
|
311
|
-
// Draw from _bottomRow + 2 (after the events box header rows)
|
|
312
|
-
let r = _bottomRow + 2;
|
|
313
|
-
|
|
314
|
-
for (let i = 0; i < MAX_EVENTS; i++) {
|
|
315
|
-
row(r); clrLine();
|
|
316
|
-
if (i < _events.length) {
|
|
317
|
-
const ev = _events[i];
|
|
318
|
-
const msgPadded = padR(`${ev.icon} ${ev.msg}`, inner - 30);
|
|
319
|
-
const tsPadded = padL(`[${ev.ts}]`, 20);
|
|
320
|
-
process.stdout.write(`${B}│\x1b[0m ${ev.col}${msgPadded}${c.reset}${tsPadded} ${B}│`);
|
|
140
|
+
const shown = sorted.slice(0, maxShown);
|
|
141
|
+
for (let i = 0; i < shown.length; i++) {
|
|
142
|
+
const w = shown[i];
|
|
143
|
+
const rawIdx = String(_workers.indexOf(w) + 1);
|
|
144
|
+
|
|
145
|
+
let logText = w.lastStatus || 'idle';
|
|
146
|
+
let activity = '';
|
|
147
|
+
if (w.globalCooldownUntil && Date.now() < w.globalCooldownUntil) {
|
|
148
|
+
const s = Math.ceil((w.globalCooldownUntil - Date.now()) / 1000);
|
|
149
|
+
logText = s > 60 ? `cooldown ${Math.ceil(s/60)}m` : `cd ${s}s`;
|
|
150
|
+
activity = chalk.yellow(getSpinner());
|
|
151
|
+
} else if (w.paused || w.dashboardPaused) {
|
|
152
|
+
logText = 'paused';
|
|
153
|
+
} else if (w.channel) {
|
|
154
|
+
activity = chalk.cyan(getSpinner());
|
|
321
155
|
} else {
|
|
322
|
-
|
|
156
|
+
activity = chalk.gray(getLoader());
|
|
323
157
|
}
|
|
324
|
-
ln();
|
|
325
|
-
r++;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Bottom border of events box
|
|
329
|
-
row(r); clrLine();
|
|
330
|
-
process.stdout.write(`${B}└${'─'.repeat(inner)}┘`);
|
|
331
|
-
ln();
|
|
332
|
-
}
|
|
333
158
|
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const
|
|
339
|
-
|
|
159
|
+
// Fixed widths construction
|
|
160
|
+
const numPart = padFixed(` ${rawIdx}`, wCols.num);
|
|
161
|
+
|
|
162
|
+
// Name Part
|
|
163
|
+
const dot = getDot(w);
|
|
164
|
+
let uName = w.username || '?';
|
|
165
|
+
// dot + space + name -> subtract 2 from allowed length
|
|
166
|
+
if (uName.length > wCols.name - 2) uName = uName.substring(0, wCols.name - 3) + '…';
|
|
167
|
+
const cName = `${dot.color(dot.dot)} ${getAccountColor(uName)(chalk.bold(uName))}`;
|
|
168
|
+
const namePart = padFixed(cName, wCols.name, true);
|
|
169
|
+
|
|
170
|
+
// Bal Part
|
|
171
|
+
let bVal = w.stats.balance !== undefined ? (w.stats.balance >= 1000 ? w.stats.balance.toLocaleString() : String(w.stats.balance)) : '—';
|
|
172
|
+
bVal = bVal.length > wCols.bal - 2 ? bVal.substring(0, wCols.bal - 3) + '…' : bVal;
|
|
173
|
+
const cBal = bVal === '—' ? chalk.dim('—') : chalk.bold.green(`⏣${bVal}`);
|
|
174
|
+
const balPart = padFixed(cBal, wCols.bal, true);
|
|
175
|
+
|
|
176
|
+
// LS Part
|
|
177
|
+
let lVal = w._lifesavers !== undefined ? String(w._lifesavers) : '—';
|
|
178
|
+
const lColor = lVal === '—' ? chalk.dim : (w._lifesavers === 0 ? chalk.bold.red : (w._lifesavers <= 2 ? chalk.bold.yellow : chalk.bold.cyan));
|
|
179
|
+
const lsPart = padFixed(lColor(lVal.substring(0, wCols.ls)), wCols.ls, true);
|
|
180
|
+
|
|
181
|
+
// LV Part
|
|
182
|
+
let lvVal = w._level !== undefined ? String(w._level) : '—';
|
|
183
|
+
const lvPart = padFixed(chalk.bold.magenta(lvVal.substring(0, wCols.lv)), wCols.lv, true);
|
|
184
|
+
|
|
185
|
+
// Log Part
|
|
186
|
+
let lStr = logText;
|
|
187
|
+
if (lStr.length > logW - 3) lStr = lStr.substring(0, logW - 4) + '…';
|
|
188
|
+
const cLog = `${activity} ${chalk.dim.bold(lStr)}`;
|
|
189
|
+
const logPart = padFixed(cLog, logW, true);
|
|
190
|
+
|
|
191
|
+
out += `${chalk.dim(numPart)} ${namePart} ${balPart} ${lsPart} ${lvPart} ${logPart}\n`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (_workers.length > maxShown) {
|
|
195
|
+
out += chalk.bold.dim(` ... and ${(_workers.length - maxShown).toLocaleString()} more accounts running ${getSpinner()}\n`);
|
|
340
196
|
}
|
|
341
|
-
return out + '\x1b[0m';
|
|
342
|
-
}
|
|
343
197
|
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
198
|
+
out += sep + '\n';
|
|
199
|
+
const totC = totalCoins >= 1000000 ? (totalCoins/1000000).toFixed(1)+'M' : (totalCoins >= 1000 ? (totalCoins/1000).toFixed(1)+'k' : totalCoins);
|
|
200
|
+
out += ` ${chalk.bold('Σ ACCOUNTS:')} ${chalk.bold.magenta(_workers.length)} │ ${chalk.bold('⚡ ONLINE:')} ${chalk.bold.cyan(onlineCount)} │ ${chalk.bold('💰 TOTAL:')} ${chalk.bold.green('⏣' + totalBal.toLocaleString())} │ ${chalk.bold('📈 GAINED:')} ${chalk.bold.yellow('+' + totC)} │ ${chalk.bold('⏱ UP:')} ${chalk.bold.dim(fmtUptime())}\n`;
|
|
201
|
+
out += sep + '\n';
|
|
202
|
+
|
|
203
|
+
out += chalk.bold.cyan(` 📻 LATEST EVENTS \n`) + chalk.dim.bold('─'.repeat(cols)) + '\n';
|
|
204
|
+
if (_events.length === 0) out += chalk.dim(' Quiet here...\n');
|
|
205
|
+
_events.forEach(ev => { out += chalk.dim(` [${ev.ts}] `) + `${ev.icon} ${ev.msg}\n`; });
|
|
206
|
+
out += chalk.dim.bold('─'.repeat(cols)) + '\n';
|
|
352
207
|
|
|
353
|
-
|
|
354
|
-
function init({ workers }) {
|
|
355
|
-
_startTime = Date.now();
|
|
356
|
-
_workers = workers;
|
|
357
|
-
_version = '0.0.0';
|
|
358
|
-
_events = [];
|
|
359
|
-
_live = false;
|
|
360
|
-
_phase = 'init';
|
|
361
|
-
_accountRows = [];
|
|
208
|
+
logUpdate(out);
|
|
362
209
|
}
|
|
363
210
|
|
|
364
|
-
function drawBanner(version) { _version = version || '0.0.0'; }
|
|
365
211
|
function start() {}
|
|
366
|
-
function
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
function
|
|
371
|
-
function
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (accountIdx >= 0 && _live) updateAccountRow(accountIdx);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function logGlobal(msg) {
|
|
378
|
-
if (!_live) return;
|
|
379
|
-
addEvent('info', msg);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ── Refresh timer ─────────────────────────────────────────────────
|
|
383
|
-
let _refreshTimer = null;
|
|
384
|
-
function startRefresh() {
|
|
385
|
-
if (_refreshTimer) return;
|
|
386
|
-
_refreshTimer = setInterval(() => {
|
|
387
|
-
if (!_live) return;
|
|
388
|
-
// Update all visible account rows
|
|
389
|
-
for (const idx of Object.keys(_accountRows)) {
|
|
390
|
-
updateAccountRow(parseInt(idx));
|
|
391
|
-
}
|
|
392
|
-
// Refresh uptime bar
|
|
393
|
-
const online = _workers.filter(w => w.channel && !w.paused && !w.dashboardPaused).length;
|
|
394
|
-
const up = fmtUptime();
|
|
395
|
-
let totalBal = 0;
|
|
396
|
-
for (const w of _workers) totalBal += w.stats.balance || 0;
|
|
397
|
-
const totalBalStr = totalBal > 0 ? c.green + '⏣' + totalBal.toLocaleString() + c.reset : DIM + '—' + c.reset;
|
|
398
|
-
// Update title bar on row 2 (row 1 = top border)
|
|
399
|
-
const titleLeft = `${c.bold}DANKGRINDER${c.reset} ${c.dim}v${_version}${c.reset}`;
|
|
400
|
-
const titleRight = `${c.green}●${c.reset} ${online} online ${totalBalStr} uptime ${up}`;
|
|
401
|
-
const totalW = stripAnsi(titleLeft) + stripAnsi(titleRight);
|
|
402
|
-
const padding = Math.max(0, _inner - totalW);
|
|
403
|
-
const leftPad = Math.floor(padding / 2);
|
|
404
|
-
const rightPad = padding - leftPad;
|
|
405
|
-
row(2); clrLine();
|
|
406
|
-
process.stdout.write(`${B}│\x1b[0m${' '.repeat(leftPad)}${titleLeft}${' '.repeat(rightPad)}${titleRight}${B}│`);
|
|
407
|
-
ln();
|
|
408
|
-
}, 1000);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function stopRefresh() {
|
|
412
|
-
if (_refreshTimer) { clearInterval(_refreshTimer); _refreshTimer = null; }
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
module.exports = { init, drawBanner, start, draw, log, logGlobal, stop, setLive, setPhase, updateAccountRow, startRefresh, stopRefresh, addEvent };
|
|
212
|
+
function draw() { render(); }
|
|
213
|
+
function setLive(val) { _live = val; if (val) render(); }
|
|
214
|
+
function setPhase(phase) {}
|
|
215
|
+
function startRefresh() { if (!_refreshTimer) _refreshTimer = setInterval(() => { if (_live) render(); }, 80); }
|
|
216
|
+
function stopRefresh() { if (_refreshTimer) { clearInterval(_refreshTimer); _refreshTimer = null; } }
|
|
217
|
+
function stop() { _live = false; stopRefresh(); logUpdate.clear(); }
|
|
218
|
+
|
|
219
|
+
module.exports = { init, drawBanner, start, draw, log, logGlobal, stop, setLive, setPhase, updateAccountRow, startRefresh, stopRefresh, addEvent };
|