Fixed situation, when bot are waiting for charges to restore, when there another acc with charges, by simply switching acc when done with painting

(cherry picked from commit ca4e5244ba)
This commit is contained in:
JustEngineer authored and SKProCH committed 2025-10-12 15:46:13 +03:00
1 parent 2b3094dbde
commit 8695ee1d0c
2 files changed
+294 -205

No files matched your search

+35 -28
View File
@@ -7517,38 +7517,16 @@
return;
}
swapAccountTrigger(nextToken);
let maxRetries = 20;
let retryCount = 0;
let swapSuccess = false;
while (!swapSuccess && retryCount < maxRetries) {
console.log(`⏳ Waiting for account swap... (Attempt ${retryCount + 1}/${maxRetries})`);
// Wait for a short period before checking.
await new Promise(resolve => setTimeout(resolve, 1000));
try {
await fetchAccount();
console.log("✅ Account swap confirmed.");
swapSuccess = true;
} catch (error) {
console.warn("❌ Account swap not yet successful. Retrying...", error);
retryCount++;
}
}
const swapSuccess = await swapAccountTrigger(nextToken);
if (swapSuccess) {
const { charges, cooldown } = await WPlaceService.getCharges();
state.currentCharges = Math.floor(charges);
state.cooldown = cooldown;
Utils.performSmartSave();
updateStats();
} else {
console.error("❌ Failed to swap account after multiple retries. Stopping loop.");
console.error("❌ Failed to swap account after confirmation timeout. Stopping loop.");
state.stopFlag = true;
}
}
@@ -7800,7 +7778,7 @@
const token = accountsTokens[i];
console.log(`🔄 Refreshing account ${i + 1}/${accountsTokens.length} for data collection only...`);
swapAccountTrigger(token);
await swapAccountTrigger(token);
let retries = 0;
let swapped = false;
@@ -7834,7 +7812,7 @@
if (originalToken) {
console.log("🔄 Switching back to original account after data collection...");
swapAccountTrigger(originalToken);
await swapAccountTrigger(originalToken);
}
await Utils.sleep(1000);
@@ -8486,15 +8464,44 @@
console.error("An error occurred during the purchase:", e);
}
}
function swapAccountTrigger(token) {
async function waitForCookieSet(timeout = 10000) {
return new Promise((resolve, reject) => {
const onMessage = (event) => {
if (event.source !== window) return;
const data = event.data || {};
if (data.type === 'cookieSet') {
window.removeEventListener('message', onMessage);
clearTimeout(timer);
resolve(true);
}
};
const timer = setTimeout(() => {
window.removeEventListener('message', onMessage);
reject(new Error('cookieSet timeout'));
}, timeout);
window.addEventListener('message', onMessage);
});
}
async function swapAccountTrigger(token) {
localStorage.removeItem("lp");
if (!token) return;
if (!token) {
console.error('❌ Cannot swap account: token is null or undefined');
return false;
}
console.log("Sending token to extension...");
window.postMessage({
source: 'my-userscript',
type: 'setCookie',
value: token
}, '*');
try {
await waitForCookieSet(10000);
console.log('✅ Cookie set confirmed');
return true;
} catch (e) {
console.warn('⚠️ No cookieSet confirmation:', e.message);
return false;
}
}
async function getAccounts() {
return new Promise((resolve, reject) => {
+259 -177
View File
@@ -1777,8 +1777,9 @@ function getText(key, params) {
async getCharges() {
try {
const res = await fetch("https://backend.wplace.live/me", {
const res = await fetch(`https://backend.wplace.live/me?_=${Date.now()}` , {
credentials: "include",
cache: 'no-store'
})
const data = await res.json()
return {
@@ -1802,8 +1803,9 @@ function getText(key, params) {
async fetchCheck() {
try {
const res = await fetch("https://backend.wplace.live/me", {
const res = await fetch(`https://backend.wplace.live/me?_=${Date.now()}` , {
credentials: "include",
cache: 'no-store'
})
const data = await res.json()
return {
@@ -8606,72 +8608,19 @@ function getText(key, params) {
break;
}
} else {
// Debug current state
const totalAccounts = accountManager.getAccountCount();
console.log(`📊 Account Status - Current index: ${accountManager.currentIndex}, Total accounts: ${totalAccounts}`);
// Check if we're at the last account in the sequence
const isLastAccount = accountManager.currentIndex >= totalAccounts - 1;
console.log(`🔍 Is last account? ${isLastAccount} (index ${accountManager.currentIndex} of ${totalAccounts})`);
if (!isLastAccount && totalAccounts > 1) {
// Update current account status before switching
console.log('📊 Updating current account status before switch...');
await updateCurrentAccountInList();
// Switch to next account immediately (no cooldown) - only if we have multiple accounts
const nextAccount = accountManager.getNextAccount();
console.log(`🔄 Switching to next account: ${nextAccount?.displayName} (${accountManager.currentIndex + 2}/${totalAccounts})`);
const switchResult = await switchToNextAccount(accounts);
if (!switchResult) {
console.log('❌ Account switch failed, stopping');
state.stopFlag = true;
break;
}
// Continue painting with new account immediately
continue;
} else if (totalAccounts === 1) {
// Only one account available - use cooldown and continue with same account
console.log('ℹ️ Only one account available, entering cooldown period');
const cooldownResult = await executeCooldownPeriod();
if (cooldownResult === 'stopped') break;
console.log('✅ Cooldown complete, continuing with same account');
continue;
} else {
// Last account reached - use cooldown then switch to first account
console.log('⏱️ Last account reached, entering cooldown period');
console.log(`📊 Current account: index ${accountManager.currentIndex}, Last account: index ${totalAccounts - 1}`);
const cooldownResult = await executeCooldownPeriod();
if (cooldownResult === 'stopped') break;
// After cooldown, switch to first account
console.log('🔁 Cooldown complete, switching to first account');
console.log(`🔄 Before switch - Current index: ${accountManager.currentIndex}, Target: index 0`);
const firstAccountInfo = accountManager.getAccountByIndex(0);
const firstAccountToken = firstAccountInfo?.token;
if (!firstAccountToken) {
console.log('❌ First account token not found, stopping');
state.stopFlag = true;
break;
}
// Reset to first position
accountManager.setCurrentIndex(0);
const switchResult = await switchToSpecificAccount(firstAccountToken, firstAccountInfo.displayName);
if (!switchResult) {
console.log('❌ Switch to ID 1 failed, stopping');
state.stopFlag = true;
break;
}
console.log(`✅ Successfully switched to ID 1 (${firstAccountInfo.displayName}). Cycle restarted.`);
// Continue painting with first account
// Try to find an account with enough charges to continue painting
const minRequired = Math.max(1, state.cooldownChargeThreshold || 1);
console.log(`🔎 Searching for account with ≥${minRequired} charges...`);
const switched = await selectAndSwitchToAccountWithCharges(minRequired);
if (switched) {
console.log('✅ Switched to an account with sufficient charges, continuing painting immediately');
continue;
}
// If none had enough charges, enter cooldown on the best available account
console.log('🕒 No accounts have enough charges. Entering cooldown on the account with the soonest recharge...');
const cooldownResult = await executeCooldownPeriod();
if (cooldownResult === 'stopped') break;
continue;
}
}
}
@@ -9976,13 +9925,43 @@ function getText(key, params) {
type: 'setCookie',
value: token
}, '*');
console.log('✅ setCookie message sent successfully');
return true;
} catch (error) {
console.error('❌ Failed to send setCookie message:', error);
return false;
}
// Wait for background confirmation that cookie was set
const confirmed = await new Promise((resolve) => {
let settled = false;
const timer = setTimeout(() => {
if (!settled) {
settled = true;
console.warn('⚠️ cookieSet confirmation timeout');
resolve(false);
}
}, 10000);
function onMessage(event) {
if (event.source !== window) return;
const data = event.data || {};
if (data.type === 'cookieSet') {
if (!settled) {
settled = true;
clearTimeout(timer);
window.removeEventListener('message', onMessage);
resolve(true);
}
}
}
window.addEventListener('message', onMessage);
});
if (confirmed) {
console.log('✅ Cookie set confirmed');
} else {
console.warn('⚠️ Proceeding without cookie confirmation');
}
return true;
}
async function getAccounts() {
return new Promise((resolve, reject) => {
@@ -10114,10 +10093,16 @@ function getText(key, params) {
await switchToSpecificAccount(originalCurrentAccount.token, originalCurrentAccount.displayName);
//await Utils.sleep(300);
// Mark it as current again
// Mark it as current again and sync index
accountManager.updateAccountData(originalCurrentAccount.token, {
isCurrent: true
});
const list = accountManager.getAllAccounts();
const idxOriginal = list.findIndex(acc => acc.token === originalCurrentAccount.token);
if (idxOriginal !== -1 && typeof accountManager.setCurrentIndex === 'function') {
accountManager.setCurrentIndex(idxOriginal);
state.accountIndex = idxOriginal;
}
} catch (error) {
console.warn(`⚠️ [FETCH] Failed to switch back to original account:`, error);
}
@@ -10147,95 +10132,78 @@ function getText(key, params) {
async function updateCurrentAccountInList() {
if (accountManager.getAccountCount() === 0) return;
// Find current account in the list and update its charges
const currentAccount = accountManager.getCurrentAccount();
if (currentAccount) {
const { charges, cooldown, droplets } = await WPlaceService.getCharges();
state.displayCharges = Math.floor(charges);
state.preciseCurrentCharges = charges;
try {
// Always trust backend /me and then map by ID to avoid mixing data between tokens
const me = await WPlaceService.getCharges();
state.displayCharges = Math.floor(me.charges);
state.preciseCurrentCharges = me.charges;
await updateStats();
// Update the current account data in AccountManager
accountManager.updateAccountData(currentAccount.token, {
Charges: Math.floor(state.displayCharges || state.preciseCurrentCharges || 0),
Max: state.maxCharges,
Droplets: Math.floor(droplets)
});
const accounts = accountManager.getAllAccounts();
const idx = accounts.findIndex(acc => acc.ID === me.id);
const targetToken = idx !== -1 ? accounts[idx].token : accountManager.getCurrentAccount()?.token;
// Re-render the account list to show updated charges
renderAccountsList();
if (targetToken) {
accountManager.updateAccountData(targetToken, {
Charges: Math.floor(state.displayCharges || 0),
Max: Math.floor(me.max || state.maxCharges || 0),
Droplets: Math.floor(me.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();
}
} catch (e) {
console.warn('⚠️ updateCurrentAccountInList failed:', e);
}
}
// Function to update current account spotlight when switching during painting
async function updateCurrentAccountSpotlight() {
if (accountManager.getAccountCount() === 0) return;
// await Utils.sleep(500); // Wait a bit for the switch to take effect
try {
const currentAccountData = await WPlaceService.getCharges();
console.log("Current account after switch:", currentAccountData);
console.log(`🔍 Switched to account with ID: ${currentAccountData.id}`);
const me = await WPlaceService.getCharges();
console.log("Current account after switch:", me);
console.log(`🔍 Switched to account with ID: ${me.id}`);
// Find the current account in AccountManager and update it
const accounts = accountManager.getAllAccounts();
const currentAccount = accounts.find(acc => acc.ID === currentAccountData.id);
const idx = accounts.findIndex(acc => acc.ID === me.id);
if (currentAccount) {
const currentAccountInfo = await WPlaceService.fetchCheck();
if (idx !== -1) {
const currentAccount = accounts[idx];
const info = await WPlaceService.fetchCheck();
// Sync manager index and flags to actual account
if (typeof accountManager.setCurrentIndex === 'function') {
accountManager.setCurrentIndex(idx);
} else {
accounts.forEach((acc, i) => (acc.isCurrent = i === idx));
}
// Update account data in AccountManager
accountManager.updateAccountData(currentAccount.token, {
isCurrent: true,
Charges: Math.floor(currentAccountData.charges),
Max: Math.floor(currentAccountData.max),
Droplets: Math.floor(currentAccountData.droplets),
displayName: currentAccountInfo.Username || currentAccountInfo.name || currentAccount.displayName
Charges: Math.floor(me.charges),
Max: Math.floor(me.max),
Droplets: Math.floor(me.droplets),
displayName: info.Username || info.name || currentAccount.displayName
});
console.log(`🎯 Updated current account spotlight: ${currentAccount.displayName}`);
// Mirror to UI state for consistency
state.displayCharges = Math.floor(me.charges);
state.preciseCurrentCharges = me.charges;
state.cooldown = me.cooldown;
state.accountIndex = idx;
// Check for autobuy after account is loaded
if (CONFIG.autoBuyToggle && Math.floor(currentAccountData.droplets) > 500) {
console.log(`💰 Account has ${Math.floor(currentAccountData.droplets)} droplets (>500), triggering autobuy...`);
try {
const purchaseResult = await purchase(CONFIG.autoBuy);
if (purchaseResult == 2) {
console.log('✅ Autobuy successful after account switch');
// Update charges after purchase
const updatedCharges = await WPlaceService.getCharges();
accountManager.updateAccountData(currentAccount.token, {
Charges: Math.floor(updatedCharges.charges),
Droplets: Math.floor(updatedCharges.droplets)
});
// Re-render account list to show updated status after purchase
renderAccountsList();
} else {
console.log('❌ Autobuy failed after account switch');
}
} catch (error) {
console.error('❌ Error during autobuy after account switch:', error);
}
} else if (CONFIG.autoBuyToggle) {
console.log(`💰 Account has ${Math.floor(currentAccountData.droplets)} droplets (≤500), skipping autobuy`);
}
// Re-render the account list to show new current account
renderAccountsList();
console.log(`🔒 PRESERVING currentActiveIndex: ${state.currentActiveIndex} (do not recalculate from isCurrent flag)`);
console.log(`� Account at currentActiveIndex ${state.currentActiveIndex}: ${state.originalAccountOrder[state.currentActiveIndex]?.displayName} (ID ${state.originalAccountOrder[state.currentActiveIndex]?.orderId})`);
// Update accountIndex to match original array position
const originalArrayIndex = state.allAccountsInfo.findIndex(acc => acc.isCurrent);
if (originalArrayIndex !== -1) {
state.accountIndex = originalArrayIndex;
}
console.log(`📊 Final state: activeIndex=${state.currentActiveIndex}, accountIndex=${state.accountIndex}, orderId=${newCurrentAccount.orderId}`);
console.warn(`⚠️ Could not find account ID ${newCurrentAccount.orderId} in switching order`);
console.warn(`⚠️ Could not find switched account with ID ${currentAccountData.id} in account list`);
console.log(`🎯 Updated current account spotlight: ${currentAccount.displayName}`);
} else {
console.warn(`⚠️ Could not find switched account with ID ${me.id} in account list`);
}
// Re-render the account list to show new current account
@@ -10359,6 +10327,10 @@ function getText(key, params) {
return false;
}
// Capture the pre-switch (current) account to detect stale reads
const preSwitchAccount = accountManager.getCurrentAccount();
const previousKnownId = preSwitchAccount?.ID || null;
// Get next account using simplified manager
const nextAccount = accountManager.switchToNext();
if (!nextAccount) {
@@ -10385,8 +10357,46 @@ function getText(key, params) {
console.log(`✅ [SWITCH] Successfully switched to ${nextAccount.displayName}`);
// Wait a moment for the switch to fully complete
// await new Promise(resolve => setTimeout(resolve, 1000));
// Verify backend reflects the new account to avoid stale reads
// Strategy: poll /me until we see an ID different from the pre-switch account's ID
// Only then accept and (if needed) assign the next account's ID.
let verifiedId = null;
for (let attempt = 1; attempt <= 8 && !verifiedId; attempt++) {
try {
const me = await WPlaceService.getCharges();
const curId = me?.id;
if (!curId) {
await Utils.sleep(500);
continue;
}
if (previousKnownId && curId === previousKnownId) {
console.log(`⏳ [SWITCH] Still seeing previous ID ${curId} (attempt ${attempt}/8), waiting...`);
await Utils.sleep(500);
continue;
}
// If we had a stored ID for the target and it's different from what we see now,
// prefer the live value (curId) and update the stored ID.
if (nextAccount.ID && nextAccount.ID !== curId) {
console.log(`🔁 [SWITCH] Updating stored ID for ${nextAccount.displayName}: ${nextAccount.ID} → ${curId}`);
}
verifiedId = curId;
break;
} catch (e) {
await Utils.sleep(500);
}
}
if (!verifiedId) {
console.warn('⚠️ [SWITCH] Could not verify account change via /me after cookie set; skipping ID update but proceeding cautiously.');
} else {
// Persist verified ID for the next account
if (nextAccount.ID !== verifiedId) {
accountManager.updateAccountData(nextAccount.token, { ID: verifiedId });
}
}
// Update the account status and UI after successful switch
await updateCurrentAccountInList();
@@ -10401,54 +10411,77 @@ 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}`);
if (!token) {
console.error('❌ [SPECIFIC SWITCH] Missing token');
return false;
}
console.log(`🔑 [SPECIFIC SWITCH] Using token: ${token.substring(0, 20)}...`);
await swapAccountTrigger(token);
// Capture previous account ID to detect stale responses
let previousId = null;
try {
const prev = await WPlaceService.getCharges();
previousId = prev?.id || null;
} catch {}
let maxRetries = 20;
let retryCount = 0;
let swapSuccess = false;
while (!swapSuccess && retryCount < maxRetries) {
console.log(`⏳ [SPECIFIC SWITCH] Waiting for account swap... (Attempt ${retryCount + 1}/${maxRetries})`);
//await new Promise(resolve => setTimeout(resolve, 1000));
const ok = await swapAccountTrigger(token);
if (!ok) {
console.error('❌ [SPECIFIC SWITCH] Cookie confirmation failed');
return false;
}
// Poll /me until it reflects a different ID than the previous account
let verifiedId = null;
for (let attempt = 1; attempt <= 8 && !verifiedId; attempt++) {
try {
// await fetchAccount();
console.log('✅ [SPECIFIC SWITCH] Account swap confirmed.');
swapSuccess = true;
} catch (error) {
console.warn('❌ [SPECIFIC SWITCH] Account swap not yet successful. Retrying...', error);
retryCount++;
if (retryCount % 5 === 0) {
console.log('🔄 [SPECIFIC SWITCH] Re-triggering account swap...');
await swapAccountTrigger(token);
await new Promise(resolve => setTimeout(resolve, 2000));
const me = await WPlaceService.getCharges();
const curId = me?.id;
if (!curId) {
await Utils.sleep(500);
continue;
}
if (previousId && curId === previousId) {
console.log(`⏳ [SPECIFIC SWITCH] Still seeing previous ID ${curId} (attempt ${attempt}/8), waiting...`);
await Utils.sleep(500);
continue;
}
verifiedId = curId;
break;
} catch {
await Utils.sleep(500);
}
}
if (swapSuccess) {
const { charges, cooldown } = await WPlaceService.getCharges();
state.displayCharges = Math.floor(charges);
state.preciseCurrentCharges = charges;
state.cooldown = cooldown;
Utils.performSmartSave();
await updateStats();
// Update account data in manager
accountManager.updateAccountData({ charges, cooldown });
// Update the account status and UI after successful switch
await updateCurrentAccountSpotlight();
console.log(`✅ [SPECIFIC SWITCH] Successfully switched to ${accountName} with ${Math.floor(charges)} charges`);
return true;
if (!verifiedId) {
console.warn('⚠️ [SPECIFIC SWITCH] Could not verify account change via /me; proceeding.');
} else {
console.error(`❌ [SPECIFIC SWITCH] Failed to swap to ${accountName} after multiple retries.`);
return false;
// Persist ID and mark as current in AccountManager
accountManager.updateAccountData(token, { ID: verifiedId, isCurrent: true });
}
// Sync manager index to the token we explicitly switched to
try {
const list = accountManager.getAllAccounts();
const idxByToken = list.findIndex(acc => acc.token === token);
if (idxByToken !== -1 && typeof accountManager.setCurrentIndex === 'function') {
accountManager.setCurrentIndex(idxByToken);
state.accountIndex = idxByToken;
}
} catch {}
// Fetch fresh stats for UI/state
const { charges, cooldown, droplets, max } = await WPlaceService.getCharges();
state.displayCharges = Math.floor(charges);
state.preciseCurrentCharges = charges;
state.cooldown = cooldown;
Utils.performSmartSave();
await updateStats();
// Update the account status and UI after successful switch
await updateCurrentAccountSpotlight();
console.log(`✅ [SPECIFIC SWITCH] Switched to ${accountName} with ${Math.floor(charges)} charges`);
return true;
}
// Wait for dependencies before initializing UI
@@ -10492,6 +10525,55 @@ function getText(key, params) {
return createUI();
}
// Helper: iterate over accounts to find one with enough charges; otherwise pick best cooldown
async function selectAndSwitchToAccountWithCharges(minRequired = 1) {
try {
const total = accountManager.getAccountCount();
if (total <= 1) return false;
const startIdx = accountManager.currentIndex;
let best = { cooldown: Infinity, token: null, name: null, idx: -1 };
for (let step = 1; step <= total - 1; step++) {
const idx = (startIdx + step) % total;
const acc = accountManager.getAccountByIndex(idx);
if (!acc || !acc.token) continue;
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 me = await WPlaceService.getCharges();
const charges = Math.floor(me?.charges || 0);
const cooldown = Math.max(0, Number(me?.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 };
}
}
// 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);
} else {
console.log('🎯 [SEARCH] Staying on current account for cooldown.');
}
return false;
} catch (e) {
console.warn('⚠️ selectAndSwitchToAccountWithCharges failed:', e);
return false;
}
}
waitForDependenciesAndInitialize().then(() => {
// Generate token automatically after UI is ready
setTimeout(initializeTokenGenerator, 1000);