// eslint-disable-next-line prettier/prettier ; (async () => { // CONFIGURATION CONSTANTS const CONFIG = { COOLDOWN_DEFAULT: 31000, TRANSPARENCY_THRESHOLD: 100, WHITE_THRESHOLD: 250, LOG_INTERVAL: 10, PAINTING_SPEED: { MIN: 1, MAX: 1000, DEFAULT: 5, }, TOKEN_SOURCE: 'generator', // "generator", "manual", or "hybrid" AUTONOMOUS_MODE: true, // Enable autonomous operation AUTO_TOKEN_REFRESH: true, // Automatically refresh tokens TOKEN_PRELOAD_BUFFER: 60000, // Preload tokens 1 minute before expiry MAX_RETRIES: 10, RETRY_DELAY_BASE: 1000, // Auto-batch configuration AUTO_BATCH_ENABLED: true, // Enable auto pixel batch sizing MAX_BATCH_SIZE: 500, // Maximum pixels per batch MIN_BATCH_SIZE: 1, // Minimum pixels per batch BATCH_OPTIMIZATION: true, // Optimize batch size based on detected damage COLOR_MAP: { 0: { id: 1, name: 'Black', rgb: { r: 0, g: 0, b: 0 } }, 1: { id: 2, name: 'Dark Gray', rgb: { r: 60, g: 60, b: 60 } }, 2: { id: 3, name: 'Gray', rgb: { r: 120, g: 120, b: 120 } }, 3: { id: 4, name: 'Light Gray', rgb: { r: 210, g: 210, b: 210 } }, 4: { id: 5, name: 'White', rgb: { r: 255, g: 255, b: 255 } }, 5: { id: 6, name: 'Deep Red', rgb: { r: 96, g: 0, b: 24 } }, 6: { id: 7, name: 'Red', rgb: { r: 237, g: 28, b: 36 } }, 7: { id: 8, name: 'Orange', rgb: { r: 255, g: 127, b: 39 } }, 8: { id: 9, name: 'Gold', rgb: { r: 246, g: 170, b: 9 } }, 9: { id: 10, name: 'Yellow', rgb: { r: 249, g: 221, b: 59 } }, 10: { id: 11, name: 'Light Yellow', rgb: { r: 255, g: 250, b: 188 } }, 11: { id: 12, name: 'Dark Green', rgb: { r: 14, g: 185, b: 104 } }, 12: { id: 13, name: 'Green', rgb: { r: 19, g: 230, b: 123 } }, 13: { id: 14, name: 'Light Green', rgb: { r: 135, g: 255, b: 94 } }, 14: { id: 15, name: 'Dark Teal', rgb: { r: 12, g: 129, b: 110 } }, 15: { id: 16, name: 'Teal', rgb: { r: 16, g: 174, b: 166 } }, 16: { id: 17, name: 'Light Teal', rgb: { r: 19, g: 225, b: 190 } }, 17: { id: 20, name: 'Cyan', rgb: { r: 96, g: 247, b: 242 } }, 18: { id: 44, name: 'Light Cyan', rgb: { r: 187, g: 250, b: 242 } }, 19: { id: 18, name: 'Dark Blue', rgb: { r: 40, g: 80, b: 158 } }, 20: { id: 19, name: 'Blue', rgb: { r: 64, g: 147, b: 228 } }, 21: { id: 21, name: 'Indigo', rgb: { r: 107, g: 80, b: 246 } }, 22: { id: 22, name: 'Light Indigo', rgb: { r: 153, g: 177, b: 251 } }, 23: { id: 23, name: 'Dark Purple', rgb: { r: 120, g: 12, b: 153 } }, 24: { id: 24, name: 'Purple', rgb: { r: 170, g: 56, b: 185 } }, 25: { id: 25, name: 'Light Purple', rgb: { r: 224, g: 159, b: 249 } }, 26: { id: 26, name: 'Dark Pink', rgb: { r: 203, g: 0, b: 122 } }, 27: { id: 27, name: 'Pink', rgb: { r: 236, g: 31, b: 128 } }, 28: { id: 28, name: 'Light Pink', rgb: { r: 243, g: 141, b: 169 } }, 29: { id: 29, name: 'Dark Brown', rgb: { r: 104, g: 70, b: 52 } }, 30: { id: 30, name: 'Brown', rgb: { r: 149, g: 104, b: 42 } }, 31: { id: 31, name: 'Beige', rgb: { r: 248, g: 178, b: 119 } }, 32: { id: 52, name: 'Light Beige', rgb: { r: 255, g: 197, b: 165 } }, 33: { id: 32, name: 'Medium Gray', rgb: { r: 170, g: 170, b: 170 } }, 34: { id: 33, name: 'Dark Red', rgb: { r: 165, g: 14, b: 30 } }, 35: { id: 34, name: 'Light Red', rgb: { r: 250, g: 128, b: 114 } }, 36: { id: 35, name: 'Dark Orange', rgb: { r: 228, g: 92, b: 26 } }, 37: { id: 37, name: 'Dark Goldenrod', rgb: { r: 156, g: 132, b: 49 } }, 38: { id: 38, name: 'Goldenrod', rgb: { r: 197, g: 173, b: 49 } }, 39: { id: 39, name: 'Light Goldenrod', rgb: { r: 232, g: 212, b: 95 } }, 40: { id: 40, name: 'Dark Olive', rgb: { r: 74, g: 107, b: 58 } }, 41: { id: 41, name: 'Olive', rgb: { r: 90, g: 148, b: 74 } }, 42: { id: 42, name: 'Light Olive', rgb: { r: 132, g: 197, b: 115 } }, 43: { id: 43, name: 'Dark Cyan', rgb: { r: 15, g: 121, b: 159 } }, 44: { id: 45, name: 'Light Blue', rgb: { r: 125, g: 199, b: 255 } }, 45: { id: 46, name: 'Dark Indigo', rgb: { r: 77, g: 49, b: 184 } }, 46: { id: 47, name: 'Dark Slate Blue', rgb: { r: 74, g: 66, b: 132 } }, 47: { id: 48, name: 'Slate Blue', rgb: { r: 122, g: 113, b: 196 } }, 48: { id: 49, name: 'Light Slate Blue', rgb: { r: 181, g: 174, b: 241 } }, 49: { id: 53, name: 'Dark Peach', rgb: { r: 155, g: 82, b: 73 } }, 50: { id: 54, name: 'Peach', rgb: { r: 209, g: 128, b: 120 } }, 51: { id: 55, name: 'Light Peach', rgb: { r: 250, g: 182, b: 164 } }, 52: { id: 50, name: 'Light Brown', rgb: { r: 219, g: 164, b: 99 } }, 53: { id: 56, name: 'Dark Tan', rgb: { r: 123, g: 99, b: 82 } }, 54: { id: 57, name: 'Tan', rgb: { r: 156, g: 132, b: 107 } }, 55: { id: 36, name: 'Light Tan', rgb: { r: 214, g: 181, b: 148 } }, 56: { id: 51, name: 'Dark Beige', rgb: { r: 209, g: 128, b: 81 } }, 57: { id: 61, name: 'Dark Stone', rgb: { r: 109, g: 100, b: 63 } }, 58: { id: 62, name: 'Stone', rgb: { r: 148, g: 140, b: 107 } }, 59: { id: 63, name: 'Light Stone', rgb: { r: 205, g: 197, b: 158 } }, 60: { id: 58, name: 'Dark Slate', rgb: { r: 51, g: 57, b: 65 } }, 61: { id: 59, name: 'Slate', rgb: { r: 109, g: 117, b: 141 } }, 62: { id: 60, name: 'Light Slate', rgb: { r: 179, g: 185, b: 209 } }, 63: { id: 0, name: 'Transparent', rgb: null }, }, }; // GLOBAL STATE const state = { running: false, imageLoaded: false, totalPixels: 0, paintedPixels: 0, availableColors: [], displayCharges: 0, maxCharges: 1, cooldown: CONFIG.COOLDOWN_DEFAULT, imageData: null, stopFlag: false, startPosition: null, region: null, paintWhitePixels: true, paintTransparentPixels: true, // Changed to true to fix transparent pixel detection autoRepairEnabled: false, autoRepairInterval: 30, autoRepairTimer: null, debugLogs: [], customTransparencyThreshold: CONFIG.TRANSPARENCY_THRESHOLD, customWhiteThreshold: CONFIG.WHITE_THRESHOLD, tokenSource: CONFIG.TOKEN_SOURCE, autonomousMode: CONFIG.AUTONOMOUS_MODE, autoTokenRefresh: CONFIG.AUTO_TOKEN_REFRESH, tokenPreloadBuffer: CONFIG.TOKEN_PRELOAD_BUFFER, retryCount: 0, tokenRetryTimer: null, tokenPreloadTimer: null, windowMinimized: false, lastAttackState: null, initialSetupComplete: false, // Auto-batch state autoBatchEnabled: CONFIG.AUTO_BATCH_ENABLED, currentBatchSize: CONFIG.MIN_BATCH_SIZE, batchOptimization: CONFIG.BATCH_OPTIMIZATION, }; // Random string generator const randStr = (len, chars = 'abcdefghijklmnopqrstuvwxyz0123456789') => [...Array(len)].map(() => chars[(crypto?.getRandomValues?.(new Uint32Array(1))[0] % chars.length) || Math.floor(Math.random() * chars.length)]).join(''); const fpStr32 = randStr(32); // Enhanced Turnstile token handling - Actualizado con la lΓ³gica de new.txt lol let turnstileToken = null; let tokenExpiryTime = 0; let tokenGenerationInProgress = false; let _resolveToken = null; let tokenPromise = new Promise((resolve) => { _resolveToken = resolve; }); let retryCount = 0; const MAX_RETRIES = 10; const MAX_BATCH_RETRIES = 10; const TOKEN_LIFETIME = 240000; // 4 minutes (tokens typically last 5 min, use 4 for safety) function setTurnstileToken(token) { if (_resolveToken) { _resolveToken(token); _resolveToken = null; } turnstileToken = token; tokenExpiryTime = Date.now() + TOKEN_LIFETIME; console.log('βœ… Turnstile token set successfully'); Utils.addDebugLog('Token cached successfully', 'success'); } function isTokenValid() { return turnstileToken && Date.now() < tokenExpiryTime; } function invalidateToken() { turnstileToken = null; tokenExpiryTime = 0; console.log('πŸ—‘οΈ Token invalidated, will force fresh generation'); Utils.addDebugLog('Token invalidated, will force fresh generation', 'warning'); } async function ensureToken(forceRefresh = false) { // Return cached token if still valid and not forcing refresh if (isTokenValid() && !forceRefresh) { return turnstileToken; } // Invalidate token if forcing refresh if (forceRefresh) invalidateToken(); // Avoid multiple simultaneous token generations if (tokenGenerationInProgress) { console.log('πŸ”„ Token generation already in progress, waiting...'); await Utils.sleep(2000); return isTokenValid() ? turnstileToken : null; } tokenGenerationInProgress = true; try { console.log('πŸ”„ Token expired or missing, generating new one...'); const token = await handleCaptchaWithRetry(); if (token && token.length > 20) { setTurnstileToken(token); console.log('βœ… Token captured and cached successfully'); return token; } console.log('⚠️ Invisible Turnstile failed, forcing browser automation...'); const fallbackToken = await handleCaptchaFallback(); if (fallbackToken && fallbackToken.length > 20) { setTurnstileToken(fallbackToken); console.log('βœ… Fallback token captured successfully'); return fallbackToken; } console.log('❌ All token generation methods failed'); return null; } finally { tokenGenerationInProgress = false; } } async function handleCaptchaWithRetry() { const startTime = performance.now(); try { const { sitekey, token: preGeneratedToken } = await Utils.obtainSitekeyAndToken(); if (!sitekey) { throw new Error('No valid sitekey found'); } console.log('πŸ”‘ Using sitekey:', sitekey); if (typeof window !== 'undefined' && window.navigator) { console.log( '🧭 UA:', window.navigator.userAgent.substring(0, 50) + '...', 'Platform:', window.navigator.platform ); } let token = null; if ( preGeneratedToken && typeof preGeneratedToken === 'string' && preGeneratedToken.length > 20 ) { console.log('♻️ Reusing pre-generated Turnstile token'); token = preGeneratedToken; } else { if (isTokenValid()) { console.log('♻️ Using existing cached token (from previous session)'); token = turnstileToken; } else { console.log('πŸ” Generating new token with executeTurnstile...'); token = await Utils.executeTurnstile(sitekey, 'paint'); if (token) setTurnstileToken(token); } } if (token && typeof token === 'string' && token.length > 20) { const elapsed = Math.round(performance.now() - startTime); console.log(`βœ… Turnstile token generated successfully in ${elapsed}ms`); return token; } else { throw new Error(`Invalid or empty token received - Length: ${token?.length || 0}`); } } catch (error) { const elapsed = Math.round(performance.now() - startTime); console.error(`❌ Turnstile token generation failed after ${elapsed}ms:`, error); throw error; } } async function handleCaptchaFallback() { return new Promise(async (resolve, reject) => { try { // Ensure we have a fresh promise to await for a new token capture if (!_resolveToken) { tokenPromise = new Promise((res) => { _resolveToken = res; }); } const timeoutPromise = Utils.sleep(20000).then(() => reject(new Error('Auto-CAPTCHA timed out.')) ); const solvePromise = (async () => { const mainPaintBtn = await Utils.waitForSelector( 'button.btn.btn-primary.btn-lg, button.btn-primary.sm\\:btn-xl', 200, 10000 ); if (!mainPaintBtn) throw new Error('Could not find the main paint button.'); mainPaintBtn.click(); await Utils.sleep(500); const transBtn = await Utils.waitForSelector('button#color-0', 200, 5000); if (!transBtn) throw new Error('Could not find the transparent color button.'); transBtn.click(); await Utils.sleep(500); const canvas = await Utils.waitForSelector('canvas', 200, 5000); if (!canvas) throw new Error('Could not find the canvas element.'); canvas.setAttribute('tabindex', '0'); canvas.focus(); const rect = canvas.getBoundingClientRect(); const centerX = Math.round(rect.left + rect.width / 2); const centerY = Math.round(rect.top + rect.height / 2); canvas.dispatchEvent( new MouseEvent('mousemove', { clientX: centerX, clientY: centerY, bubbles: true, }) ); canvas.dispatchEvent( new KeyboardEvent('keydown', { key: ' ', code: 'Space', bubbles: true, }) ); await Utils.sleep(50); canvas.dispatchEvent( new KeyboardEvent('keyup', { key: ' ', code: 'Space', bubbles: true, }) ); await Utils.sleep(500); // 800ms delay before sending confirmation await Utils.sleep(800); // Keep confirming until token is captured const confirmLoop = async () => { while (!turnstileToken) { let confirmBtn = await Utils.waitForSelector( 'button.btn.btn-primary.btn-lg, button.btn.btn-primary.sm\\:btn-xl' ); if (!confirmBtn) { const allPrimary = Array.from(document.querySelectorAll('button.btn-primary')); confirmBtn = allPrimary.length ? allPrimary[allPrimary.length - 1] : null; } if (confirmBtn) { confirmBtn.click(); } await Utils.sleep(500); // 500ms delay between confirmation attempts } }; // Start confirmation loop and wait for token confirmLoop(); const token = await tokenPromise; await Utils.sleep(300); // small delay after token is captured resolve(token); })(); await Promise.race([solvePromise, timeoutPromise]); } catch (error) { console.error('Auto-CAPTCHA process failed:', error); reject(error); } }); } // NUEVA LΓ“GICA DE INYECCIΓ“N - ACTUALIZADA DESDE new.txt function inject(callback) { try { const script = document.createElement('script'); script.textContent = `(${callback})();`; document.documentElement?.appendChild(script); script.remove(); } catch (error) { console.error('❌ Injection error:', error); } } inject(() => { const fetchedBlobQueue = new Map(); window.addEventListener('message', (event) => { const { source, blobID, blobData } = event.data; if (source === 'auto-image-overlay' && blobID && blobData) { const callback = fetchedBlobQueue.get(blobID); if (typeof callback === 'function') { callback(blobData); } fetchedBlobQueue.delete(blobID); } }); const originalFetch = window.fetch; window.fetch = async function (...args) { const response = await originalFetch.apply(this, args); const url = args[0] instanceof Request ? args[0].url : args[0]; if (typeof url === 'string') { if (url.includes('https://backend.wplace.live/s0/pixel/')) { try { const payload = JSON.parse(args[1].body); if (payload.t) { // πŸ“Š Debug log console.log( `πŸ”βœ… Turnstile Token Captured - Type: ${typeof payload.t}, Value: ${payload.t ? typeof payload.t === 'string' ? payload.t.length > 50 ? payload.t.substring(0, 50) + '...' : payload.t : JSON.stringify(payload.t) : 'null/undefined' }, Length: ${payload.t?.length || 0}` ); window.postMessage({ source: 'turnstile-capture', token: payload.t }, '*'); } } catch (_) { /* ignore */ } } const contentType = response.headers.get('content-type') || ''; if (contentType.includes('image/png') && url.includes('.png')) { const cloned = response.clone(); return new Promise(async (resolve) => { const blobUUID = crypto.randomUUID(); const originalBlob = await cloned.blob(); fetchedBlobQueue.set(blobUUID, (processedBlob) => { resolve( new Response(processedBlob, { headers: cloned.headers, status: cloned.status, statusText: cloned.statusText, }) ); }); window.postMessage( { source: 'auto-image-tile', endpoint: url, blobID: blobUUID, blobData: originalBlob, }, '*' ); }); } } return response; }; }); window.addEventListener('message', (event) => { const { source, endpoint, blobID, blobData, token } = event.data; if (source === 'auto-image-tile' && endpoint && blobID && blobData) { overlayManager.processAndRespondToTileRequest(event.data); } if (source === 'turnstile-capture' && token) { setTurnstileToken(token); Utils.addDebugLog('Token captured from injection system', 'success'); updateTokenStatus(); } }); // NUEVO SISTEMA WASM - ACTUALIZADO DESDE new.txt var pawtect_chunk = null; // Find module if pawtect_chunk is null pawtect_chunk ??= await findTokenModule("pawtect_wasm_bg.wasm"); async function createWasmToken(regionX, regionY, payload) { try { // Load the Pawtect module and WASM const mod = await import(new URL('/_app/immutable/chunks/'+pawtect_chunk, location.origin).href); let wasm; try { wasm = await mod._(); console.log('βœ… WASM initialized successfully'); } catch (wasmError) { console.error('❌ WASM initialization failed:', wasmError); return null; } try { try { const me = await fetch(`https://backend.wplace.live/me`, { credentials: 'include' }).then(r => r.ok ? r.json() : null); if (me?.id) { mod.i(me.id); console.log('βœ… user ID set:', me.id); } } catch { } } catch (userIdError) { console.log('⚠️ Error setting user ID:', userIdError.message); } try { const testUrl = `https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`; if (mod.r) { mod.r(testUrl); console.log('βœ… Request URL set:', testUrl); } else { console.log('⚠️ request_url function (mod.r) not available'); } } catch (urlError) { console.log('⚠️ Error setting request URL:', urlError.message); } console.log('πŸ” payload:', payload); // Encode payload const enc = new TextEncoder(); const dec = new TextDecoder(); const bodyStr = JSON.stringify(payload); const bytes = enc.encode(bodyStr); console.log('πŸ” Payload size:', bytes.length, 'bytes'); console.log('πŸ”„ Payload string:', bodyStr); // Allocate WASM memory with validation let inPtr; try { if (!wasm.__wbindgen_malloc) { console.error('❌ __wbindgen_malloc function not found'); return null; } inPtr = wasm.__wbindgen_malloc(bytes.length, 1); console.log('βœ… WASM memory allocated, pointer:', inPtr); // Copy data to WASM memory const wasmBuffer = new Uint8Array(wasm.memory.buffer, inPtr, bytes.length); wasmBuffer.set(bytes); console.log('βœ… Data copied to WASM memory'); } catch (memError) { console.error('❌ Memory allocation error:', memError); return null; } // Call the WASM function console.log('πŸš€ Calling get_pawtected_endpoint_payload...'); let outPtr, outLen, token; try { const result = wasm.get_pawtected_endpoint_payload(inPtr, bytes.length); console.log('βœ… Function called, result type:', typeof result, result); if (Array.isArray(result) && result.length === 2) { [outPtr, outLen] = result; console.log('βœ… Got output pointer:', outPtr, 'length:', outLen); // Decode the result const outputBuffer = new Uint8Array(wasm.memory.buffer, outPtr, outLen); token = dec.decode(outputBuffer); console.log('βœ… Token decoded successfully'); } else { console.error('❌ Unexpected function result format:', result); return null; } } catch (funcError) { console.error('❌ Function call error:', funcError); console.error('Stack trace:', funcError.stack); return null; } // Cleanup memory try { if (wasm.__wbindgen_free && outPtr && outLen) { wasm.__wbindgen_free(outPtr, outLen, 1); console.log('βœ… Output memory freed'); } if (wasm.__wbindgen_free && inPtr) { wasm.__wbindgen_free(inPtr, bytes.length, 1); console.log('βœ… Input memory freed'); } } catch (cleanupError) { console.log('⚠️ Cleanup warning:', cleanupError.message); } console.log('πŸŽ‰ SUCCESS!'); console.log('πŸ”‘ Full token:', token); return token; } catch (error) { console.error('❌ Failed to generate fp parameter:', error); return null; } } async function findTokenModule(str) { console.log('πŸ”Ž Searching for wasm Module...'); const links = Array.from(document.querySelectorAll('link[rel="modulepreload"][href$=".js"]')); for (const link of links) { try { const url = new URL(link.getAttribute("href"), location.origin).href; const code = await fetch(url).then(r => r.text()); if (code.includes(str)) { console.log('βœ… Found wasm Module...'); return url.split('/').pop(); } } catch (e) { /* ignore individual fetch errors */ } } console.error('❌ Could not find Pawtect chunk among preloaded modules'); return null; } // Audio notification system function playNotificationSound() { try { const audio = new Audio('https://cdn.pixabay.com/download/audio/2025/03/21/audio_9bec51b17f.mp3?filename=glass-break-316720.mp3'); audio.volume = 0.5; audio.play().catch(() => { console.warn('Could not play notification sound'); }); } catch (error) { console.warn('Audio notification failed:', error); } } // Notification system for attacks and repairs function showAttackNotification(type, count = 0) { const existing = document.getElementById('attack-notification'); if (existing) existing.remove(); const notification = document.createElement('div'); notification.id = 'attack-notification'; notification.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 15000; background: rgba(0,0,0,0.9); color: white; border-radius: 15px; padding: 20px; text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.8); border: 2px solid ${type === 'attack' ? '#ff4444' : '#44ff44'}; min-width: 350px; max-width: 450px; `; if (type === 'attack') { notification.innerHTML = `
Attack

