dankgrinder 4.9.5 → 4.9.8
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 +25 -7
- package/lib/commands/trivia.js +2 -2
- package/lib/grinder.js +74 -11
- package/package.json +1 -1
|
@@ -194,15 +194,33 @@ async function runInventory({ channel, waitForDankMemer, client, accountId, redi
|
|
|
194
194
|
|
|
195
195
|
while (page < total) {
|
|
196
196
|
const buttons = getAllButtons(response);
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
const enabled = buttons.filter(b => !b.disabled);
|
|
198
|
+
const expectedNextPage = page; // page is 1-based, paginator target indexes are usually 0-based
|
|
199
|
+
|
|
200
|
+
const parseTargetPage = (customId) => {
|
|
201
|
+
if (!customId) return null;
|
|
202
|
+
const m = String(customId).match(/:(\d+)(?:\.\d+)?$/);
|
|
203
|
+
return m ? parseInt(m[1], 10) : null;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
let nextBtn = enabled.find((b) => {
|
|
207
|
+
const id = String(b.customId || '');
|
|
208
|
+
if (!/paginator-inventory-list/i.test(id)) return false;
|
|
209
|
+
if (!/setpage/i.test(id)) return false;
|
|
210
|
+
return parseTargetPage(id) === expectedNextPage;
|
|
204
211
|
});
|
|
205
212
|
|
|
213
|
+
if (!nextBtn) {
|
|
214
|
+
nextBtn = enabled.find((b) => {
|
|
215
|
+
const id = (b.customId || '').toLowerCase();
|
|
216
|
+
const label = (b.label || '').toLowerCase();
|
|
217
|
+
const emoji = (b.emoji?.name || '').toLowerCase();
|
|
218
|
+
return (id.includes('paginator-inventory') && (id.includes('setpage') || id.includes('next')))
|
|
219
|
+
|| label.includes('next') || label === '▶' || label === '→'
|
|
220
|
+
|| emoji.includes('arrowright') || emoji.includes('doubleright') || emoji === '▶';
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
206
224
|
if (!nextBtn) {
|
|
207
225
|
LOG.debug(`[inv] No next button on page ${page}`);
|
|
208
226
|
break;
|
package/lib/commands/trivia.js
CHANGED
|
@@ -131,10 +131,10 @@ async function runTrivia({ channel, waitForDankMemer, redis }) {
|
|
|
131
131
|
return { result: `trivia → ${btn.label} → +⏣ ${coins.toLocaleString()}`, coins };
|
|
132
132
|
}
|
|
133
133
|
if (lower.includes('correct answer was') || lower.includes('nitwit') || lower.includes('wrong') || lower.includes('incorrect')) {
|
|
134
|
-
return { result: `trivia → ${btn.label} →
|
|
134
|
+
return { result: `trivia → ${btn.label} → wrong`, coins: 0 };
|
|
135
135
|
}
|
|
136
136
|
if (lower.includes('correct') || lower.includes('nice') || lower.includes('right')) {
|
|
137
|
-
return { result: `trivia → ${btn.label} →
|
|
137
|
+
return { result: `trivia → ${btn.label} → correct`, coins: 0 };
|
|
138
138
|
}
|
|
139
139
|
return { result: `trivia → ${btn.label}`, coins: 0 };
|
|
140
140
|
}
|
package/lib/grinder.js
CHANGED
|
@@ -300,10 +300,12 @@ async function fetchConfig(retries = 3, delayMs = 1500) {
|
|
|
300
300
|
|
|
301
301
|
async function sendLog(accountName, command, response, status) {
|
|
302
302
|
try {
|
|
303
|
+
const safeCommand = stripAnsi(String(command || '')).replace(/\s+/g, ' ').trim();
|
|
304
|
+
const safeResponse = stripAnsi(String(response || '')).replace(/\s+/g, ' ').trim();
|
|
303
305
|
await fetch(`${API_URL}/api/grinder/log`, {
|
|
304
306
|
method: 'POST',
|
|
305
307
|
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
|
|
306
|
-
body: JSON.stringify({ account_name: accountName, command, response, status }),
|
|
308
|
+
body: JSON.stringify({ account_name: accountName, command: safeCommand, response: safeResponse, status }),
|
|
307
309
|
});
|
|
308
310
|
} catch { /* silent */ }
|
|
309
311
|
}
|
|
@@ -321,10 +323,13 @@ async function reportEarnings(accountId, accountName, earned, spent, command) {
|
|
|
321
323
|
|
|
322
324
|
async function reportCommandFeed(accountId, accountName, data) {
|
|
323
325
|
try {
|
|
326
|
+
const normalized = { ...data };
|
|
327
|
+
if (typeof normalized.command === 'string') normalized.command = stripAnsi(normalized.command).replace(/\s+/g, ' ').trim();
|
|
328
|
+
if (typeof normalized.result === 'string') normalized.result = stripAnsi(normalized.result).replace(/\s+/g, ' ').trim();
|
|
324
329
|
await fetch(`${API_URL}/api/grinder/command-feed`, {
|
|
325
330
|
method: 'POST',
|
|
326
331
|
headers: { Authorization: `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
|
|
327
|
-
body: JSON.stringify({ account_id: accountId, account_name: accountName, ...
|
|
332
|
+
body: JSON.stringify({ account_id: accountId, account_name: accountName, ...normalized }),
|
|
328
333
|
});
|
|
329
334
|
} catch { /* silent */ }
|
|
330
335
|
}
|
|
@@ -548,7 +553,7 @@ class AccountWorker {
|
|
|
548
553
|
}
|
|
549
554
|
|
|
550
555
|
setStatus(text) {
|
|
551
|
-
this.lastStatus = text;
|
|
556
|
+
this.lastStatus = stripAnsi(String(text || '')).replace(/\s+/g, ' ').trim();
|
|
552
557
|
if (dashboardStarted) scheduleRender();
|
|
553
558
|
}
|
|
554
559
|
|
|
@@ -827,6 +832,39 @@ class AccountWorker {
|
|
|
827
832
|
|
|
828
833
|
async checkBalance() {
|
|
829
834
|
const prefix = this.account.use_slash ? '/' : 'pls';
|
|
835
|
+
const sentAt = Date.now();
|
|
836
|
+
|
|
837
|
+
const looksLikeBalance = (t) => {
|
|
838
|
+
if (!t) return false;
|
|
839
|
+
const lower = t.toLowerCase();
|
|
840
|
+
return lower.includes('balance') || lower.includes('balances') || lower.includes('global rank')
|
|
841
|
+
|| lower.includes('wallet') || /<a?:coin:\d+>/.test(t) || /<a?:bank:\d+>/.test(t);
|
|
842
|
+
};
|
|
843
|
+
|
|
844
|
+
const readBalanceText = async (msg, forceCV2 = false) => {
|
|
845
|
+
if (!msg) return '';
|
|
846
|
+
if (isCV2(msg)) await ensureCV2(msg, forceCV2);
|
|
847
|
+
return stripAnsi(getFullText(msg)).replace(/\s+/g, ' ').trim();
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const findRecentBalanceMessage = async () => {
|
|
851
|
+
if (!this.channel?.messages?.fetch) return null;
|
|
852
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
853
|
+
try {
|
|
854
|
+
const recent = await this.channel.messages.fetch({ limit: 8 });
|
|
855
|
+
const candidates = [...recent.values()].filter((m) =>
|
|
856
|
+
m?.author?.id === DANK_MEMER_ID && (m.createdTimestamp || 0) >= sentAt - 2500
|
|
857
|
+
);
|
|
858
|
+
for (const m of candidates) {
|
|
859
|
+
const t = await readBalanceText(m, true);
|
|
860
|
+
if (looksLikeBalance(t)) return m;
|
|
861
|
+
}
|
|
862
|
+
} catch {}
|
|
863
|
+
await new Promise((r) => setTimeout(r, 700));
|
|
864
|
+
}
|
|
865
|
+
return null;
|
|
866
|
+
};
|
|
867
|
+
|
|
830
868
|
await this.channel.send(`${prefix} bal`);
|
|
831
869
|
let response = await this.waitForDankMemer(10000);
|
|
832
870
|
|
|
@@ -837,15 +875,35 @@ class AccountWorker {
|
|
|
837
875
|
}
|
|
838
876
|
|
|
839
877
|
if (response) {
|
|
840
|
-
|
|
841
|
-
let text = stripAnsi(getFullText(response)).replace(/\s+/g, ' ').trim();
|
|
878
|
+
let text = await readBalanceText(response);
|
|
842
879
|
|
|
843
880
|
// Dank Memer sometimes sends empty first payload then edits in the full card.
|
|
844
|
-
if (!text && response.id) {
|
|
881
|
+
if ((!text || !looksLikeBalance(text)) && response.id) {
|
|
845
882
|
const edited = await this.waitForMessageUpdate(response.id, 5000);
|
|
846
883
|
if (edited) {
|
|
847
|
-
|
|
848
|
-
|
|
884
|
+
text = await readBalanceText(edited, true);
|
|
885
|
+
response = edited;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// If we received a stale/irrelevant update, fetch same message fresh.
|
|
890
|
+
if ((!text || !looksLikeBalance(text)) && response.id && this.channel?.messages?.fetch) {
|
|
891
|
+
const fetched = await Promise.resolve(this.channel.messages.fetch(response.id)).catch(() => null);
|
|
892
|
+
if (fetched) {
|
|
893
|
+
const fetchedText = await readBalanceText(fetched, true);
|
|
894
|
+
if (fetchedText) {
|
|
895
|
+
text = fetchedText;
|
|
896
|
+
response = fetched;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Final fallback: scan latest Dank messages right after command send.
|
|
902
|
+
if (!text || !looksLikeBalance(text)) {
|
|
903
|
+
const recentBalance = await findRecentBalanceMessage();
|
|
904
|
+
if (recentBalance) {
|
|
905
|
+
text = await readBalanceText(recentBalance, true);
|
|
906
|
+
response = recentBalance;
|
|
849
907
|
}
|
|
850
908
|
}
|
|
851
909
|
|
|
@@ -854,6 +912,11 @@ class AccountWorker {
|
|
|
854
912
|
return;
|
|
855
913
|
}
|
|
856
914
|
|
|
915
|
+
if (!looksLikeBalance(text)) {
|
|
916
|
+
this.log('warn', `Balance response did not look like balance card: "${text.substring(0, 140)}"`);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
|
|
857
920
|
let wallet = 0;
|
|
858
921
|
let bank = 0;
|
|
859
922
|
let matched = '';
|
|
@@ -898,9 +961,9 @@ class AccountWorker {
|
|
|
898
961
|
if (this.stats.balance > 0 || this.stats.bankBalance > 0) return;
|
|
899
962
|
}
|
|
900
963
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
964
|
+
this.stats.balance = wallet;
|
|
965
|
+
this.stats.bankBalance = bank;
|
|
966
|
+
this.log('bal', `Wallet: ${c.bold}${c.green}⏣ ${wallet.toLocaleString()}${c.reset} Bank: ${c.bold}${c.cyan}⏣ ${bank.toLocaleString()}${c.reset} Total: ${c.bold}⏣ ${(wallet + bank).toLocaleString()}${c.reset} ${c.dim}(${matched || 'none'})${c.reset}`);
|
|
904
967
|
|
|
905
968
|
// Store in Redis for persistence
|
|
906
969
|
if (redis) {
|