mirror of
https://github.com/tiennm99/WPlace-AutoBOT.git
synced 2026-09-05 22:16:26 +00:00
Merge pull request #1 from JustEngineerLGTM/main
New paint prediction system
This commit is contained in:
@@ -13,29 +13,6 @@ chrome.runtime.onInstalled.addListener(async () => {
|
||||
console.log('📦 Installation resource test:', resources);
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === 'executeScript') {
|
||||
// Get tabId from sender or requests
|
||||
const tabId = request.tabId || sender.tab?.id;
|
||||
|
||||
if (!tabId) {
|
||||
sendResponse({ success: false, error: 'Could not determine target tab' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Use IIFE for async handlings
|
||||
(async () => {
|
||||
try {
|
||||
await executeLocalScript(request.scriptName, tabId);
|
||||
sendResponse({ success: true });
|
||||
} catch (error) {
|
||||
sendResponse({ success: false, error: error.message });
|
||||
}
|
||||
})();
|
||||
|
||||
return true; // Important: indicates async response
|
||||
}
|
||||
});
|
||||
|
||||
async function executeLocalScript(scriptName, tabId) {
|
||||
try {
|
||||
|
||||
+220
-135
@@ -10,36 +10,6 @@
|
||||
// ==/UserScript==
|
||||
localStorage.removeItem("lp");
|
||||
|
||||
// Fallback translation function for when utils manager isn't loaded
|
||||
function getText(key, params) {
|
||||
// Try to get translation from loadedTranslations
|
||||
try {
|
||||
if (window.loadedTranslations && window.loadedTranslations[key]) {
|
||||
let text = window.loadedTranslations[key];
|
||||
if (params) {
|
||||
Object.keys(params).forEach(paramKey => {
|
||||
text = text.replace(new RegExp(`{{${paramKey}}}`, 'g'), params[paramKey]);
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// Try with state.language if available
|
||||
if (window.state && window.state.language && window.loadedTranslations && window.loadedTranslations[window.state.language] && window.loadedTranslations[window.state.language][key]) {
|
||||
let text = window.loadedTranslations[window.state.language][key];
|
||||
if (params) {
|
||||
Object.keys(params).forEach(paramKey => {
|
||||
text = text.replace(new RegExp(`{{${paramKey}}}`, 'g'), params[paramKey]);
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error in getText fallback:', error);
|
||||
}
|
||||
|
||||
return key; // Fallback to key if no translation found
|
||||
}
|
||||
|
||||
; (async () => {
|
||||
// Prevent multiple instances of this script from running
|
||||
@@ -952,6 +922,94 @@ function getText(key, params) {
|
||||
// Create global account manager instance
|
||||
const accountManager = new AccountManager();
|
||||
|
||||
// Local Charge Model to minimize API calls and drive account switching
|
||||
const ChargeModel = (() => {
|
||||
class Model {
|
||||
constructor() {
|
||||
this.map = new Map(); // token -> {charges,max,lastTickAt,lastSyncAt}
|
||||
this.tickIntervalMs = 30_000; // +1 charge per 30s
|
||||
this.timer = null;
|
||||
this.startedAt = Date.now();
|
||||
}
|
||||
seedFromAccounts(accounts) {
|
||||
const now = Date.now();
|
||||
(accounts || []).forEach(acc => {
|
||||
if (!acc || !acc.token) return;
|
||||
const existing = this.map.get(acc.token) || {};
|
||||
const charges = Number.isFinite(acc.Charges) ? Math.floor(acc.Charges) : (existing.charges || 0);
|
||||
const max = Number.isFinite(acc.Max) ? Math.floor(acc.Max) : (existing.max || 1);
|
||||
this.map.set(acc.token, {
|
||||
charges: Math.max(0, Math.min(charges, max)),
|
||||
max: Math.max(1, max),
|
||||
lastTickAt: existing.lastTickAt || now,
|
||||
lastSyncAt: existing.lastSyncAt || now,
|
||||
});
|
||||
});
|
||||
}
|
||||
ensureToken(token) {
|
||||
if (!token) return null;
|
||||
if (!this.map.has(token)) {
|
||||
this.map.set(token, { charges: 0, max: Math.max(1, state.maxCharges || 1), lastTickAt: Date.now(), lastSyncAt: 0 });
|
||||
}
|
||||
return this.map.get(token);
|
||||
}
|
||||
get(token) { return this.ensureToken(token); }
|
||||
getForCurrent() { return this.get(accountManager.getCurrentAccount()?.token); }
|
||||
setFromServer(token, charges, max) {
|
||||
const node = this.ensureToken(token);
|
||||
if (!node) return;
|
||||
node.charges = Math.max(0, Math.min(Math.floor(charges || 0), Math.max(1, Math.floor(max || node.max || 1))));
|
||||
node.max = Math.max(1, Math.floor(max || node.max || 1));
|
||||
node.lastSyncAt = Date.now();
|
||||
}
|
||||
decrement(token, amount) {
|
||||
const node = this.ensureToken(token);
|
||||
if (!node) return 0;
|
||||
const n = Math.max(0, Math.floor(amount || 0));
|
||||
node.charges = Math.max(0, node.charges - n);
|
||||
return node.charges;
|
||||
}
|
||||
incrementTickAll() {
|
||||
const now = Date.now();
|
||||
accountManager.getAllAccounts().forEach(acc => {
|
||||
if (!acc?.token) return;
|
||||
const node = this.ensureToken(acc.token);
|
||||
if (!node) return;
|
||||
// catch-up ticks if tab was inactive
|
||||
const elapsed = now - (node.lastTickAt || now);
|
||||
const ticks = Math.floor(elapsed / this.tickIntervalMs);
|
||||
if (ticks > 0) {
|
||||
node.charges = Math.min(node.max, node.charges + ticks);
|
||||
node.lastTickAt = (node.lastTickAt || now) + ticks * this.tickIntervalMs;
|
||||
}
|
||||
});
|
||||
// Mirror values into AccountManager and UI state
|
||||
this.syncToAccountManager();
|
||||
}
|
||||
start() {
|
||||
if (this.timer) return;
|
||||
this.timer = setInterval(() => this.incrementTickAll(), this.tickIntervalMs);
|
||||
}
|
||||
stop() { if (this.timer) { clearInterval(this.timer); this.timer = null; } }
|
||||
predictTimeToReach(token, target) {
|
||||
const node = this.get(token);
|
||||
if (!node) return Infinity;
|
||||
if (node.charges >= target) return 0;
|
||||
return (target - node.charges) * this.tickIntervalMs;
|
||||
}
|
||||
syncToAccountManager() {
|
||||
const list = accountManager.getAllAccounts();
|
||||
list.forEach(acc => {
|
||||
const node = this.map.get(acc.token);
|
||||
if (!node) return;
|
||||
accountManager.updateAccountData(acc.token, { Charges: node.charges, Max: node.max });
|
||||
});
|
||||
renderAccountsList();
|
||||
}
|
||||
}
|
||||
return new Model();
|
||||
})();
|
||||
|
||||
// GLOBAL STATE
|
||||
const state = {
|
||||
running: false,
|
||||
@@ -1017,6 +1075,10 @@ function getText(key, params) {
|
||||
notificationIntervalMinutes: CONFIG.NOTIFICATIONS.REPEAT_MINUTES,
|
||||
_lastChargesNotifyAt: 0,
|
||||
_lastChargesBelow: true,
|
||||
// Switch debouncing state
|
||||
lastSwitchAt: 0,
|
||||
paintedSinceSwitch: 0,
|
||||
minMsBetweenSwitches: 3000,
|
||||
// Smart save tracking
|
||||
_lastSavePixelCount: 0,
|
||||
_lastSaveTime: 0,
|
||||
@@ -4348,7 +4410,7 @@ function getText(key, params) {
|
||||
const timeText = Utils.msToTimeText(remainingMs);
|
||||
|
||||
if (currentChargesEl) {
|
||||
currentChargesEl.innerHTML = `${state.displayCharges} / ${state.maxCharges}`;
|
||||
currentChargesEl.innerHTML = `${state.displayCharges} / ${max}`;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -4546,7 +4608,7 @@ function getText(key, params) {
|
||||
totalMaxCharges = accounts.reduce((sum, acc) => {
|
||||
if (currentAccount && acc.token === currentAccount.token) {
|
||||
// Use real-time max charges for current account
|
||||
return sum + Math.floor(state.maxCharges || acc.Max || 0);
|
||||
return sum + Math.floor((state.fullChargeData?.max ?? state.maxCharges ?? acc.Max ?? 0));
|
||||
} else {
|
||||
// Use stored data for other accounts
|
||||
return sum + Math.floor(acc.Max || 0);
|
||||
@@ -4571,7 +4633,7 @@ function getText(key, params) {
|
||||
<i class="fas fa-bolt"></i> ${Utils.t('charges')}
|
||||
</div>
|
||||
<div class="wplace-stat-value" id="wplace-stat-charges-value">
|
||||
${state.displayCharges} / ${state.maxCharges}
|
||||
${state.displayCharges} / ${state.fullChargeData?.max ?? state.maxCharges}
|
||||
</div>
|
||||
</div>
|
||||
<div class="wplace-stat-item">
|
||||
@@ -8503,9 +8565,20 @@ function getText(key, params) {
|
||||
state.lastPaintedPosition = { x: lastPixel.localX, y: lastPixel.localY };
|
||||
}
|
||||
|
||||
// Track painted pixels since last account switch to avoid flip-flop
|
||||
state.paintedSinceSwitch = (state.paintedSinceSwitch || 0) + actuallyPaintedCount;
|
||||
|
||||
// IMPORTANT: Decrement charges locally to match Acc-Switch.js behavior
|
||||
state.displayCharges = Math.max(0, state.displayCharges - batchSize);
|
||||
state.preciseCurrentCharges = Math.max(0, state.preciseCurrentCharges - batchSize);
|
||||
// Also update the global local charge model per account with bonus logic
|
||||
try {
|
||||
const tok = accountManager.getCurrentAccount()?.token;
|
||||
const after = ChargeModel.decrement(tok, batchSize);
|
||||
state.displayCharges = Math.floor(after);
|
||||
state.preciseCurrentCharges = after;
|
||||
if (tok) accountManager.updateAccountData(tok, { Charges: state.displayCharges });
|
||||
} catch {}
|
||||
|
||||
state.fullChargeData = {
|
||||
...state.fullChargeData,
|
||||
@@ -8671,13 +8744,23 @@ function getText(key, params) {
|
||||
|
||||
// IMPORTANT: Check charges once at start, then paint until depleted
|
||||
console.log('🔋 Checking initial charges for painting session');
|
||||
const initialChargeCheck = await WPlaceService.getCharges();
|
||||
state.displayCharges = Math.floor(initialChargeCheck.charges);
|
||||
state.preciseCurrentCharges = initialChargeCheck.charges;
|
||||
state.cooldown = initialChargeCheck.cooldown;
|
||||
// Use local charge model to avoid extra API calls
|
||||
const currentToken = accountManager.getCurrentAccount()?.token;
|
||||
let node = ChargeModel.get(currentToken);
|
||||
// One-time sync for current account if never synced (safety)
|
||||
if (node && !node.lastSyncAt) {
|
||||
try {
|
||||
const sync = await WPlaceService.getCharges();
|
||||
ChargeModel.setFromServer(currentToken, sync.charges, sync.max);
|
||||
node = ChargeModel.get(currentToken);
|
||||
} catch {}
|
||||
}
|
||||
state.displayCharges = Math.floor(node?.charges || 0);
|
||||
state.preciseCurrentCharges = node?.charges || 0;
|
||||
state.cooldown = CONFIG.COOLDOWN_DEFAULT;
|
||||
await updateStats();
|
||||
if (state.displayCharges <= 0) {
|
||||
console.log('⚡ No charges available, skipping painting session');
|
||||
console.log('⚡ No charges available (local), skipping painting session');
|
||||
return 'charges_depleted';
|
||||
}
|
||||
|
||||
@@ -9030,55 +9113,38 @@ function getText(key, params) {
|
||||
// const maxChargeChecks = 10; // REMOVED: No limit on API calls during cooldown
|
||||
|
||||
while (!state.stopFlag) {
|
||||
chargeCheckCount++;
|
||||
const threshold = Math.max(1, state.cooldownChargeThreshold || 1);
|
||||
const accounts = accountManager.getAllAccounts();
|
||||
let anyReady = false;
|
||||
let bestMs = Infinity;
|
||||
let currentCharges = ChargeModel.getForCurrent()?.charges || 0;
|
||||
|
||||
const { charges, cooldown } = await WPlaceService.getCharges();
|
||||
state.displayCharges = Math.floor(charges);
|
||||
state.preciseCurrentCharges = charges;
|
||||
state.cooldown = cooldown;
|
||||
for (const acc of accounts) {
|
||||
const node = ChargeModel.get(acc.token);
|
||||
if (!node) continue;
|
||||
if (node.charges >= threshold) {
|
||||
anyReady = true;
|
||||
break;
|
||||
}
|
||||
const ms = ChargeModel.predictTimeToReach(acc.token, threshold);
|
||||
if (ms < bestMs) bestMs = ms;
|
||||
}
|
||||
|
||||
if (state.displayCharges >= state.cooldownChargeThreshold) {
|
||||
console.log(`✅ Cooldown target reached: ${state.displayCharges}/${state.cooldownChargeThreshold}`);
|
||||
if (anyReady) {
|
||||
console.log(`✅ Cooldown target reached locally (≥${threshold})`);
|
||||
NotificationManager.maybeNotifyChargesReached(true);
|
||||
await updateStats();
|
||||
return 'target_reached';
|
||||
}
|
||||
|
||||
const waitMs = Number.isFinite(bestMs) ? Math.max(1000, Math.min(bestMs, 10000)) : 10000;
|
||||
updateUI('noChargesThreshold', 'warning', {
|
||||
time: Utils.msToTimeText(state.cooldown),
|
||||
threshold: state.cooldownChargeThreshold,
|
||||
current: state.displayCharges,
|
||||
time: Utils.msToTimeText(waitMs),
|
||||
threshold,
|
||||
current: currentCharges,
|
||||
});
|
||||
await updateStats();
|
||||
|
||||
// Smart delay calculation to reduce API calls
|
||||
const chargesNeeded = state.cooldownChargeThreshold - state.displayCharges;
|
||||
const estimatedWaitTime = chargesNeeded * state.cooldown;
|
||||
|
||||
// Use longer delays during cooldown to prevent rate limiting
|
||||
let delayTime;
|
||||
if (chargeCheckCount < 3) {
|
||||
// First few checks - shorter delay
|
||||
delayTime = Math.max(3000, state.cooldown); // 3 seconds minimum
|
||||
} else if (estimatedWaitTime > 60000) {
|
||||
// Long wait expected - check every 15 seconds
|
||||
delayTime = 10000;
|
||||
} else if (estimatedWaitTime > 30000) {
|
||||
// Medium wait - check every 15 seconds
|
||||
delayTime = 15000;
|
||||
} else {
|
||||
// Close to target - check every 5 seconds
|
||||
delayTime = 5000;
|
||||
}
|
||||
|
||||
console.log(`⏱️ Cooldown check ${chargeCheckCount}: ${state.displayCharges}/${state.cooldownChargeThreshold} charges, waiting 10s before next check`);
|
||||
await Utils.sleep(10000);
|
||||
|
||||
// REMOVED: No limit on charge checks - bot will wait infinitely until charges are available
|
||||
// if (chargeCheckCount >= maxChargeChecks) {
|
||||
// console.warn('⚠️ Max charge checks reached during cooldown, continuing anyway');
|
||||
// break;
|
||||
// }
|
||||
await Utils.sleep(waitMs);
|
||||
}
|
||||
|
||||
return 'stopped';
|
||||
@@ -9847,9 +9913,10 @@ function getText(key, params) {
|
||||
console.log('✅ Found wasm Module...');
|
||||
return url.split('/').pop();
|
||||
}
|
||||
} catch { }
|
||||
} catch (e) { /* ignore individual fetch errors */ }
|
||||
}
|
||||
console.error(`❌ Could not find Pawtect chunk: `, error);
|
||||
console.error('❌ Could not find Pawtect chunk among preloaded modules');
|
||||
return null;
|
||||
}
|
||||
|
||||
async function purchase(type) {
|
||||
@@ -9993,12 +10060,6 @@ function getText(key, params) {
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchAccount() {
|
||||
const { ID, Charges, Max, Droplets } = await WPlaceService.fetchCheck();
|
||||
console.log("User's ID :", ID);
|
||||
console.log("User's Charges :", Charges, "/", Max);
|
||||
console.log("User's Droplets :", Droplets);
|
||||
}
|
||||
|
||||
async function fetchAllAccountDetails() {
|
||||
if (state.isFetchingAllAccounts) {
|
||||
@@ -10109,6 +10170,7 @@ function getText(key, params) {
|
||||
}
|
||||
|
||||
console.log(`🎯 [FETCH] Completed fetching fresh data for all accounts`);
|
||||
try { ChargeModel.seedFromAccounts(accountManager.getAllAccounts()); } catch {}
|
||||
}
|
||||
|
||||
// Render the accounts list with fresh data
|
||||
@@ -10131,34 +10193,18 @@ function getText(key, params) {
|
||||
// Function to update current account charges in the account list
|
||||
async function updateCurrentAccountInList() {
|
||||
if (accountManager.getAccountCount() === 0) return;
|
||||
|
||||
try {
|
||||
// Always trust backend /me and then map by ID to avoid mixing data between tokens
|
||||
const currentAccountData = await WPlaceService.getCharges();
|
||||
state.displayCharges = Math.floor(currentAccountData.charges);
|
||||
state.preciseCurrentCharges = currentAccountData.charges;
|
||||
const current = accountManager.getCurrentAccount();
|
||||
const node = ChargeModel.get(current?.token);
|
||||
if (!current || !node) return;
|
||||
state.displayCharges = Math.floor(node.charges || 0);
|
||||
state.preciseCurrentCharges = node.charges || 0;
|
||||
await updateStats();
|
||||
|
||||
const accounts = accountManager.getAllAccounts();
|
||||
const idx = accounts.findIndex(acc => acc.ID === currentAccountData.id);
|
||||
const targetToken = idx !== -1 ? accounts[idx].token : accountManager.getCurrentAccount()?.token;
|
||||
|
||||
if (targetToken) {
|
||||
accountManager.updateAccountData(targetToken, {
|
||||
Charges: Math.floor(state.displayCharges || 0),
|
||||
Max: Math.floor(currentAccountData.max || state.maxCharges || 0),
|
||||
Droplets: Math.floor(currentAccountData.droplets)
|
||||
});
|
||||
|
||||
// Keep manager index in sync with reality when possible
|
||||
if (idx !== -1 && typeof accountManager.setCurrentIndex === 'function') {
|
||||
accountManager.setCurrentIndex(idx);
|
||||
state.accountIndex = idx;
|
||||
}
|
||||
|
||||
// Re-render the account list to show updated charges
|
||||
renderAccountsList();
|
||||
}
|
||||
accountManager.updateAccountData(current.token, {
|
||||
Charges: Math.floor(node.charges || 0),
|
||||
Max: Math.floor(node.max || current.Max || 0)
|
||||
});
|
||||
renderAccountsList();
|
||||
} catch (e) {
|
||||
console.warn('⚠️ updateCurrentAccountInList failed:', e);
|
||||
}
|
||||
@@ -10321,6 +10367,18 @@ function getText(key, params) {
|
||||
async function switchToNextAccount(accounts) {
|
||||
console.log(`🔄 [SWITCH] Starting account switch`);
|
||||
|
||||
// Debounce rapid consecutive switches when no painting happened
|
||||
try {
|
||||
const now = Date.now();
|
||||
const last = state.lastSwitchAt || 0;
|
||||
const minGap = state.minMsBetweenSwitches || 3000;
|
||||
const painted = state.paintedSinceSwitch || 0;
|
||||
if (now - last < minGap && painted === 0) {
|
||||
console.log(`⏳ [SWITCH] Debounced rapid switch (Δ${now - last}ms < ${minGap}ms and paintedSinceSwitch=${painted}).`);
|
||||
return false;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
// Validate we have accounts
|
||||
if (accountManager.getAccountCount() === 0) {
|
||||
console.error('❌ No accounts available for switching');
|
||||
@@ -10401,6 +10459,10 @@ function getText(key, params) {
|
||||
// Update the account status and UI after successful switch
|
||||
await updateCurrentAccountInList();
|
||||
|
||||
// Record switch time and reset painted counter to prevent rapid bouncing
|
||||
state.lastSwitchAt = Date.now();
|
||||
state.paintedSinceSwitch = 0;
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('❌ [SWITCH] Account switch failed:', error);
|
||||
@@ -10411,6 +10473,19 @@ function getText(key, params) {
|
||||
// SIMPLIFIED helper function for specific account switching
|
||||
async function switchToSpecificAccount(token, accountName) {
|
||||
console.log(`🔄 [SPECIFIC SWITCH] Attempting to switch to account: ${accountName}`);
|
||||
|
||||
// Debounce rapid consecutive switches when no painting happened
|
||||
try {
|
||||
const now = Date.now();
|
||||
const last = state.lastSwitchAt || 0;
|
||||
const minGap = state.minMsBetweenSwitches || 3000;
|
||||
const painted = state.paintedSinceSwitch || 0;
|
||||
if (now - last < minGap && painted === 0) {
|
||||
console.log(`⏳ [SPECIFIC SWITCH] Debounced rapid switch (Δ${now - last}ms < ${minGap}ms and paintedSinceSwitch=${painted}).`);
|
||||
return false;
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (!token) {
|
||||
console.error('❌ [SPECIFIC SWITCH] Missing token');
|
||||
return false;
|
||||
@@ -10471,6 +10546,7 @@ function getText(key, params) {
|
||||
|
||||
// Fetch fresh stats for UI/state
|
||||
const { charges, cooldown, droplets, max } = await WPlaceService.getCharges();
|
||||
try { ChargeModel.setFromServer(token, charges, max); } catch {}
|
||||
state.displayCharges = Math.floor(charges);
|
||||
state.preciseCurrentCharges = charges;
|
||||
state.cooldown = cooldown;
|
||||
@@ -10530,42 +10606,43 @@ function getText(key, params) {
|
||||
try {
|
||||
const total = accountManager.getAccountCount();
|
||||
if (total <= 1) return false;
|
||||
const threshold = Math.max(1, minRequired || 1);
|
||||
|
||||
const startIdx = accountManager.currentIndex;
|
||||
let best = { cooldown: Infinity, token: null, name: null, idx: -1 };
|
||||
let candidate = null; // {token,name,idx}
|
||||
let bestWait = Infinity; // ms to reach threshold
|
||||
|
||||
for (let step = 1; step <= total - 1; step++) {
|
||||
const idx = (startIdx + step) % total;
|
||||
const acc = accountManager.getAccountByIndex(idx);
|
||||
if (!acc || !acc.token) continue;
|
||||
const node = ChargeModel.get(acc.token);
|
||||
const localCharges = Math.floor(node?.charges || 0);
|
||||
|
||||
console.log(`🔄 [SEARCH] Switching temporarily to ${acc.displayName} (${idx + 1}/${total}) to check charges...`);
|
||||
const ok = await switchToSpecificAccount(acc.token, acc.displayName);
|
||||
if (!ok) {
|
||||
console.warn(`⚠️ [SEARCH] Failed to switch to ${acc.displayName}, trying next...`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentAccountData = await WPlaceService.getCharges();
|
||||
const charges = Math.floor(currentAccountData?.charges || 0);
|
||||
const cooldown = Math.max(0, Number(currentAccountData?.cooldown || 0));
|
||||
if (charges >= minRequired) {
|
||||
console.log(`✅ [SEARCH] Found account with sufficient charges: ${acc.displayName} (⚡${charges})`);
|
||||
return true;
|
||||
}
|
||||
|
||||
console.log(`⏳ [SEARCH] ${acc.displayName} has no charges (⚡${charges}), cooldown ${cooldown}ms`);
|
||||
if (cooldown < best.cooldown) {
|
||||
best = { cooldown, token: acc.token, name: acc.displayName, idx };
|
||||
console.log(`🔍 [SEARCH] Checking locally ${acc.displayName}: ⚡${localCharges}/${node?.max ?? 0}`);
|
||||
if (localCharges >= threshold) {
|
||||
candidate = { token: acc.token, name: acc.displayName, idx };
|
||||
break;
|
||||
} else {
|
||||
const eta = ChargeModel.predictTimeToReach(acc.token, threshold);
|
||||
if (eta < bestWait) {
|
||||
bestWait = eta;
|
||||
candidate = { token: acc.token, name: acc.displayName, idx };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// None had enough charges: switch to the one with the soonest recharge and return false
|
||||
if (best.token && best.idx !== accountManager.currentIndex) {
|
||||
console.log(`🎯 [SEARCH] Switching to account with soonest recharge: ${best.name} (~${Utils.msToTimeText(best.cooldown)})`);
|
||||
await switchToSpecificAccount(best.token, best.name);
|
||||
if (candidate && ChargeModel.get(candidate.token)?.charges >= threshold) {
|
||||
console.log(`✅ [SEARCH] Local model found eligible account: ${candidate.name}`);
|
||||
const ok = await switchToSpecificAccount(candidate.token, candidate.name);
|
||||
return !!ok;
|
||||
}
|
||||
|
||||
// None eligible yet – do not switch now. Caller may enter cooldown.
|
||||
if (candidate) {
|
||||
console.log(`🕒 [SEARCH] No accounts meet threshold. Best candidate: ${candidate.name} in ~${Utils.msToTimeText(bestWait)}`);
|
||||
} else {
|
||||
console.log('🎯 [SEARCH] Staying on current account for cooldown.');
|
||||
console.log('🕒 [SEARCH] No candidate accounts available.');
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
@@ -10583,6 +10660,14 @@ function getText(key, params) {
|
||||
console.log('🔄 Initial account load from cache...');
|
||||
try {
|
||||
await accountManager.loadAccounts();
|
||||
// Seed and start local charge model regardless of count
|
||||
try {
|
||||
state.chargeModel = ChargeModel;
|
||||
ChargeModel.seedFromAccounts(accountManager.getAllAccounts());
|
||||
ChargeModel.start();
|
||||
console.log('⚡ Local ChargeModel started (tick +1 per 30s for all accounts)');
|
||||
} catch (e) { console.warn('ChargeModel init failed:', e); }
|
||||
|
||||
if (accountManager.getAccountCount() > 0) {
|
||||
console.log(`✅ Loaded ${accountManager.getAccountCount()} cached accounts`);
|
||||
renderAccountsList();
|
||||
|
||||
@@ -595,9 +595,10 @@
|
||||
console.log('✅ Found wasm Module...');
|
||||
return url.split('/').pop();
|
||||
}
|
||||
} catch { }
|
||||
} catch (e) { /* ignore individual fetch errors */ }
|
||||
}
|
||||
console.error(`❌ Could not find Pawtect chunk with string: ${str}`);
|
||||
console.error('❌ Could not find Pawtect chunk among preloaded modules');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Audio notification system
|
||||
|
||||
Reference in New Issue
Block a user