PIXEL ART ATTACKED!

Detected ${count} damaged pixels!

`; } else if (type === 'repaired') { notification.innerHTML = `
Repaired

COMPLETELY REPAIRED!

All pixels have been restored!

`; } document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.style.transition = 'opacity 0.5s ease'; notification.style.opacity = '0'; setTimeout(() => { if (notification.parentNode) notification.remove(); }, 500); } }, 3000); } // Peaceful state display in main window function showPeacefulState() { const statusDiv = document.getElementById('status'); if (statusDiv && !state.running) { statusDiv.innerHTML = `
Peaceful No attacks detected - All secure
`; } } // Typewriter effect for title function createTypewriterTitle() { const titleText = "WPlace Autonomous Repair Tool"; let currentIndex = 0; const titleElement = document.getElementById('main-title'); if (!titleElement) return; function typeNext() { if (currentIndex < titleText.length) { titleElement.textContent = titleText.substring(0, currentIndex + 1); currentIndex++; setTimeout(typeNext, 100); } else { setTimeout(() => { currentIndex = 0; titleElement.textContent = ''; setTimeout(typeNext, 1000); }, 3000); } } typeNext(); } // Fallback translations const TEXTS = { title: 'WPlace Autonomous Repair Tool', loadFromFile: 'Load Progress File', repairPixels: 'Repair Pixels', enableAutoRepair: 'Enable Auto Repair', repairInterval: 'Check Interval (seconds)', debug: 'Debug Console', clearDebug: 'Clear Debug', scanningForDamage: 'Scanning for damaged pixels...', damageDetected: 'Damage detected: {count} pixels', noDamageDetected: 'No damage found', repairingPixels: 'Repairing {count} damaged pixels...', repairComplete: 'Repair completed: {repaired} pixels fixed', autoRepairStarted: 'Auto repair started (every {interval}s)', autoRepairStopped: 'Auto repair stopped', fileLoaded: 'Progress file loaded successfully', invalidFile: 'Invalid file format', noImageData: 'No image data found in file', turnstileInstructions: 'Complete the verification', hideTurnstileBtn: 'Hide', tokenCapturedSuccess: 'Token captured successfully', autonomousModeActive: 'Autonomous mode active', tokenSystemReady: 'Advanced token system ready', fileOperationsAvailable: 'File operations available', initializingToken: 'Initializing token system...', tokenReady: 'Token system ready', }; // UTILIDADES ACTUALIZADAS CON NUEVA IMPLEMENTACIΓ“N TURNSTILE const Utils = { sleep: (ms) => new Promise((r) => setTimeout(r, ms)), t: (key, params = {}) => { let text = TEXTS[key] || key; Object.keys(params).forEach((param) => { text = text.replace(`{${param}}`, params[param]); }); return text; }, showAlert: (message, type = 'info') => { const alertDiv = document.createElement('div'); alertDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 10000; padding: 12px 16px; border-radius: 8px; color: white; font-weight: 500; background: ${type === 'success' ? '#28a745' : type === 'error' ? '#dc3545' : type === 'warning' ? '#ffc107' : '#17a2b8'}; box-shadow: 0 4px 12px rgba(0,0,0,0.3); max-width: 300px; word-wrap: break-word; `; alertDiv.textContent = message; document.body.appendChild(alertDiv); setTimeout(() => { alertDiv.style.transition = 'opacity 0.3s ease'; alertDiv.style.opacity = '0'; setTimeout(() => { if (alertDiv.parentNode) alertDiv.remove(); }, 300); }, 4000); }, addDebugLog: (message, type = 'info') => { const timestamp = new Date().toLocaleTimeString(); state.debugLogs.push({ timestamp, message, type }); if (state.debugLogs.length > 150) { state.debugLogs = state.debugLogs.slice(-150); } updateDebugConsole(); console.log(`[${timestamp}] ${message}`); }, waitForSelector: async (selector, interval = 200, timeout = 5000) => { const start = Date.now(); while (Date.now() - start < timeout) { const el = document.querySelector(selector); if (el) return el; await Utils.sleep(interval); } return null; }, createFileUploader: () => new Promise((resolve, reject) => { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = () => { try { const data = JSON.parse(reader.result); resolve(data); } catch (error) { reject(new Error('Invalid JSON file')); } }; reader.onerror = () => reject(new Error('File reading error')); reader.readAsText(file); } else { reject(new Error('No file selected')); } }; input.click(); }), isWhitePixel: (r, g, b) => { const wt = state.customWhiteThreshold || CONFIG.WHITE_THRESHOLD; return r >= wt && g >= wt && b >= wt; }, resolveColor(targetRgb, availableColors) { if (!availableColors || availableColors.length === 0) { return { id: null, rgb: targetRgb }; } let bestId = availableColors[0].id; let bestRgb = [...availableColors[0].rgb]; let bestScore = Infinity; for (let i = 0; i < availableColors.length; i++) { const c = availableColors[i]; const [r, g, b] = c.rgb; const rmean = (r + targetRgb[0]) / 2; const rdiff = r - targetRgb[0]; const gdiff = g - targetRgb[1]; const bdiff = b - targetRgb[2]; const dist = Math.sqrt( (((512 + rmean) * rdiff * rdiff) >> 8) + 4 * gdiff * gdiff + (((767 - rmean) * bdiff * bdiff) >> 8) ); if (dist < bestScore) { bestScore = dist; bestId = c.id; bestRgb = [...c.rgb]; if (dist === 0) break; } } return { id: bestId, rgb: bestRgb }; }, calculateTileRange( startRegionX, startRegionY, startPixelX, startPixelY, width, height, tileSize = 1000 ) { const endPixelX = startPixelX + width; const endPixelY = startPixelY + height; return { startTileX: startRegionX + Math.floor(startPixelX / tileSize), startTileY: startRegionY + Math.floor(startPixelY / tileSize), endTileX: startRegionX + Math.floor((endPixelX - 1) / tileSize), endTileY: startRegionY + Math.floor((endPixelY - 1) / tileSize), }; }, dynamicSleep: async function (tickAndGetRemainingMs) { let remaining = Math.max(0, await tickAndGetRemainingMs()); while (remaining > 0) { const interval = remaining > 5000 ? 2000 : remaining > 1000 ? 500 : 100; await this.sleep(Math.min(interval, remaining)); remaining = Math.max(0, await tickAndGetRemainingMs()); } }, // NUEVA IMPLEMENTACIΓ“N TURNSTILE COMPLETA - ACTUALIZADA DESDE new.txt turnstileLoaded: false, _turnstileContainer: null, _turnstileOverlay: null, _turnstileWidgetId: null, _lastSitekey: null, _cachedSitekey: null, async loadTurnstile() { if (window.turnstile) { this.turnstileLoaded = true; return Promise.resolve(); } return new Promise((resolve, reject) => { if (document.querySelector('script[src^="https://challenges.cloudflare.com/turnstile/v0/api.js"]')) { const checkReady = () => { if (window.turnstile) { this.turnstileLoaded = true; resolve(); } else { setTimeout(checkReady, 100); } }; return checkReady(); } const script = document.createElement('script'); script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; script.async = true; script.defer = true; script.onload = () => { this.turnstileLoaded = true; Utils.addDebugLog('Turnstile script loaded successfully', 'success'); resolve(); }; script.onerror = () => { Utils.addDebugLog('Failed to load Turnstile script', 'error'); reject(new Error('Failed to load Turnstile')); }; document.head.appendChild(script); }); }, ensureTurnstileContainer() { if (!this._turnstileContainer || !document.body.contains(this._turnstileContainer)) { if (this._turnstileContainer) { this._turnstileContainer.remove(); } this._turnstileContainer = document.createElement('div'); this._turnstileContainer.className = 'wplace-turnstile-hidden'; this._turnstileContainer.setAttribute('aria-hidden', 'true'); this._turnstileContainer.id = 'turnstile-widget-container'; document.body.appendChild(this._turnstileContainer); } return this._turnstileContainer; }, ensureTurnstileOverlayContainer() { if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { return this._turnstileOverlay; } const overlay = document.createElement('div'); overlay.id = 'turnstile-overlay-container'; overlay.className = 'wplace-turnstile-overlay wplace-overlay-hidden'; const title = document.createElement('div'); title.textContent = Utils.t('turnstileInstructions'); title.className = 'wplace-turnstile-title'; const host = document.createElement('div'); host.id = 'turnstile-overlay-host'; host.className = 'wplace-turnstile-host'; const hideBtn = document.createElement('button'); hideBtn.textContent = Utils.t('hideTurnstileBtn'); hideBtn.className = 'wplace-turnstile-hide-btn'; hideBtn.addEventListener('click', () => overlay.remove()); overlay.appendChild(title); overlay.appendChild(host); overlay.appendChild(hideBtn); document.body.appendChild(overlay); this._turnstileOverlay = overlay; return overlay; }, async executeTurnstile(sitekey, action = 'paint') { await this.loadTurnstile(); // Try reusing existing widget first if sitekey matches if (this._turnstileWidgetId && this._lastSitekey === sitekey && window.turnstile?.execute) { try { console.log('πŸ”„ Reusing existing Turnstile widget...'); const token = await Promise.race([ window.turnstile.execute(this._turnstileWidgetId, { action }), new Promise((_, reject) => setTimeout(() => reject(new Error('Execute timeout')), 15000) ), ]); if (token && token.length > 20) { console.log('βœ… Token generated via widget reuse'); return token; } } catch (error) { console.log(' Widget reuse failed, will create a fresh widget:', error.message); } } // Try invisible widget first const invisibleToken = await this.createTurnstileWidget(sitekey, action); if (invisibleToken && invisibleToken.length > 20) { return invisibleToken; } console.log(' Falling back to interactive Turnstile (visible).'); return await this.createTurnstileWidgetInteractive(sitekey, action); }, async createTurnstileWidget(sitekey, action) { return new Promise((resolve) => { try { // Force cleanup of any existing widget if (this._turnstileWidgetId && window.turnstile?.remove) { try { window.turnstile.remove(this._turnstileWidgetId); console.log('🧹 Cleaned up existing Turnstile widget'); } catch (e) { console.warn('⚠️ Widget cleanup warning:', e.message); } } const container = this.ensureTurnstileContainer(); container.innerHTML = ''; // Verify Turnstile is available if (!window.turnstile?.render) { console.error('❌ Turnstile not available for rendering'); resolve(null); return; } console.log('πŸ”§ Creating invisible Turnstile widget...'); const widgetId = window.turnstile.render(container, { sitekey, action, size: 'invisible', retry: 'auto', 'retry-interval': 8000, callback: (token) => { console.log('βœ… Invisible Turnstile callback'); resolve(token); }, 'error-callback': () => resolve(null), 'timeout-callback': () => resolve(null), }); this._turnstileWidgetId = widgetId; this._lastSitekey = sitekey; if (!widgetId) { return resolve(null); } // Execute the widget and race with timeout Promise.race([ window.turnstile.execute(widgetId, { action }), new Promise((_, reject) => setTimeout(() => reject(new Error('Invisible execute timeout')), 12000) ), ]) .then(resolve) .catch(() => resolve(null)); } catch (e) { console.error('❌ Invisible Turnstile creation failed:', e); resolve(null); } }); }, async createTurnstileWidgetInteractive(sitekey, action) { // Create a visible widget that users can interact with if needed console.log('πŸ”„ Creating interactive Turnstile widget (visible)'); return new Promise((resolve) => { try { // Force cleanup of any existing widget if (this._turnstileWidgetId && window.turnstile?.remove) { try { window.turnstile.remove(this._turnstileWidgetId); } catch (e) { console.warn('⚠️ Widget cleanup warning:', e.message); } } const overlay = this.ensureTurnstileOverlayContainer(); overlay.classList.remove('wplace-overlay-hidden'); overlay.style.display = 'block'; const host = overlay.querySelector('#turnstile-overlay-host'); host.innerHTML = ''; // Set a timeout for interactive mode const timeout = setTimeout(() => { console.warn('⏰ Interactive Turnstile widget timeout'); overlay.classList.add('wplace-overlay-hidden'); overlay.style.display = 'none'; resolve(null); }, 60000); // 60 seconds for user interaction const widgetId = window.turnstile.render(host, { sitekey, action, size: 'normal', theme: 'light', callback: (token) => { clearTimeout(timeout); overlay.classList.add('wplace-overlay-hidden'); overlay.style.display = 'none'; console.log('βœ… Interactive Turnstile completed successfully'); if (typeof token === 'string' && token.length > 20) { resolve(token); } else { console.warn('❌ Invalid token from interactive widget'); resolve(null); } }, 'error-callback': (error) => { clearTimeout(timeout); overlay.classList.add('wplace-overlay-hidden'); overlay.style.display = 'none'; console.warn('❌ Interactive Turnstile error:', error); resolve(null); }, }); this._turnstileWidgetId = widgetId; this._lastSitekey = sitekey; if (!widgetId) { clearTimeout(timeout); overlay.classList.add('wplace-overlay-hidden'); overlay.style.display = 'none'; console.warn('❌ Failed to create interactive Turnstile widget'); resolve(null); } else { console.log('βœ… Interactive Turnstile widget created, waiting for user interaction...'); } } catch (e) { console.error('❌ Interactive Turnstile creation failed:', e); resolve(null); } }); }, cleanupTurnstile() { if (this._turnstileWidgetId && window.turnstile?.remove) { try { window.turnstile.remove(this._turnstileWidgetId); } catch (e) { console.warn('Failed to cleanup Turnstile widget:', e); } } if (this._turnstileContainer && document.body.contains(this._turnstileContainer)) { this._turnstileContainer.remove(); } if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { this._turnstileOverlay.remove(); } this._turnstileWidgetId = null; this._turnstileContainer = null; this._turnstileOverlay = null; this._lastSitekey = null; }, // DETECCIΓ“N DE SITEKEY MEJORADA - ACTUALIZADA DESDE new.txt async obtainSitekeyAndToken(fallback = '0x4AAAAAABpqJe8FO0N84q0F') { // Cache sitekey to avoid repeated DOM queries if (this._cachedSitekey) { console.log('πŸ” Using cached sitekey:', this._cachedSitekey); return isTokenValid() ? { sitekey: this._cachedSitekey, token: turnstileToken, } : { sitekey: this._cachedSitekey, token: null }; } // List of potential sitekeys to try const potentialSitekeys = [ '0x4AAAAAABpqJe8FO0N84q0F', // WPlace common sitekey '0x4AAAAAAAJ7xjKAp6Mt_7zw', // Alternative WPlace sitekey '0x4AAAAAADm5QWx6Ov2LNF2g', // Another common sitekey ]; const trySitekey = async (sitekey, source) => { if (!sitekey || sitekey.length < 10) return null; console.log(`πŸ” Testing sitekey from ${source}:`, sitekey); const token = await this.executeTurnstile(sitekey); if (token && token.length >= 20) { console.log(`βœ… Valid token generated from ${source} sitekey`); setTurnstileToken(token); this._cachedSitekey = sitekey; return { sitekey, token }; } else { console.log(`❌ Failed to get token from ${source} sitekey`); return null; } }; try { // 1️⃣ data-sitekey attribute const sitekeySel = document.querySelector('[data-sitekey]'); if (sitekeySel) { const sitekey = sitekeySel.getAttribute('data-sitekey'); const result = await trySitekey(sitekey, 'data attribute'); if (result) { return result; } } // 2️⃣ Turnstile element const turnstileEl = document.querySelector('.cf-turnstile'); if (turnstileEl?.dataset?.sitekey) { const sitekey = turnstileEl.dataset.sitekey; const result = await trySitekey(sitekey, 'turnstile element'); if (result) { return result; } } // 3️⃣ Meta tags const metaTags = document.querySelectorAll( 'meta[name*="turnstile"], meta[property*="turnstile"]' ); for (const meta of metaTags) { const content = meta.getAttribute('content'); const result = await trySitekey(content, 'meta tag'); if (result) { return result; } } // 4️⃣ Global variable if (window.__TURNSTILE_SITEKEY) { const result = await trySitekey(window.__TURNSTILE_SITEKEY, 'global variable'); if (result) { return result; } } // 5️⃣ Script tags const scripts = document.querySelectorAll('script'); for (const script of scripts) { const content = script.textContent || script.innerHTML; const match = content.match( /(?:sitekey|data-sitekey)['"\s\[\]:\=\(]*['"]?([0-9a-zA-Z_-]{20,})['"]?/i ); if (match && match[1]) { const extracted = match[1].replace(/['"]/g, ''); const result = await trySitekey(extracted, 'script content'); if (result) { return result; } } } // 6️⃣ Known potential sitekeys console.log('πŸ” Testing known potential sitekeys...'); for (const testSitekey of potentialSitekeys) { const result = await trySitekey(testSitekey, 'known list'); if (result) { return result; } } } catch (error) { console.warn('⚠️ Error during sitekey detection:', error); } // 7️⃣ Fallback console.log('πŸ”§ Trying fallback sitekey:', fallback); const fallbackResult = await trySitekey(fallback, 'fallback'); if (fallbackResult) { return fallbackResult; } console.error('❌ No working sitekey or token found.'); return { sitekey: null, token: null }; }, }; // Enhanced Overlay Manager for pixel detection with autonomous capabilities class OverlayManager { constructor() { this.originalTiles = new Map(); this.originalTilesData = new Map(); this.tileSize = 1000; this.loadingPromises = new Map(); this.autonomousMode = state.autonomousMode; } async processAndRespondToTileRequest(eventData) { const { endpoint, blobID, blobData } = eventData; const tileMatch = endpoint.match(/(\d+)\/(\d+)\.png/); if (tileMatch) { const tileX = parseInt(tileMatch[1], 10); const tileY = parseInt(tileMatch[2], 10); const tileKey = `${tileX},${tileY}`; try { const originalBitmap = await createImageBitmap(blobData); this.originalTiles.set(tileKey, originalBitmap); try { let canvas, ctx; if (typeof OffscreenCanvas !== 'undefined') { canvas = new OffscreenCanvas(originalBitmap.width, originalBitmap.height); ctx = canvas.getContext('2d'); } else { canvas = document.createElement('canvas'); canvas.width = originalBitmap.width; canvas.height = originalBitmap.height; ctx = canvas.getContext('2d'); } ctx.imageSmoothingEnabled = false; ctx.drawImage(originalBitmap, 0, 0); const imgData = ctx.getImageData(0, 0, originalBitmap.width, originalBitmap.height); this.originalTilesData.set(tileKey, { w: originalBitmap.width, h: originalBitmap.height, data: new Uint8ClampedArray(imgData.data), }); if (this.autonomousMode) { Utils.addDebugLog(`Auto-cached tile: ${tileKey} (${originalBitmap.width}x${originalBitmap.height})`, 'info'); } } catch (e) { Utils.addDebugLog(`Failed to cache tile ImageData: ${tileKey} - ${e.message}`, 'warning'); } } catch (e) { Utils.addDebugLog(`Failed to create tile bitmap: ${tileKey} - ${e.message}`, 'error'); } } window.postMessage( { source: 'auto-image-overlay', blobID: blobID, blobData: blobData, }, '*' ); } async getTilePixelColor(tileX, tileY, pixelX, pixelY) { const tileKey = `${tileX},${tileY}`; const alphaThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; const cached = this.originalTilesData.get(tileKey); if (cached && cached.data && cached.w > 0 && cached.h > 0) { const x = Math.max(0, Math.min(cached.w - 1, pixelX)); const y = Math.max(0, Math.min(cached.h - 1, pixelY)); const idx = (y * cached.w + x) * 4; const d = cached.data; const a = d[idx + 3]; // Always return pixel data, let caller decide about transparency return [d[idx], d[idx + 1], d[idx + 2], a]; } const bitmap = this.originalTiles.get(tileKey); if (!bitmap) { if (this.autonomousMode) { Utils.addDebugLog(`Tile ${tileKey} not available, requesting load...`, 'warning'); } return null; } try { let canvas, ctx; if (typeof OffscreenCanvas !== 'undefined') { canvas = new OffscreenCanvas(bitmap.width, bitmap.height); ctx = canvas.getContext('2d'); } else { canvas = document.createElement('canvas'); canvas.width = bitmap.width; canvas.height = bitmap.height; ctx = canvas.getContext('2d'); } ctx.imageSmoothingEnabled = false; ctx.drawImage(bitmap, 0, 0); const x = Math.max(0, Math.min(bitmap.width - 1, pixelX)); const y = Math.max(0, Math.min(bitmap.height - 1, pixelY)); const data = ctx.getImageData(x, y, 1, 1).data; const a = data[3]; // Always return pixel data, let caller decide about transparency return [data[0], data[1], data[2], a]; } catch (e) { Utils.addDebugLog(`Error reading pixel from tile ${tileKey}: ${e.message}`, 'error'); return null; } } async waitForTiles(startRegionX, startRegionY, pixelWidth, pixelHeight, startPixelX = 0, startPixelY = 0, timeoutMs = 15000) { const { startTileX, startTileY, endTileX, endTileY } = Utils.calculateTileRange( startRegionX, startRegionY, startPixelX, startPixelY, pixelWidth, pixelHeight, this.tileSize ); const requiredTiles = []; for (let ty = startTileY; ty <= endTileY; ty++) { for (let tx = startTileX; tx <= endTileX; tx++) { requiredTiles.push(`${tx},${ty}`); } } if (requiredTiles.length === 0) return true; Utils.addDebugLog(`Waiting for ${requiredTiles.length} tiles (autonomous: ${this.autonomousMode})...`, 'info'); const startTime = Date.now(); let lastProgress = 0; while (Date.now() - startTime < timeoutMs) { if (state.stopFlag) { Utils.addDebugLog('waitForTiles: stopped by user', 'warning'); return false; } const loaded = requiredTiles.filter((k) => this.originalTiles.has(k)).length; const progress = Math.round((loaded / requiredTiles.length) * 100); if (progress !== lastProgress && progress % 20 === 0) { Utils.addDebugLog(`Tile loading progress: ${loaded}/${requiredTiles.length} (${progress}%)`, 'info'); lastProgress = progress; } if (loaded === requiredTiles.length) { Utils.addDebugLog(`All ${requiredTiles.length} required tiles are loaded`, 'success'); return true; } await Utils.sleep(this.autonomousMode ? 500 : 1000); } const loaded = requiredTiles.filter((k) => this.originalTiles.has(k)).length; Utils.addDebugLog(`Timeout waiting for tiles: ${loaded}/${requiredTiles.length} loaded`, 'warning'); if (this.autonomousMode && loaded > requiredTiles.length * 0.8) { Utils.addDebugLog(`Autonomous mode: proceeding with ${loaded}/${requiredTiles.length} tiles (80%+ loaded)`, 'warning'); return true; } return loaded > 0; } } const overlayManager = new OverlayManager(); // Enhanced WPlace API Service with auto-batch functionality const WPlaceService = { async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount = 0) { try { await ensureToken(); if (!turnstileToken) { Utils.addDebugLog('No valid token available for paint request', 'error'); return 'token_error'; } const payload = { coords: [pixelX, pixelY], colors: [color], t: turnstileToken, fp: fpStr32, }; const wasmToken = await createWasmToken(regionX, regionY, payload); if (!wasmToken) { Utils.addDebugLog('Failed to generate WASM token', 'error'); return false; } const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { method: 'POST', headers: { 'Content-Type': 'text/plain;charset=UTF-8', 'x-pawtect-token': wasmToken }, credentials: 'include', body: JSON.stringify(payload), }); if (res.status === 403) { Utils.addDebugLog(`403 Forbidden for pixel (${pixelX},${pixelY}). Token invalid/expired.`, 'error'); invalidateToken(); if (retryCount < 2 && state.autonomousMode) { Utils.addDebugLog(`Autonomous retry ${retryCount + 1}/2 for pixel (${pixelX},${pixelY})`, 'warning'); await Utils.sleep(1000); return await this.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color, retryCount + 1); } return 'token_error'; } if (!res.ok) { Utils.addDebugLog(`Paint request failed with status ${res.status}`, 'error'); return false; } const data = await res.json(); const success = data?.painted === 1; if (success) { Utils.addDebugLog(`Paint SUCCESS for (${pixelX},${pixelY}) with color ${color}`, 'success'); } else { Utils.addDebugLog(`Paint FAILED for (${pixelX},${pixelY}) - server response: ${JSON.stringify(data)}`, 'error'); } return success; } catch (e) { Utils.addDebugLog(`Paint request error for (${pixelX},${pixelY}): ${e.message}`, 'error'); return false; } }, // NEW: Auto-batch pixel painting functionality async paintPixelBatchInRegion(regionX, regionY, pixelBatch, retryCount = 0) { try { await ensureToken(); if (!turnstileToken) { Utils.addDebugLog('No valid token available for batch paint request', 'error'); return 'token_error'; } // Prepare batch coordinates and colors const coords = []; const colors = []; for (const pixel of pixelBatch) { coords.push(pixel.pixelX, pixel.pixelY); colors.push(pixel.color); } const payload = { coords: coords, colors: colors, t: turnstileToken, fp: fpStr32, }; const wasmToken = await createWasmToken(regionX, regionY, payload); if (!wasmToken) { Utils.addDebugLog('Failed to generate WASM token for batch', 'error'); return false; } const res = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { method: 'POST', headers: { 'Content-Type': 'text/plain;charset=UTF-8', 'x-pawtect-token': wasmToken }, credentials: 'include', body: JSON.stringify(payload), }); if (res.status === 403) { Utils.addDebugLog(`403 Forbidden for batch (${pixelBatch.length} pixels). Token invalid/expired.`, 'error'); invalidateToken(); if (retryCount < 2 && state.autonomousMode) { Utils.addDebugLog(`Autonomous batch retry ${retryCount + 1}/2 for ${pixelBatch.length} pixels`, 'warning'); await Utils.sleep(1000); return await this.paintPixelBatchInRegion(regionX, regionY, pixelBatch, retryCount + 1); } return 'token_error'; } if (!res.ok) { Utils.addDebugLog(`Batch paint request failed with status ${res.status}`, 'error'); return false; } const data = await res.json(); const successCount = data?.painted || 0; Utils.addDebugLog(`Batch paint result: ${successCount}/${pixelBatch.length} pixels painted successfully`, successCount === pixelBatch.length ? 'success' : 'warning'); return { success: successCount > 0, painted: successCount, total: pixelBatch.length }; } catch (e) { Utils.addDebugLog(`Batch paint request error for ${pixelBatch.length} pixels: ${e.message}`, 'error'); return false; } }, async getCharges() { try { const res = await fetch('https://backend.wplace.live/me', { credentials: 'include', }); if (!res.ok) return { charges: 0, max: 1, cooldown: CONFIG.COOLDOWN_DEFAULT }; const data = await res.json(); return { charges: data.charges?.count ?? 0, max: data.charges?.max ?? 1, cooldown: data.charges?.cooldownMs ?? CONFIG.COOLDOWN_DEFAULT, }; } catch (e) { Utils.addDebugLog(`Error fetching charges: ${e.message}`, 'warning'); return { charges: 0, max: 1, cooldown: CONFIG.COOLDOWN_DEFAULT }; } }, }; // Enhanced anti-grief repair system with improved transparent pixel detection async function scanForDamage() { if (!state.imageData || !state.startPosition || !state.region) { Utils.addDebugLog('No image data or position for scanning', 'warning'); return []; } Utils.addDebugLog('Starting enhanced damage scan with transparent pixel detection...', 'info'); updateStatus(Utils.t('scanningForDamage')); const damagedPixels = []; const { width, height, pixels } = state.imageData; // Check if we're working with restored data - skip tile waiting for restored saves const isRestoredData = state.availableColors && state.availableColors.length > 0 && state.colorsChecked; if (!isRestoredData) { const ready = await overlayManager.waitForTiles( state.region.x, state.region.y, width, height, state.startPosition.x, state.startPosition.y, state.autonomousMode ? 20000 : 15000 ); if (!ready) { Utils.addDebugLog('Failed to load required tiles for scanning', 'error'); if (state.autonomousMode) { Utils.addDebugLog('Autonomous mode: will retry scan in 30 seconds', 'warning'); setTimeout(() => { if (state.autoRepairEnabled && !state.stopFlag) { scanForDamage(); } }, 30000); } return []; } } else { Utils.addDebugLog('Using restored data - skipping tile wait for damage scan', 'info'); } Utils.addDebugLog(`Scanning ${width}x${height} image for damage (transparent detection: ${state.paintTransparentPixels})...`, 'info'); let scannedPixels = 0; let transparentPixelsDetected = 0; let wrongColorPixelsDetected = 0; let lastProgressUpdate = Date.now(); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { if (state.stopFlag) break; const idx = (y * width + x) * 4; const originalR = pixels[idx]; const originalG = pixels[idx + 1]; const originalB = pixels[idx + 2]; const originalA = pixels[idx + 3]; // Enhanced transparent pixel detection const isOriginalTransparent = originalA < state.customTransparencyThreshold; // Skip white pixels if configured if (!state.paintWhitePixels && Utils.isWhitePixel(originalR, originalG, originalB)) { continue; } scannedPixels++; const absX = state.startPosition.x + x; const absY = state.startPosition.y + y; const tileX = state.region.x + Math.floor(absX / 1000); const tileY = state.region.y + Math.floor(absY / 1000); const pixelX = absX % 1000; const pixelY = absY % 1000; try { const currentPixel = await overlayManager.getTilePixelColor(tileX, tileY, pixelX, pixelY); // Handle missing current pixel data (could indicate transparent area) if (!currentPixel) { if (state.paintTransparentPixels && !isOriginalTransparent) { // Original pixel is not transparent but current is missing/transparent const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); damagedPixels.push({ x, y, originalColor: targetColor, currentColor: { id: 63, name: 'Transparent', rgb: [0, 0, 0] }, // Transparent color originalRgb: [originalR, originalG, originalB], currentRgb: [0, 0, 0], isDamagedTransparent: true }); transparentPixelsDetected++; if (!state.autonomousMode || transparentPixelsDetected <= 10) { Utils.addDebugLog(`Missing/Transparent pixel at (${x},${y}): expected color ${targetColor.id}, found transparent/missing`, 'warning'); } } continue; } const currentIsTransparent = currentPixel[3] < state.customTransparencyThreshold; // Case 1: Original is not transparent, but current is transparent (damaged) if (!isOriginalTransparent && currentIsTransparent && state.paintTransparentPixels) { const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); damagedPixels.push({ x, y, originalColor: targetColor, currentColor: { id: 63, name: 'Transparent', rgb: [0, 0, 0] }, originalRgb: [originalR, originalG, originalB], currentRgb: currentPixel.slice(0, 3), isDamagedTransparent: true }); transparentPixelsDetected++; if (!state.autonomousMode || transparentPixelsDetected <= 10) { Utils.addDebugLog(`Transparent damage at (${x},${y}): expected color ${targetColor.id}, found transparent`, 'warning'); } continue; } // Case 2: Original is transparent, current is not transparent (wrong placement) if (isOriginalTransparent && !currentIsTransparent && state.paintTransparentPixels) { damagedPixels.push({ x, y, originalColor: { id: 63, name: 'Transparent', rgb: null }, currentColor: Utils.resolveColor(currentPixel.slice(0, 3), state.availableColors), originalRgb: [originalR, originalG, originalB], currentRgb: currentPixel.slice(0, 3), isDamagedTransparent: true }); transparentPixelsDetected++; if (!state.autonomousMode || transparentPixelsDetected <= 10) { Utils.addDebugLog(`Wrong placement at (${x},${y}): expected transparent, found color`, 'warning'); } continue; } // Case 3: Both are not transparent, check color match (normal damage detection) if (!isOriginalTransparent && !currentIsTransparent) { const targetColor = Utils.resolveColor([originalR, originalG, originalB], state.availableColors); const currentColor = Utils.resolveColor(currentPixel.slice(0, 3), state.availableColors); if (targetColor.id === currentColor.id) { // Pixel is already correctly painted - skip it continue; } // Pixel has wrong color - mark as damaged damagedPixels.push({ x, y, originalColor: targetColor, currentColor: currentColor, originalRgb: [originalR, originalG, originalB], currentRgb: currentPixel.slice(0, 3), isDamagedTransparent: false }); wrongColorPixelsDetected++; if (!state.autonomousMode || wrongColorPixelsDetected <= 10) { Utils.addDebugLog(`Color damage at (${x},${y}): expected color ${targetColor.id}, found color ${currentColor.id}`, 'warning'); } } } catch (e) { if (!state.autonomousMode) { Utils.addDebugLog(`Error checking pixel (${x},${y}): ${e.message}`, 'error'); } } } if (state.autonomousMode && Date.now() - lastProgressUpdate > 5000) { Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%), found ${damagedPixels.length} damaged (${transparentPixelsDetected} transparent, ${wrongColorPixelsDetected} wrong color)`, 'info'); lastProgressUpdate = Date.now(); } else if (!state.autonomousMode && y % 10 === 0) { Utils.addDebugLog(`Scan progress: ${y}/${height} rows (${Math.round((y / height) * 100)}%)`, 'info'); } } const logLevel = damagedPixels.length > 0 ? 'warning' : 'success'; Utils.addDebugLog(`Enhanced scan complete. Checked ${scannedPixels} pixels, found ${damagedPixels.length} damaged (${transparentPixelsDetected} transparent issues, ${wrongColorPixelsDetected} wrong colors)`, logLevel); // Smart auto-adjust batch size: if 6 pixels detected, batch size = 6 if (state.autoBatchEnabled && state.batchOptimization && damagedPixels.length > 0) { const newBatchSize = Math.min(CONFIG.MAX_BATCH_SIZE, Math.max(CONFIG.MIN_BATCH_SIZE, damagedPixels.length)); if (newBatchSize !== state.currentBatchSize) { state.currentBatchSize = newBatchSize; Utils.addDebugLog(`Smart batch: Auto-adjusted batch size to ${newBatchSize} to match ${damagedPixels.length} detected damaged pixels`, 'info'); updateBatchInfo(); // Update UI display } } return damagedPixels; } async function repairDamagedPixels(damagedPixels) { if (damagedPixels.length === 0) { updateStatus(Utils.t('noDamageDetected')); return 0; } Utils.addDebugLog(`Starting enhanced repair of ${damagedPixels.length} pixels with auto-batch (batch size: ${state.currentBatchSize})`, 'info'); updateStatus(Utils.t('repairingPixels', { count: damagedPixels.length })); let repairedCount = 0; let consecutiveFailures = 0; const maxConsecutiveFailures = state.autonomousMode ? 5 : 3; // Group pixels by region for batch processing const pixelsByRegion = new Map(); for (const pixel of damagedPixels) { const absX = state.startPosition.x + pixel.x; const absY = state.startPosition.y + pixel.y; const regionX = state.region.x + Math.floor(absX / 1000); const regionY = state.region.y + Math.floor(absY / 1000); const regionKey = `${regionX},${regionY}`; if (!pixelsByRegion.has(regionKey)) { pixelsByRegion.set(regionKey, []); } pixelsByRegion.get(regionKey).push({ ...pixel, regionX, regionY, pixelX: absX % 1000, pixelY: absY % 1000, color: pixel.isDamagedTransparent && pixel.originalColor.id === 63 ? 63 : pixel.originalColor.id }); } Utils.addDebugLog(`Grouped ${damagedPixels.length} pixels into ${pixelsByRegion.size} regions for batch processing`, 'info'); // Process each region for (const [regionKey, regionPixels] of pixelsByRegion) { if (state.stopFlag) { Utils.addDebugLog('Repair stopped by user request', 'warning'); break; } const [regionX, regionY] = regionKey.split(',').map(Number); Utils.addDebugLog(`Processing region ${regionKey} with ${regionPixels.length} pixels`, 'info'); // Process pixels in batches for (let i = 0; i < regionPixels.length; i += state.currentBatchSize) { if (state.stopFlag) break; const batch = regionPixels.slice(i, i + state.currentBatchSize); const actualBatchSize = Math.min(batch.length, state.currentBatchSize); // Enhanced charge management for batch processing await updateCharges(); let chargeWaitAttempts = 0; const maxChargeWaitAttempts = state.autonomousMode ? 20 : 10; while (state.displayCharges < actualBatchSize && !state.stopFlag && chargeWaitAttempts < maxChargeWaitAttempts) { chargeWaitAttempts++; const waitTime = state.autonomousMode ? Math.min(state.cooldown, 10000) : state.cooldown; if (chargeWaitAttempts === 1) { Utils.addDebugLog(`Waiting for charges... (need ${actualBatchSize}, have ${state.displayCharges}/${state.maxCharges})`, 'info'); } await Utils.dynamicSleep(() => { if (state.displayCharges >= actualBatchSize) return 0; if (state.stopFlag) return 0; return waitTime; }); await updateCharges(); } if (state.stopFlag) break; if (state.displayCharges < actualBatchSize) { Utils.addDebugLog(`Insufficient charges for batch (need ${actualBatchSize}, have ${state.displayCharges}), falling back to single pixel repair`, 'warning'); // Fall back to single pixel repair for (const pixel of batch) { if (state.stopFlag) break; await updateCharges(); if (state.displayCharges < 1) { Utils.addDebugLog(`No charges available, skipping pixel (${pixel.x},${pixel.y})`, 'warning'); continue; } const success = await repairSinglePixel(pixel); if (success) { repairedCount++; consecutiveFailures = 0; } else { consecutiveFailures++; } if (consecutiveFailures >= maxConsecutiveFailures) break; await Utils.sleep(100); } } else { // Use batch repair const batchResult = await repairPixelBatch(regionX, regionY, batch); if (batchResult && batchResult.success) { repairedCount += batchResult.painted; consecutiveFailures = 0; Utils.addDebugLog(`Batch repair: ${batchResult.painted}/${batchResult.total} pixels repaired successfully [Total: ${repairedCount}/${damagedPixels.length}]`, 'success'); } else { consecutiveFailures++; Utils.addDebugLog(`Batch repair failed for ${batch.length} pixels [${consecutiveFailures}/${maxConsecutiveFailures} consecutive failures]`, 'error'); if (consecutiveFailures >= maxConsecutiveFailures) { Utils.addDebugLog(`Too many consecutive batch failures (${consecutiveFailures}), pausing repair`, 'error'); if (state.autonomousMode) { Utils.addDebugLog('Autonomous mode: will retry repair in 60 seconds', 'warning'); setTimeout(() => { if (state.autoRepairEnabled && !state.stopFlag) { performRepairCheck(); } }, 60000); } break; } } } await updateCharges(); // Dynamic delay based on mode and success rate const baseDelay = state.autonomousMode ? 100 : 200; const adaptiveDelay = consecutiveFailures > 0 ? baseDelay * (consecutiveFailures + 1) : baseDelay; await Utils.sleep(adaptiveDelay); } if (consecutiveFailures >= maxConsecutiveFailures) break; } const message = Utils.t('repairComplete', { repaired: repairedCount }); updateStatus(message); Utils.addDebugLog(`${message} (${damagedPixels.length - repairedCount} remaining) - Used auto-batch with size ${state.currentBatchSize}`, 'success'); return repairedCount; } async function repairPixelBatch(regionX, regionY, pixelBatch) { try { Utils.addDebugLog(`Attempting batch repair of ${pixelBatch.length} pixels in region ${regionX},${regionY}`, 'info'); const result = await WPlaceService.paintPixelBatchInRegion(regionX, regionY, pixelBatch); if (result === 'token_error') { Utils.addDebugLog('Token error during batch repair, refreshing token...', 'warning'); await ensureToken(true); await Utils.sleep(state.autonomousMode ? 500 : 1000); // Retry once with new token const retryResult = await WPlaceService.paintPixelBatchInRegion(regionX, regionY, pixelBatch); return retryResult; } return result; } catch (e) { Utils.addDebugLog(`Error during batch repair: ${e.message}`, 'error'); return false; } } async function repairSinglePixel(pixel) { const { x, y, originalColor, regionX, regionY, pixelX, pixelY, color } = pixel; try { const result = await WPlaceService.paintPixelInRegion( regionX, regionY, pixelX, pixelY, color || originalColor.id ); if (result === 'token_error') { Utils.addDebugLog('Token error during repair, refreshing token...', 'warning'); await ensureToken(true); await Utils.sleep(state.autonomousMode ? 500 : 1000); // Retry once with new token const retryResult = await WPlaceService.paintPixelInRegion(regionX, regionY, pixelX, pixelY, color || originalColor.id); return retryResult === true; } if (result === true) { if (pixel.isDamagedTransparent) { Utils.addDebugLog(`Repaired transparent pixel (${x},${y}) with color ${color || originalColor.id}`, 'success'); } } return result === true; } catch (e) { Utils.addDebugLog(`Error repairing pixel (${x},${y}): ${e.message}`, 'error'); return false; } } async function performRepairCheck() { if (state.running) { Utils.addDebugLog('Repair check skipped - manual repair in progress', 'info'); return; } try { Utils.addDebugLog('Autonomous repair check triggered', 'info'); // Ensure we have a valid token before starting if (!isTokenValid()) { Utils.addDebugLog('No valid token for autonomous repair, generating...', 'warning'); await ensureToken(true); if (!isTokenValid()) { Utils.addDebugLog('Failed to generate token for autonomous repair, will retry next cycle', 'error'); return; } } const damagedPixels = await scanForDamage(); if (damagedPixels.length > 0) { Utils.addDebugLog(`Autonomous repair: Found ${damagedPixels.length} damaged pixels, starting repair`, 'warning'); updateStatus(Utils.t('damageDetected', { count: damagedPixels.length })); // Show attack notification showAttackNotification('attack', damagedPixels.length); state.lastAttackState = 'attacked'; const repairedCount = await repairDamagedPixels(damagedPixels); // Show repair complete notification if all pixels were repaired if (repairedCount === damagedPixels.length) { showAttackNotification('repaired'); state.lastAttackState = 'repaired'; } } else { updateStatus(Utils.t('noDamageDetected')); // Show peaceful state if we're not under attack if (state.lastAttackState !== 'peaceful') { showPeacefulState(); state.lastAttackState = 'peaceful'; } if (!state.autonomousMode) { Utils.addDebugLog('Autonomous repair: No damage detected', 'success'); } } } catch (error) { Utils.addDebugLog(`Autonomous repair error: ${error.message}`, 'error'); if (state.autonomousMode) { Utils.addDebugLog('Autonomous mode: will retry repair check in 120 seconds due to error', 'warning'); setTimeout(() => { if (state.autoRepairEnabled && !state.stopFlag) { performRepairCheck(); } }, 120000); } } } function startAutoRepair() { if (state.autoRepairTimer) { clearInterval(state.autoRepairTimer); } const intervalMs = state.autoRepairInterval * 1000; state.autoRepairTimer = setInterval(performRepairCheck, intervalMs); const message = Utils.t('autoRepairStarted', { interval: state.autoRepairInterval }); Utils.addDebugLog(`${message} (autonomous: ${state.autonomousMode})`, 'info'); Utils.showAlert(message, 'success'); // Perform first check immediately setTimeout(performRepairCheck, 2000); } function stopAutoRepair() { if (state.autoRepairTimer) { clearInterval(state.autoRepairTimer); state.autoRepairTimer = null; } const message = Utils.t('autoRepairStopped'); Utils.addDebugLog(`${message}`, 'info'); Utils.showAlert(message, 'info'); } // FUNCIONES DE UI MEJORADAS CON GESTIΓ“N DE CONFIGURACIΓ“N INICIAL function enableFileOperations() { state.initialSetupComplete = true; const loadBtn = document.querySelector('#loadFileBtn'); if (loadBtn) { loadBtn.disabled = false; loadBtn.title = ''; loadBtn.style.animation = 'pulse 0.6s ease-in-out'; setTimeout(() => { if (loadBtn) loadBtn.style.animation = ''; }, 600); Utils.addDebugLog('Load Progress button enabled after initial setup', 'success'); } Utils.showAlert(Utils.t('fileOperationsAvailable'), 'success'); } async function initializeTokenGenerator() { if (isTokenValid()) { Utils.addDebugLog('Valid token already available, skipping initialization', 'success'); updateTokenStatus(); enableFileOperations(); return; } try { Utils.addDebugLog('Initializing Turnstile token generator...', 'info'); updateStatus(Utils.t('initializingToken')); await Utils.loadTurnstile(); Utils.addDebugLog('Turnstile script loaded successfully', 'success'); if (state.autoTokenRefresh) { Utils.addDebugLog('Pre-generating token for autonomous operations...', 'info'); await ensureToken(); } state.initialSetupComplete = true; Utils.addDebugLog('Enhanced token generator initialization complete', 'success'); updateStatus(Utils.t('tokenReady')); enableFileOperations(); } catch (error) { Utils.addDebugLog('Token system initialization failed: ' + error.message, 'warning'); Utils.addDebugLog('Manual token generation will be required', 'info'); enableFileOperations(); } } function updateStatus(message) { const statusEl = document.getElementById('status'); if (statusEl) { if (typeof message === 'string') { statusEl.innerHTML = message; } else { statusEl.textContent = message; } } } function updateDebugConsole() { const debugConsole = document.getElementById('debugConsole'); if (!debugConsole) return; const logsHtml = state.debugLogs.map(log => { const colorClass = log.type === 'error' ? 'neon-text danger' : log.type === 'warning' ? 'neon-text warning' : log.type === 'success' ? 'neon-text success' : log.type === 'info' ? 'neon-text info' : 'neon-text'; return `
[${log.timestamp}] ${log.message}
`; }).join(''); debugConsole.innerHTML = logsHtml; debugConsole.scrollTop = debugConsole.scrollHeight; } async function updateCharges() { try { const { charges, max, cooldown } = await WPlaceService.getCharges(); state.displayCharges = Math.floor(charges); state.maxCharges = Math.max(1, Math.floor(max)); state.cooldown = cooldown; updateChargesDisplay(); } catch (error) { if (!state.autonomousMode) { Utils.addDebugLog(`Error updating charges: ${error.message}`, 'error'); } } } function updateChargesDisplay() { const chargesEl = document.getElementById('chargesInfo'); if (chargesEl) { chargesEl.textContent = `Charges: ${state.displayCharges}/${state.maxCharges} (cooldown: ${Math.round(state.cooldown / 1000)}s)`; } } function updateTokenStatus() { const tokenEl = document.getElementById('tokenInfo'); if (tokenEl) { // Remove existing classes first tokenEl.className = ''; if (isTokenValid()) { const remaining = Math.round((tokenExpiryTime - Date.now()) / 1000); tokenEl.textContent = `Token: Valid (expires in ${remaining}s)`; tokenEl.className = 'neon-text success'; } else { tokenEl.textContent = 'Token: Not generated or expired'; tokenEl.className = 'neon-text warning'; } } } // Update batch information display for new UI function updateBatchInfo() { const batchSizeEl = document.getElementById('batchSize'); const batchStatusEl = document.getElementById('batchStatus'); const smartModeEl = document.getElementById('smartBatchMode'); const manualModeEl = document.getElementById('manualBatchMode'); const manualControlsEl = document.getElementById('manualBatchControls'); // Update radio button states if (smartModeEl && manualModeEl) { smartModeEl.checked = state.autoBatchEnabled && state.batchOptimization; manualModeEl.checked = !state.autoBatchEnabled || !state.batchOptimization; } // Update manual controls visibility if (manualControlsEl) { manualControlsEl.style.display = (!state.autoBatchEnabled || !state.batchOptimization) ? 'flex' : 'none'; } // Update batch size input if (batchSizeEl) { batchSizeEl.value = state.currentBatchSize; } // Update status display if (batchStatusEl) { if (state.autoBatchEnabled && state.batchOptimization) { batchStatusEl.textContent = `🧠 Smart Batch: AUTO (Current: ${state.currentBatchSize})`; batchStatusEl.className = 'status-info'; } else { batchStatusEl.textContent = `βœ‹ Manual Batch: ${state.currentBatchSize} pixels fixed`; batchStatusEl.className = 'status-warning'; } } } function updateSystemStatus() { const systemEl = document.getElementById('systemInfo'); if (systemEl) { const autonomousStatus = state.autonomousMode ? 'AUTONOMOUS' : 'MANUAL'; const tokenMode = state.tokenSource.toUpperCase(); systemEl.textContent = `System: ${autonomousStatus} | Token: ${tokenMode} | Auto-refresh: ${state.autoTokenRefresh ? 'ON' : 'OFF'}`; } } // Window dragging functionality function makeWindowDraggable() { const container = document.getElementById('wplace-repair-tool'); const header = document.getElementById('window-header'); let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; function dragStart(e) { if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') { return; } initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; if (e.target === header || header.contains(e.target)) { isDragging = true; container.style.cursor = 'grabbing'; } } function dragEnd() { initialX = currentX; initialY = currentY; isDragging = false; container.style.cursor = 'default'; } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; container.style.transform = `translate(${currentX}px, ${currentY}px)`; } } header.addEventListener('mousedown', dragStart); document.addEventListener('mouseup', dragEnd); document.addEventListener('mousemove', drag); } // Enhanced UI Creation with NEW IMPLEMENTATION function createUI() { const existing = document.getElementById('wplace-repair-tool'); if (existing) existing.remove(); const container = document.createElement('div'); container.id = 'wplace-repair-tool'; container.style.cssText = ` position: fixed; top: 20px; left: 20px; z-index: 10000; background: #1a1a2e; border: 3px solid #00ff41; border-radius: 0; min-width: 600px; max-width: 700px; box-shadow: 0 0 30px rgb(0 255 65 / 50%), inset 0 0 30px rgb(0 255 65 / 10%), 0 0 0 1px #00ff41; color: #00ff41; font-family: 'Press Start 2P', monospace, 'Courier New'; resize: both; overflow: hidden; animation: neon-pulse 2s ease-in-out infinite alternate; `; // Add Neon theme styles const neonStyles = document.createElement('style'); neonStyles.id = 'auto-repair-neon-styles'; neonStyles.textContent = ` @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); #wplace-repair-tool::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: linear-gradient(90deg, transparent, #00ff41, transparent); z-index: 1; pointer-events: none; animation: scanline 3s linear infinite; opacity: 0.7; } .neon-btn { background: #16213e !important; border: 2px solid #00ff41 !important; border-radius: 0 !important; color: #00ff41 !important; padding: 8px 15px !important; font-family: 'Press Start 2P', monospace !important; font-size: 8px !important; text-transform: uppercase !important; cursor: pointer !important; transition: all 0.3s ease !important; text-shadow: 0 0 8px #00ff41 !important; letter-spacing: 1px !important; margin: 4px !important; } .neon-btn:hover { background: rgba(0,255,65,0.1) !important; box-shadow: 0 0 20px rgb(0 255 65 / 60%) !important; animation: pixel-blink 0.5s infinite !important; } .neon-btn:disabled { background: #0d1117 !important; border-color: #555 !important; color: #555 !important; text-shadow: none !important; cursor: not-allowed !important; } .neon-btn.danger { border-color: #ff073a !important; color: #ff073a !important; text-shadow: 0 0 8px #ff073a !important; } .neon-btn.danger:hover { background: rgba(255, 7, 58, 0.1) !important; box-shadow: 0 0 20px rgb(255 7 58 / 60%) !important; } .neon-btn.warning { border-color: #ff6b35 !important; color: #ff6b35 !important; text-shadow: 0 0 8px #ff6b35 !important; } .neon-btn.warning:hover { background: rgba(255, 107, 53, 0.1) !important; box-shadow: 0 0 20px rgb(255 107 53 / 60%) !important; } .neon-input { background: #16213e !important; border: 2px solid #00ff41 !important; border-radius: 0 !important; color: #00ff41 !important; padding: 6px 10px !important; font-family: 'Press Start 2P', monospace !important; font-size: 8px !important; text-shadow: 0 0 5px #00ff41 !important; } .neon-input:focus { outline: none !important; box-shadow: 0 0 15px rgb(0 255 65 / 60%) !important; animation: pixel-blink 0.5s infinite !important; } .neon-select { background: #16213e !important; border: 2px solid #00ff41 !important; border-radius: 0 !important; color: #00ff41 !important; padding: 4px 8px !important; font-family: 'Press Start 2P', monospace !important; font-size: 7px !important; text-shadow: 0 0 5px #00ff41 !important; } .neon-checkbox { accent-color: #00ff41 !important; transform: scale(1.2) !important; margin-right: 8px !important; } /* Enhanced Batch Control Styles */ .batch-control-group { display: flex !important; align-items: center !important; gap: 8px !important; margin: 5px 0 !important; padding: 8px !important; background: rgba(0, 255, 65, 0.05) !important; border: 1px solid rgba(0, 255, 65, 0.3) !important; border-radius: 3px !important; } .batch-control-header { text-align: center !important; margin-bottom: 10px !important; padding: 5px !important; border-bottom: 1px solid rgba(0, 255, 65, 0.3) !important; } .batch-control-label { color: #00ff41 !important; font-family: 'Press Start 2P', monospace !important; font-size: 6px !important; text-shadow: 0 0 3px #00ff41 !important; white-space: nowrap !important; } .batch-mode-selection { margin: 10px 0 !important; } .batch-mode-option { margin: 8px 0 !important; padding: 6px !important; background: rgba(0, 255, 65, 0.03) !important; border: 1px solid rgba(0, 255, 65, 0.2) !important; border-radius: 3px !important; transition: all 0.3s ease !important; } .batch-mode-option:hover { background: rgba(0, 255, 65, 0.08) !important; border-color: rgba(0, 255, 65, 0.4) !important; } .batch-mode-label { color: #00ff41 !important; font-family: 'Press Start 2P', monospace !important; font-size: 6px !important; text-shadow: 0 0 3px #00ff41 !important; margin-left: 8px !important; } .neon-radio { accent-color: #00ff41 !important; transform: scale(1.2) !important; margin-right: 8px !important; } .batch-manual-controls { display: flex !important; align-items: center !important; gap: 8px !important; margin: 8px 0 !important; padding: 8px !important; background: rgba(0, 255, 65, 0.05) !important; border: 1px solid rgba(0, 255, 65, 0.3) !important; border-radius: 3px !important; } .batch-size-control { display: flex !important; align-items: center !important; gap: 2px !important; border: 1px solid #00ff41 !important; border-radius: 3px !important; background: #16213e !important; overflow: hidden !important; } .batch-adjust-btn { width: 20px !important; height: 24px !important; background: #16213e !important; border: none !important; color: #00ff41 !important; font-family: 'Press Start 2P', monospace !important; font-size: 8px !important; cursor: pointer !important; transition: all 0.3s ease !important; text-shadow: 0 0 3px #00ff41 !important; display: flex !important; align-items: center !important; justify-content: center !important; } .batch-adjust-btn:hover { background: rgba(0, 255, 65, 0.1) !important; box-shadow: 0 0 5px rgba(0, 255, 65, 0.3) !important; } .batch-adjust-btn:active { background: rgba(0, 255, 65, 0.2) !important; transform: scale(0.95) !important; } .batch-size-input { width: 50px !important; height: 24px !important; background: #16213e !important; border: none !important; color: #00ff41 !important; padding: 0 4px !important; font-family: 'Press Start 2P', monospace !important; font-size: 6px !important; text-align: center !important; text-shadow: 0 0 3px #00ff41 !important; outline: none !important; /* Hide browser default spinner buttons */ -webkit-appearance: none !important; -moz-appearance: textfield !important; } /* Hide Chrome spinner buttons */ .batch-size-input::-webkit-outer-spin-button, .batch-size-input::-webkit-inner-spin-button { -webkit-appearance: none !important; margin: 0 !important; } /* Hide Firefox spinner buttons */ .batch-size-input[type=number] { -moz-appearance: textfield !important; } .status-info { color: #00ff41 !important; } .status-warning { color: #ff6b35 !important; } .neon-panel { background: rgba(22, 33, 62, 0.3) !important; border: 1px solid #00ff41 !important; border-radius: 0 !important; padding: 15px !important; margin: 10px 0 !important; box-shadow: inset 0 0 15px rgba(0, 255, 65, 0.1) !important; } .neon-text { color: #00ff41 !important; text-shadow: 0 0 5px #00ff41 !important; font-family: 'Press Start 2P', monospace !important; } .neon-text.success { color: #39ff14 !important; text-shadow: 0 0 5px #39ff14 !important; } .neon-text.warning { color: #ff6b35 !important; text-shadow: 0 0 5px #ff6b35 !important; } .neon-text.danger { color: #ff073a !important; text-shadow: 0 0 5px #ff073a !important; } .neon-text.info { color: #00ccff !important; text-shadow: 0 0 5px #00ccff !important; } /* Custom scrollbar for debug console */ #debugConsole::-webkit-scrollbar { width: 12px; } #debugConsole::-webkit-scrollbar-track { background: #16213e; border: 1px solid #00ff41; } #debugConsole::-webkit-scrollbar-thumb { background: #00ff41; border-radius: 0; box-shadow: 0 0 10px #00ff41; } #debugConsole::-webkit-scrollbar-thumb:hover { background: #39ff14; box-shadow: 0 0 15px #39ff14; } /* Animations */ @keyframes neon-pulse { 0% { box-shadow: 0 0 30px rgb(0 255 65 / 50%), inset 0 0 30px rgb(0 255 65 / 10%), 0 0 0 1px #00ff41; } 100% { box-shadow: 0 0 40px rgb(0 255 65 / 70%), inset 0 0 40px rgb(0 255 65 / 15%), 0 0 0 1px #00ff41; } } @keyframes pixel-blink { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0.7; } } @keyframes scanline { 0% { transform: translateY(-100%); } 100% { transform: translateY(400px); } } @keyframes text-glow { 0% { text-shadow: 0 0 15px #00ff41; } 100% { text-shadow: 0 0 25px #00ff41, 0 0 35px #00ff41; } } `; if (!document.getElementById('auto-repair-neon-styles')) { document.head.appendChild(neonStyles); } container.innerHTML = `
πŸ”§

