dankgrinder 5.22.0 → 5.23.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 +22 -4
- package/lib/grinder.js +80 -32
- package/package.json +1 -1
|
@@ -202,14 +202,21 @@ async function runInventory({ channel, waitForDankMemer, client, accountId, redi
|
|
|
202
202
|
|
|
203
203
|
if (!response) {
|
|
204
204
|
LOG.warn('[inv] No response');
|
|
205
|
-
return { result: 'no response', items: [] };
|
|
205
|
+
return { result: 'no response', items: [], complete: false, pagesVisited: 0, pagesTotal: 0 };
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
if (isHoldTight(response)) {
|
|
209
209
|
const reason = getHoldTightReason(response);
|
|
210
210
|
LOG.warn(`[inv] Hold Tight${reason ? ` (reason: ${reason})` : ''}`);
|
|
211
211
|
await sleep(30000);
|
|
212
|
-
return {
|
|
212
|
+
return {
|
|
213
|
+
result: `hold tight (${reason || 'unknown'})`,
|
|
214
|
+
items: [],
|
|
215
|
+
holdTightReason: reason,
|
|
216
|
+
complete: false,
|
|
217
|
+
pagesVisited: 0,
|
|
218
|
+
pagesTotal: 0,
|
|
219
|
+
};
|
|
213
220
|
}
|
|
214
221
|
|
|
215
222
|
if (isCV2(response)) await ensureCV2(response);
|
|
@@ -346,13 +353,19 @@ async function runInventory({ channel, waitForDankMemer, client, accountId, redi
|
|
|
346
353
|
}
|
|
347
354
|
|
|
348
355
|
const items = Object.values(itemMap);
|
|
349
|
-
|
|
356
|
+
const pagesVisited = visitedPages.size;
|
|
357
|
+
const pagesTotal = Math.max(total || 1, 1);
|
|
358
|
+
const complete = page >= total || pagesVisited >= pagesTotal;
|
|
359
|
+
LOG.success(`[inv] Found ${items.length} unique items across ${pagesVisited}/${pagesTotal} pages`);
|
|
360
|
+
if (!complete) {
|
|
361
|
+
LOG.warn(`[inv] Incomplete pagination detected: stopped at page ${page}/${pagesTotal}`);
|
|
362
|
+
}
|
|
350
363
|
|
|
351
364
|
const { totalValue, totalMarket } = await enrichItems(items);
|
|
352
365
|
LOG.info(`[inv] Net value: ${c.bold}${c.green}⏣ ${totalValue.toLocaleString()}${c.reset} Market: ${c.bold}⏣ ${totalMarket.toLocaleString()}${c.reset}`);
|
|
353
366
|
|
|
354
367
|
// Store in Redis — NO EXPIRY (permanent until next update)
|
|
355
|
-
if (redis && accountId) {
|
|
368
|
+
if (complete && redis && accountId) {
|
|
356
369
|
try {
|
|
357
370
|
const payload = {
|
|
358
371
|
items,
|
|
@@ -366,6 +379,8 @@ async function runInventory({ channel, waitForDankMemer, client, accountId, redi
|
|
|
366
379
|
} catch (e) {
|
|
367
380
|
LOG.error(`[inv] Redis store failed: ${e.message}`);
|
|
368
381
|
}
|
|
382
|
+
} else if (!complete) {
|
|
383
|
+
LOG.warn('[inv] Skipping Redis store for incomplete inventory scan');
|
|
369
384
|
}
|
|
370
385
|
|
|
371
386
|
return {
|
|
@@ -374,6 +389,9 @@ async function runInventory({ channel, waitForDankMemer, client, accountId, redi
|
|
|
374
389
|
totalValue,
|
|
375
390
|
totalMarket,
|
|
376
391
|
coins: 0,
|
|
392
|
+
complete,
|
|
393
|
+
pagesVisited,
|
|
394
|
+
pagesTotal,
|
|
377
395
|
};
|
|
378
396
|
}
|
|
379
397
|
|
package/lib/grinder.js
CHANGED
|
@@ -1085,42 +1085,78 @@ class AccountWorker {
|
|
|
1085
1085
|
|
|
1086
1086
|
// ── Check Balance ───────────────────────────────────────────
|
|
1087
1087
|
async checkInventory(options = {}) {
|
|
1088
|
-
const {
|
|
1089
|
-
|
|
1090
|
-
|
|
1088
|
+
const {
|
|
1089
|
+
force = false,
|
|
1090
|
+
startupProgress = null,
|
|
1091
|
+
requireComplete = false,
|
|
1092
|
+
maxAttempts = 1,
|
|
1093
|
+
} = options;
|
|
1094
|
+
if (this._invRunning) return { ok: false, skipped: 'busy' };
|
|
1095
|
+
if (!force && this._lastInvCheck && Date.now() - this._lastInvCheck < 300_000) return { ok: false, skipped: 'recent' };
|
|
1091
1096
|
this._invRunning = true;
|
|
1092
1097
|
this._lastInvCheck = Date.now();
|
|
1093
1098
|
this.busy = true;
|
|
1094
1099
|
try {
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1100
|
+
const tries = Math.max(1, Number.isFinite(maxAttempts) ? maxAttempts : 1);
|
|
1101
|
+
let lastErr = null;
|
|
1102
|
+
for (let attempt = 1; attempt <= tries; attempt++) {
|
|
1103
|
+
if (startupProgress && Number.isInteger(startupProgress.current) && Number.isInteger(startupProgress.total)) {
|
|
1104
|
+
this.log('info', `Checking inventory... (${startupProgress.current}/${startupProgress.total}) [try ${attempt}/${tries}]`);
|
|
1105
|
+
} else {
|
|
1106
|
+
this.log('info', `Checking inventory...${tries > 1 ? ` [try ${attempt}/${tries}]` : ''}`);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
try {
|
|
1110
|
+
const result = await commands.runInventory({
|
|
1111
|
+
channel: this.channel,
|
|
1112
|
+
waitForDankMemer: (timeout) => this.waitForDankMemer(timeout),
|
|
1113
|
+
client: this.client,
|
|
1114
|
+
accountId: this.account.id,
|
|
1115
|
+
redis,
|
|
1116
|
+
onPageProgress: ({ page, total }) => {
|
|
1117
|
+
this.log('info', `Inventory pages: ${page}/${total}`);
|
|
1118
|
+
},
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
if (requireComplete && !result.complete) {
|
|
1122
|
+
throw new Error(`incomplete pages (${result.pagesVisited || 0}/${result.pagesTotal || 0})`);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
this.log('success', `Inventory: ${result.items?.length || 0} items, ⏣ ${(result.totalValue || 0).toLocaleString()} net`);
|
|
1126
|
+
try {
|
|
1127
|
+
await fetch(`${API_URL}/api/grinder/inventory`, {
|
|
1128
|
+
method: 'POST',
|
|
1129
|
+
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
|
|
1130
|
+
body: JSON.stringify({
|
|
1131
|
+
account_id: this.account.id,
|
|
1132
|
+
items: result.items || [],
|
|
1133
|
+
totalValue: result.totalValue || 0,
|
|
1134
|
+
}),
|
|
1135
|
+
});
|
|
1136
|
+
} catch {}
|
|
1137
|
+
|
|
1138
|
+
return {
|
|
1139
|
+
ok: true,
|
|
1140
|
+
complete: !!result.complete,
|
|
1141
|
+
pagesVisited: result.pagesVisited || 0,
|
|
1142
|
+
pagesTotal: result.pagesTotal || 0,
|
|
1143
|
+
attempts: attempt,
|
|
1144
|
+
result,
|
|
1145
|
+
};
|
|
1146
|
+
} catch (e) {
|
|
1147
|
+
lastErr = e;
|
|
1148
|
+
if (attempt < tries) {
|
|
1149
|
+
this.log('warn', `Inventory attempt ${attempt}/${tries} failed (${e.message}). Retrying...`);
|
|
1150
|
+
await sleep(1500 + Math.floor(Math.random() * 1500));
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
throw lastErr || new Error('inventory check failed');
|
|
1122
1157
|
} catch (e) {
|
|
1123
1158
|
this.log('error', `Inventory check failed: ${e.message}`);
|
|
1159
|
+
return { ok: false, error: e.message };
|
|
1124
1160
|
} finally {
|
|
1125
1161
|
this._invRunning = false;
|
|
1126
1162
|
this.busy = false;
|
|
@@ -2425,14 +2461,26 @@ async function start(apiKey, apiUrl) {
|
|
|
2425
2461
|
const label = w?.username || w?.account?.label || w?.account?.id || `account-${i + 1}`;
|
|
2426
2462
|
log('info', `${c.dim}[inv-startup] ${i + 1}/${workers.length} ${label}${c.reset}`);
|
|
2427
2463
|
try {
|
|
2428
|
-
await w.checkInventory({
|
|
2429
|
-
|
|
2464
|
+
const invRes = await w.checkInventory({
|
|
2465
|
+
force: true,
|
|
2466
|
+
startupProgress: { current: i + 1, total: workers.length },
|
|
2467
|
+
requireComplete: true,
|
|
2468
|
+
maxAttempts: 3,
|
|
2469
|
+
});
|
|
2470
|
+
if (invRes?.ok) invDone++;
|
|
2471
|
+
else invFailed++;
|
|
2430
2472
|
} catch {
|
|
2431
2473
|
invFailed++;
|
|
2432
2474
|
}
|
|
2433
2475
|
const invComplete = invDone + invFailed;
|
|
2434
2476
|
log('info', `${c.dim}[inv-startup-progress] ${invComplete}/${workers.length} complete (${invDone} ok, ${invFailed} failed)${c.reset}`);
|
|
2435
2477
|
}));
|
|
2478
|
+
|
|
2479
|
+
if (invFailed > 0) {
|
|
2480
|
+
log('error', `${c.red}Inventory phase incomplete: ${invDone}/${workers.length} complete, ${invFailed} failed/incomplete. Not starting grind loops.${c.reset}`);
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2436
2484
|
const invSummaryColor = invFailed > 0 ? c.yellow : c.green;
|
|
2437
2485
|
log('success', `${c.dim}Inventory phase complete: ${invSummaryColor}${invDone}/${workers.length}${c.reset}${c.dim} done${invFailed > 0 ? `, ${c.yellow}${invFailed} failed${c.reset}${c.dim}` : ''}. Starting grind loops...${c.reset}`);
|
|
2438
2486
|
|