From 4d72113a1ac2e8ee30b0e3290d4e8e340259a228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=A9ng?= <99890979+maiminhdung@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:01:59 +0700 Subject: [PATCH] Update Auto-Image.js --- Auto-Image.js | 319 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 253 insertions(+), 66 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index d1bdca6..8767286 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -5,6 +5,9 @@ TRANSPARENCY_THRESHOLD: 100, WHITE_THRESHOLD: 250, LOG_INTERVAL: 10, + TOKEN_REFRESH_INTERVAL: 300000, // Refresh token every 5 minutes + MAX_RETRIES: 3, + RETRY_DELAY: 5000, THEME: { primary: '#000000', secondary: '#111111', @@ -52,7 +55,9 @@ waitingInit: "Aguardando inicialização...", resizeSuccess: "✅ Imagem redimensionada para {width}x{height}", paintingPaused: "⏸️ Pintura pausada na posição X: {x}, Y: {y}", - captchaNeeded: "❗ Token CAPTCHA necessário. Pinte um pixel manualmente para continuar." + captchaNeeded: "❗ Token CAPTCHA necessário. Pinte um pixel manualmente para continuar.", + tokenRefreshed: "🔄 Token atualizado automaticamente", + retryingRequest: "🔄 Tentando novamente... ({retry}/{max})" }, en: { title: "WPlace Auto-Image", @@ -87,7 +92,9 @@ waitingInit: "Waiting for initialization...", resizeSuccess: "✅ Image resized to {width}x{height}", paintingPaused: "⏸️ Painting paused at position X: {x}, Y: {y}", - captchaNeeded: "❗ CAPTCHA token needed. Paint one pixel manually to continue." + captchaNeeded: "❗ CAPTCHA token needed. Paint one pixel manually to continue.", + tokenRefreshed: "🔄 Token refreshed automatically", + retryingRequest: "🔄 Retrying... ({retry}/{max})" } }; @@ -110,36 +117,134 @@ minimized: false, lastPosition: { x: 0, y: 0 }, estimatedTime: 0, - language: 'en' + language: 'en', + tokenLastRefreshed: Date.now(), + failedAttempts: 0, + sessionHeaders: null }; - // Global variable to store the captured CAPTCHA token. + // Enhanced token management let capturedCaptchaToken = null; + let capturedHeaders = null; + let tokenRefreshTimer = null; - // Intercept the original window.fetch function to "listen" for network requests. + // Enhanced request interceptor with header capture const originalFetch = window.fetch; + const originalXHROpen = XMLHttpRequest.prototype.open; + const originalXHRSend = XMLHttpRequest.prototype.send; + const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader; + + // Intercept XMLHttpRequest for better header capture + XMLHttpRequest.prototype.open = function(method, url) { + this._url = url; + this._method = method; + this._requestHeaders = {}; + return originalXHROpen.apply(this, arguments); + }; + + XMLHttpRequest.prototype.setRequestHeader = function(header, value) { + this._requestHeaders[header] = value; + return originalXHRSetRequestHeader.apply(this, arguments); + }; + + XMLHttpRequest.prototype.send = function(body) { + if (this._url && this._url.includes('backend.wplace.live/s0/pixel/')) { + try { + const payload = JSON.parse(body); + if (payload.t) { + console.log("✅ CAPTCHA Token Captured via XHR:", payload.t); + capturedCaptchaToken = payload.t; + capturedHeaders = {...this._requestHeaders}; + state.tokenLastRefreshed = Date.now(); + + if (document.querySelector('#statusText')?.textContent.includes('CAPTCHA')) { + Utils.showAlert("Token captured successfully! You can start the bot now.", "success"); + updateUI('colorsFound', 'success', { count: state.availableColors.length }); + } + + // Reset failed attempts counter + state.failedAttempts = 0; + + // Start token refresh timer + startTokenRefreshTimer(); + } + } catch (e) {} + } + return originalXHRSend.apply(this, arguments); + }; + + // Enhanced fetch interceptor window.fetch = async (url, options) => { - // Check if the request is for painting a pixel on wplace. - if (typeof url === 'string' && url.includes('https://backend.wplace.live/s0/pixel/')) { + if (typeof url === 'string' && url.includes('backend.wplace.live/s0/pixel/')) { try { const payload = JSON.parse(options.body); - // If the request body contains the 't' field, it's our CAPTCHA token. if (payload.t) { - console.log("✅ CAPTCHA Token Captured:", payload.t); - // Store the token for our bot to use. + console.log("✅ CAPTCHA Token Captured via Fetch:", payload.t); capturedCaptchaToken = payload.t; - // Notify the user that the token is captured and they can start the bot. - if(document.querySelector('#statusText')?.textContent.includes('CAPTCHA')){ - Utils.showAlert("Token captured successfully! You can start the bot now.", "success"); - updateUI('colorsFound', 'success', { count: state.availableColors.length }); + capturedHeaders = options.headers ? {...options.headers} : null; + state.tokenLastRefreshed = Date.now(); + + if (document.querySelector('#statusText')?.textContent.includes('CAPTCHA')) { + Utils.showAlert("Token captured successfully! You can start the bot now.", "success"); + updateUI('colorsFound', 'success', { count: state.availableColors.length }); } + + state.failedAttempts = 0; + startTokenRefreshTimer(); } - } catch (e) { /* Ignore errors if the request body isn't valid JSON */ } + } catch (e) {} } - // Finally, execute the original request, whether we inspected it or not. return originalFetch(url, options); }; + // Token refresh mechanism + function startTokenRefreshTimer() { + if (tokenRefreshTimer) { + clearInterval(tokenRefreshTimer); + } + + tokenRefreshTimer = setInterval(async () => { + if (state.running && capturedCaptchaToken) { + const timeSinceRefresh = Date.now() - state.tokenLastRefreshed; + if (timeSinceRefresh >= CONFIG.TOKEN_REFRESH_INTERVAL) { + console.log("⏰ Token refresh time reached, requesting manual paint..."); + updateUI('captchaNeeded', 'warning'); + Utils.showAlert("Please paint a pixel manually to refresh the token", "warning"); + } + } + }, 60000); // Check every minute + } + + // Auto-recovery mechanism + async function attemptAutoRecovery() { + console.log("🔧 Attempting auto-recovery..."); + + // Try to trigger a Cloudflare challenge solve + try { + // Method 1: Try to fetch the main page to trigger CF challenge + const mainPageResponse = await fetch('https://wplace.live/', { + credentials: 'include', + headers: { + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'User-Agent': navigator.userAgent + } + }); + + // Method 2: Try to get user info which might refresh session + const meResponse = await fetch('https://backend.wplace.live/me', { + credentials: 'include' + }); + + if (meResponse.ok) { + console.log("✅ Session appears to be valid"); + return true; + } + } catch (e) { + console.error("Auto-recovery failed:", e); + } + + return false; + } async function detectLanguage() { try { @@ -262,36 +367,80 @@ } }; - // WPLACE API SERVICE + // Enhanced WPLACE API SERVICE with retry logic const WPlaceService = { - async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color) { - try { - // Construct the payload including the captured 't' token. - const payload = { - coords: [pixelX, pixelY], - colors: [color], - t: capturedCaptchaToken - }; - const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { - method: 'POST', - headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, - credentials: 'include', - body: JSON.stringify(payload) + async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount = 0) { + try { + const payload = { + coords: [pixelX, pixelY], + colors: [color], + t: capturedCaptchaToken + }; + + // Build headers based on captured headers + const headers = { + 'Content-Type': 'text/plain;charset=UTF-8', + ...(capturedHeaders || {}) + }; + + const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { + method: 'POST', + headers: headers, + credentials: 'include', + body: JSON.stringify(payload) + }); + + // Handle different response codes + if (res.status === 403) { + console.error("❌ 403 Forbidden. Token expired or invalid."); + state.failedAttempts++; + + // Try auto-recovery first + if (retryCount < CONFIG.MAX_RETRIES) { + updateUI('retryingRequest', 'warning', { + retry: retryCount + 1, + max: CONFIG.MAX_RETRIES }); - - // If we get a 403 Forbidden error, our token is likely expired. - if (res.status === 403) { - console.error("❌ 403 Forbidden. CAPTCHA token might be invalid or expired."); - capturedCaptchaToken = null; // Invalidate our stored token. - return 'token_error'; // Return a special status to stop the bot. + + const recovered = await attemptAutoRecovery(); + if (recovered) { + await Utils.sleep(CONFIG.RETRY_DELAY); + return await this.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount + 1); } - - const data = await res.json(); - return data?.painted === 1; - } catch(e) { - console.error("Paint request failed:", e); - return false; + } + + capturedCaptchaToken = null; + return 'token_error'; } + + if (res.status === 429) { + console.warn("⚠️ Rate limited. Waiting before retry..."); + await Utils.sleep(CONFIG.RETRY_DELAY * 2); + if (retryCount < CONFIG.MAX_RETRIES) { + return await this.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount + 1); + } + return false; + } + + const data = await res.json(); + + // Reset failed attempts on success + if (data?.painted === 1) { + state.failedAttempts = 0; + } + + return data?.painted === 1; + } catch(e) { + console.error("Paint request failed:", e); + + // Retry on network errors + if (retryCount < CONFIG.MAX_RETRIES) { + await Utils.sleep(CONFIG.RETRY_DELAY); + return await this.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount + 1); + } + + return false; + } }, async getCharges() { @@ -374,6 +523,7 @@ }, { color: palette[0], distance: Utils.colorDistance(rgb, palette[0].rgb) }).color.id; } + // Create UI function with enhanced token status display async function createUI() { await detectLanguage(); @@ -397,7 +547,7 @@ position: fixed; top: 20px; right: 20px; - width: 300px; + width: 320px; background: ${CONFIG.THEME.primary}; border: 1px solid ${CONFIG.THEME.accent}; border-radius: 8px; @@ -543,22 +693,25 @@ background: rgba(255, 165, 0, 0.1); color: orange; } - #paintEffect { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - border-radius: 8px; - } - .position-info { - font-size: 13px; - margin-top: 5px; + .token-status { + display: flex; + align-items: center; + gap: 5px; padding: 5px; background: ${CONFIG.THEME.secondary}; border-radius: 4px; - text-align: center; + margin-top: 10px; + font-size: 12px; + } + .token-status-indicator { + width: 8px; + height: 8px; + border-radius: 50%; + background: ${CONFIG.THEME.error}; + } + .token-status-indicator.active { + background: ${CONFIG.THEME.success}; + animation: pulse 2s infinite; } .wplace-minimized .wplace-content { display: none; @@ -667,6 +820,11 @@