WPlace Autonomous Repair Tool

${Utils.t('autonomousModeActive')}
Charges: 0/1 (cooldown: 31s)
Token: Initializing...
System: AUTONOMOUS | Token: GENERATOR | Auto-refresh: ON
Auto-Batch: ON | Size: 5 | Opt: ON
seconds
🎯 Pixel Batch Mode
Batch Size:
pixels
Image: Not loaded
Position: Not set
Colors: 0 available
Batch Mode: Auto (${state.currentBatchSize} pixels)

πŸ“‹ ${Utils.t('debug')} (Enhanced System)

[Ready] WPlace Autonomous Repair Tool v3.0
[Info] NEW Turnstile implementation active
[Info] Enhanced token management with preloading
[Info] ${Utils.t('tokenSystemReady')}
`; document.body.appendChild(container); makeWindowDraggable(); setupEventListeners(); // Start typewriter effect setTimeout(() => { createTypewriterTitle(); }, 1000); // Start autonomous systems initializeAutonomousSystems(); } function initializeAutonomousSystems() { // Update charges periodically updateCharges(); setInterval(updateCharges, 10000); // Update token status updateTokenStatus(); setInterval(updateTokenStatus, 5000); // Update system status updateSystemStatus(); setInterval(updateSystemStatus, 15000); // Update batch information updateBatchInfo(); // Initialize token generator initializeTokenGenerator(); } function setupEventListeners() { // Window controls document.getElementById('minimizeBtn').addEventListener('click', () => { const content = document.getElementById('window-content'); state.windowMinimized = !state.windowMinimized; if (state.windowMinimized) { content.style.display = 'none'; document.getElementById('minimizeBtn').textContent = '+'; } else { content.style.display = 'block'; document.getElementById('minimizeBtn').textContent = 'βˆ’'; } }); document.getElementById('closeBtn').addEventListener('click', () => { const container = document.getElementById('wplace-repair-tool'); if (container) { container.style.transition = 'opacity 0.3s ease, transform 0.3s ease'; container.style.opacity = '0'; container.style.transform = 'scale(0.8)'; setTimeout(() => { container.remove(); cleanup(); }, 300); } }); // Load file document.getElementById('loadFileBtn').addEventListener('click', async () => { try { Utils.addDebugLog('Loading progress file...', 'info'); const data = await Utils.createFileUploader(); if (!data || !data.state || !data.imageData) { throw new Error(Utils.t('invalidFile')); } const migratedData = migrateProgressData(data); Object.assign(state, migratedData.state); state.imageData = { ...migratedData.imageData, pixels: new Uint8ClampedArray(migratedData.imageData.pixels), }; state.imageLoaded = true; document.getElementById('repairBtn').disabled = false; // Update info displays document.getElementById('imageInfo').textContent = `${state.imageData.width}x${state.imageData.height}`; document.getElementById('positionInfo').textContent = state.startPosition ? `(${state.startPosition.x}, ${state.startPosition.y}) in region (${state.region.x}, ${state.region.y})` : 'Not set'; document.getElementById('colorsInfo').textContent = `${state.availableColors?.length || 0} available`; updateStatus(Utils.t('fileLoaded')); Utils.addDebugLog(`Loaded image: ${state.imageData.width}x${state.imageData.height}`, 'success'); Utils.addDebugLog(`Position: (${state.startPosition?.x}, ${state.startPosition?.y})`, 'info'); Utils.addDebugLog(`Region: (${state.region?.x}, ${state.region?.y})`, 'info'); Utils.addDebugLog(`Available colors: ${state.availableColors?.length || 0}`, 'info'); if (migratedData.state.paintWhitePixels !== undefined) { state.paintWhitePixels = migratedData.state.paintWhitePixels; Utils.addDebugLog(`Paint white pixels: ${state.paintWhitePixels}`, 'info'); } if (migratedData.state.paintTransparentPixels !== undefined) { state.paintTransparentPixels = migratedData.state.paintTransparentPixels; Utils.addDebugLog(`Paint transparent pixels: ${state.paintTransparentPixels}`, 'info'); } Utils.showAlert(Utils.t('fileLoaded'), 'success'); // If autonomous mode and auto repair is enabled, start it if (state.autonomousMode && document.getElementById('autoRepairEnabled').checked) { Utils.addDebugLog('Autonomous mode: starting auto repair after file load', 'info'); setTimeout(() => { state.autoRepairEnabled = true; startAutoRepair(); }, 5000); } } catch (error) { Utils.addDebugLog(`Load error: ${error.message}`, 'error'); Utils.showAlert(error.message, 'error'); } }); // Generate Token button document.getElementById('generateTokenBtn').addEventListener('click', async () => { const btn = document.getElementById('generateTokenBtn'); const originalText = btn.textContent; try { btn.disabled = true; btn.textContent = 'πŸ”„ Generating...'; btn.style.background = 'linear-gradient(135deg, #ffa726 0%, #ff9800 100%)'; Utils.addDebugLog('Manual token generation requested', 'info'); const token = await ensureToken(true); if (token) { Utils.addDebugLog('Token generation succeeded!', 'success'); Utils.showAlert('Token generated successfully!', 'success'); updateTokenStatus(); } else { throw new Error('Token generation failed'); } } catch (error) { Utils.addDebugLog(`Token generation failed: ${error.message}`, 'error'); Utils.showAlert('Token generation failed', 'error'); playNotificationSound(); } finally { btn.disabled = false; btn.textContent = originalText; btn.style.background = 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)'; } }); // Manual repair let repairRunning = false; document.getElementById('repairBtn').addEventListener('click', async () => { if (!state.imageLoaded) { Utils.showAlert('Please load a progress file first', 'warning'); return; } if (repairRunning) { state.stopFlag = true; Utils.addDebugLog('Manual repair stop requested', 'warning'); return; } repairRunning = true; state.running = true; state.stopFlag = false; const repairBtn = document.getElementById('repairBtn'); repairBtn.textContent = '⏸️ Stop Repair'; repairBtn.style.background = 'linear-gradient(135deg, #ff6b35 0%, #f7931e 100%)'; try { Utils.addDebugLog('Starting manual repair with enhanced autonomous system...', 'info'); if (!isTokenValid()) { Utils.addDebugLog('No valid token, generating...', 'info'); await ensureToken(true); } const damagedPixels = await scanForDamage(); if (damagedPixels.length > 0) { updateStatus(Utils.t('damageDetected', { count: damagedPixels.length })); showAttackNotification('attack', damagedPixels.length); const repairedCount = await repairDamagedPixels(damagedPixels); if (repairedCount === damagedPixels.length) { showAttackNotification('repaired'); } } else { updateStatus(Utils.t('noDamageDetected')); showPeacefulState(); } } catch (error) { Utils.addDebugLog(`Repair error: ${error.message}`, 'error'); Utils.showAlert('Repair failed', 'error'); } finally { repairRunning = false; state.running = false; state.stopFlag = false; repairBtn.textContent = 'πŸ”§ ' + Utils.t('repairPixels'); repairBtn.style.background = 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'; } }); // Auto repair toggle document.getElementById('autoRepairEnabled').addEventListener('change', (e) => { state.autoRepairEnabled = e.target.checked; if (state.autoRepairEnabled) { if (!state.imageLoaded) { Utils.showAlert('Please load a progress file first', 'warning'); e.target.checked = false; state.autoRepairEnabled = false; return; } startAutoRepair(); } else { stopAutoRepair(); } }); // Repair interval change document.getElementById('repairInterval').addEventListener('change', (e) => { const value = parseInt(e.target.value) || 30; state.autoRepairInterval = Math.max(10, Math.min(3600, value)); e.target.value = state.autoRepairInterval; Utils.addDebugLog(`Repair interval changed to ${state.autoRepairInterval} seconds`, 'info'); if (state.autoRepairEnabled && state.imageLoaded) { Utils.addDebugLog('Restarting auto repair with new interval', 'info'); startAutoRepair(); } }); // Auto token refresh toggle document.getElementById('autoTokenRefresh').addEventListener('change', (e) => { state.autoTokenRefresh = e.target.checked; Utils.addDebugLog(`Auto token refresh ${state.autoTokenRefresh ? 'enabled' : 'disabled'}`, 'info'); updateSystemStatus(); if (state.autoTokenRefresh && state.autonomousMode && !isTokenValid()) { ensureToken(); } }); // Token source selection document.getElementById('tokenSourceSelect').addEventListener('change', (e) => { state.tokenSource = e.target.value; Utils.addDebugLog(`Token source changed to: ${state.tokenSource}`, 'info'); updateSystemStatus(); // Invalidate current token to force new generation with new method if (isTokenValid()) { Utils.addDebugLog('Invalidating current token due to source change', 'info'); invalidateToken(); } }); // NEW: Enhanced batch mode controls document.getElementById('smartBatchMode').addEventListener('change', (e) => { if (e.target.checked) { state.autoBatchEnabled = true; state.batchOptimization = true; document.getElementById('manualBatchControls').style.display = 'none'; Utils.addDebugLog('Smart batch mode enabled - will auto-adjust to damage count', 'info'); updateBatchInfo(); } }); document.getElementById('manualBatchMode').addEventListener('change', (e) => { if (e.target.checked) { state.autoBatchEnabled = false; state.batchOptimization = false; document.getElementById('manualBatchControls').style.display = 'flex'; Utils.addDebugLog('Manual batch mode enabled - using fixed batch size', 'info'); updateBatchInfo(); } }); // Manual batch size controls with +/- buttons document.getElementById('batchSizeIncrease').addEventListener('click', () => { const newSize = Math.min(CONFIG.MAX_BATCH_SIZE, state.currentBatchSize + 1); if (newSize !== state.currentBatchSize) { state.currentBatchSize = newSize; document.getElementById('batchSize').value = state.currentBatchSize; Utils.addDebugLog(`Increased batch size to ${state.currentBatchSize} pixels`, 'info'); updateBatchInfo(); } }); document.getElementById('batchSizeDecrease').addEventListener('click', () => { const newSize = Math.max(CONFIG.MIN_BATCH_SIZE, state.currentBatchSize - 1); if (newSize !== state.currentBatchSize) { state.currentBatchSize = newSize; document.getElementById('batchSize').value = state.currentBatchSize; Utils.addDebugLog(`Decreased batch size to ${state.currentBatchSize} pixels`, 'info'); updateBatchInfo(); } }); // Manual input allows direct typing for precise control document.getElementById('batchSize').addEventListener('input', (e) => { const value = parseInt(e.target.value); if (!isNaN(value)) { const clampedValue = Math.max(CONFIG.MIN_BATCH_SIZE, Math.min(CONFIG.MAX_BATCH_SIZE, value)); if (clampedValue !== state.currentBatchSize) { state.currentBatchSize = clampedValue; if (clampedValue !== value) { e.target.value = state.currentBatchSize; } Utils.addDebugLog(`Manual batch size set to ${state.currentBatchSize} pixels`, 'info'); updateBatchInfo(); } } }); document.getElementById('paintTransparentPixels').addEventListener('change', (e) => { state.paintTransparentPixels = e.target.checked; Utils.addDebugLog(`Transparent pixel repair ${state.paintTransparentPixels ? 'enabled' : 'disabled'}`, 'info'); }); // Clear debug document.getElementById('clearDebugBtn').addEventListener('click', () => { state.debugLogs = []; updateDebugConsole(); Utils.addDebugLog('Debug console cleared', 'info'); Utils.addDebugLog('Enhanced WPlace Autonomous Repair Tool ready', 'info'); }); } // Enhanced data migration helper function migrateProgressData(data) { if (!data.version || data.version < '2.0') { Utils.addDebugLog('Migrating old progress data format...', 'info'); if (!data.state) data.state = {}; if (!data.imageData) data.imageData = {}; data.state.paintWhitePixels = data.state.paintWhitePixels ?? true; data.state.paintTransparentPixels = data.state.paintTransparentPixels ?? false; data.version = '2.0'; } // Additional migrations for autonomous features if (data.version === '2.0') { data.state.autonomousMode = data.state.autonomousMode ?? CONFIG.AUTONOMOUS_MODE; data.state.autoTokenRefresh = data.state.autoTokenRefresh ?? CONFIG.AUTO_TOKEN_REFRESH; data.state.tokenSource = data.state.tokenSource ?? CONFIG.TOKEN_SOURCE; data.version = '3.0'; Utils.addDebugLog('Migrated to autonomous features v3.0', 'info'); } return data; } // Main initialization function async function initialize() { Utils.addDebugLog('Starting WPlace Autonomous Repair Tool v3.0...', 'info'); // Create UI first createUI(); Utils.addDebugLog('NEW Enhanced Features Initialized:', 'info'); Utils.addDebugLog('β€’ NEW Turnstile implementation with widget reuse', 'info'); Utils.addDebugLog('β€’ Enhanced sitekey detection from multiple sources', 'info'); Utils.addDebugLog('β€’ Sophisticated token management with preloading', 'info'); Utils.addDebugLog('β€’ Autonomous operation with smart retry logic', 'info'); Utils.addDebugLog('β€’ Enhanced tile interception and caching', 'info'); Utils.addDebugLog('β€’ Adaptive repair algorithms with failure handling', 'info'); Utils.addDebugLog('β€’ Visual notifications and audio alerts', 'info'); Utils.addDebugLog('β€’ Draggable window with minimize/close controls', 'info'); if (state.autonomousMode) { Utils.addDebugLog('AUTONOMOUS MODE ACTIVE - Tool will operate independently', 'success'); Utils.addDebugLog('Instructions for autonomous operation:', 'info'); Utils.addDebugLog('1. Wait for initial setup to complete', 'info'); Utils.addDebugLog('2. Load a progress file using "Load Progress File"', 'info'); Utils.addDebugLog('3. Enable "Auto Repair" to start autonomous monitoring', 'info'); Utils.addDebugLog('4. Tool will automatically scan and repair damage', 'info'); Utils.addDebugLog('5. Tokens will be generated and refreshed automatically', 'info'); Utils.addDebugLog('6. Monitor debug console for autonomous operation logs', 'info'); } else { Utils.addDebugLog('Manual mode active - use controls for manual operation', 'info'); } // Show autonomous mode status updateStatus(state.autonomousMode ? Utils.t('autonomousModeActive') : 'Manual mode ready - Load file and enable auto repair to start'); Utils.addDebugLog('WPlace Autonomous Repair Tool ready for operation', 'success'); } // Enhanced cleanup function function cleanup() { Utils.addDebugLog('Cleaning up autonomous systems...', 'info'); // Clear all timers if (state.autoRepairTimer) { clearInterval(state.autoRepairTimer); state.autoRepairTimer = null; } if (state.tokenRetryTimer) { clearTimeout(state.tokenRetryTimer); state.tokenRetryTimer = null; } if (state.tokenPreloadTimer) { clearTimeout(state.tokenPreloadTimer); state.tokenPreloadTimer = null; } // Cleanup Turnstile Utils.cleanupTurnstile(); // Set stop flag state.stopFlag = true; Utils.addDebugLog('Cleanup completed', 'info'); } // Add cleanup on page unload window.addEventListener('beforeunload', cleanup); // Handle page visibility changes for autonomous mode document.addEventListener('visibilitychange', () => { if (state.autonomousMode) { if (document.hidden) { Utils.addDebugLog('Page hidden - autonomous systems continue running', 'info'); } else { Utils.addDebugLog('Page visible - autonomous systems active', 'info'); // Refresh status when page becomes visible updateCharges(); updateTokenStatus(); } } }); // Enhanced error handling for autonomous mode window.addEventListener('error', (event) => { if (state.autonomousMode) { Utils.addDebugLog(`Global error in autonomous mode: ${event.error?.message || 'Unknown error'}`, 'error'); } }); window.addEventListener('unhandledrejection', (event) => { if (state.autonomousMode) { Utils.addDebugLog(`Unhandled promise rejection in autonomous mode: ${event.reason}`, 'error'); } }); // Start the enhanced autonomous application initialize().catch(error => { console.error('Failed to initialize autonomous repair tool:', error); Utils.addDebugLog(`Initialization failed: ${error.message}`, 'error'); }); })();