diff --git a/Auto-Image.js b/Auto-Image.js index 925c2ae..44ec6df 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -1,584 +1,307 @@ (async () => { - // CONFIGURATION CONSTANTS const CONFIG = { - COOLDOWN_DEFAULT: 31000, - TRANSPARENCY_THRESHOLD: 100, - WHITE_THRESHOLD: 250, - LOG_INTERVAL: 10, + START_X: 742, + START_Y: 1148, + PIXELS_PER_LINE: 100, + DELAY: 1000, THEME: { - primary: "#000000", - secondary: "#111111", - accent: "#222222", - text: "#ffffff", - highlight: "#775ce3", - success: "#00ff00", - error: "#ff0000", - warning: "#ffaa00", - }, + primary: '#000000', + secondary: '#111111', + accent: '#222222', + text: '#ffffff', + highlight: '#775ce3', + success: '#00ff00', + error: '#ff0000' + } }; - // BILINGUAL TEXT STRINGS - const TEXTS = { - pt: { - title: "WPlace Auto-Image", - initBot: "Iniciar Auto-BOT", - uploadImage: "Upload da Imagem", - resizeImage: "Redimensionar Imagem", - selectPosition: "Selecionar Posição", - startPainting: "Iniciar Pintura", - stopPainting: "Parar Pintura", - checkingColors: "🔍 Verificando cores disponíveis...", - noColorsFound: "❌ Abra a paleta de cores no site e tente novamente!", - colorsFound: "✅ {count} cores disponíveis encontradas", - loadingImage: "🖼️ Carregando imagem...", - imageLoaded: "✅ Imagem carregada com {count} pixels válidos", - imageError: "❌ Erro ao carregar imagem", - selectPositionAlert: - "Pinte o primeiro pixel na localização onde deseja que a arte comece!", - waitingPosition: "👆 Aguardando você pintar o pixel de referência...", - positionSet: "✅ Posição definida com sucesso!", - positionTimeout: "❌ Tempo esgotado para selecionar posição", - startPaintingMsg: "🎨 Iniciando pintura...", - paintingProgress: "🧱 Progresso: {painted}/{total} pixels...", - noCharges: "⌛ Sem cargas. Aguardando {time}...", - paintingStopped: "⏹️ Pintura interrompida pelo usuário", - paintingComplete: "✅ Pintura concluída! {count} pixels pintados.", - paintingError: "❌ Erro durante a pintura", - missingRequirements: - "❌ Carregue uma imagem e selecione uma posição primeiro", - progress: "Progresso", - pixels: "Pixels", - charges: "Cargas", - estimatedTime: "Tempo estimado", - initMessage: "Clique em 'Iniciar Auto-BOT' para começar", - waitingInit: "Aguardando inicialização...", - resizeSuccess: "✅ Imagem redimensionada para {width}x{height}", - paintingPaused: "⏸️ Pintura pausada na posição X: {x}, Y: {y}", - captchaNeeded: - "❗ Token CAPTCHA necessário. Pinte um pixel manualmente para continuar.", - saveData: "Salvar Progresso", - loadData: "Carregar Progresso", - autoSaved: "✅ Progresso salvo automaticamente", - dataLoaded: "✅ Progresso carregado com sucesso", - noSavedData: "❌ Nenhum progresso salvo encontrado", - savedDataFound: "✅ Progresso salvo encontrado! Carregar para continuar?", - savedDate: "Salvo em: {date}", - clickLoadToContinue: "Clique em 'Carregar Progresso' para continuar.", - }, - en: { - title: "WPlace Auto-Image", - initBot: "Start Auto-BOT", - uploadImage: "Upload Image", - resizeImage: "Resize Image", - selectPosition: "Select Position", - startPainting: "Start Painting", - stopPainting: "Stop Painting", - checkingColors: "🔍 Checking available colors...", - noColorsFound: "❌ Open the color palette on the site and try again!", - colorsFound: "✅ {count} available colors found", - loadingImage: "🖼️ Loading image...", - imageLoaded: "✅ Image loaded with {count} valid pixels", - imageError: "❌ Error loading image", - selectPositionAlert: - "Paint the first pixel at the location where you want the art to start!", - waitingPosition: "👆 Waiting for you to paint the reference pixel...", - positionSet: "✅ Position set successfully!", - positionTimeout: "❌ Timeout for position selection", - startPaintingMsg: "🎨 Starting painting...", - paintingProgress: "🧱 Progress: {painted}/{total} pixels...", - noCharges: "⌛ No charges. Waiting {time}...", - paintingStopped: "⏹️ Painting stopped by user", - paintingComplete: "✅ Painting complete! {count} pixels painted.", - paintingError: "❌ Error during painting", - missingRequirements: "❌ Load an image and select a position first", - progress: "Progress", - pixels: "Pixels", - charges: "Charges", - estimatedTime: "Estimated time", - initMessage: "Click 'Start Auto-BOT' to begin", - waitingInit: "Waiting for initialization...", - resizeSuccess: "✅ Image resized to {width}x{height}", - paintingPaused: "⏸️ Painting paused at position X: {x}, Y: {y}", - captchaNeeded: - "❗ CAPTCHA token needed. Paint one pixel manually to continue.", - saveData: "Save Progress", - loadData: "Load Progress", - autoSaved: "✅ Progress saved automatically", - dataLoaded: "✅ Progress loaded successfully", - noSavedData: "❌ No saved progress found", - savedDataFound: "✅ Saved progress found! Load to continue?", - savedDate: "Saved on: {date}", - clickLoadToContinue: "Click 'Load Progress' to continue.", - }, - }; - - // GLOBAL STATE const state = { running: false, - imageLoaded: false, - processing: false, - totalPixels: 0, - paintedPixels: 0, - availableColors: [], - currentCharges: 0, - cooldown: CONFIG.COOLDOWN_DEFAULT, - imageData: null, - stopFlag: false, - colorsChecked: false, - startPosition: null, - selectingPosition: false, - region: null, + paintedCount: 0, + charges: { count: 0, max: 80, cooldownMs: 30000 }, + userInfo: null, + lastPixel: null, minimized: false, - lastPosition: { x: 0, y: 0 }, - estimatedTime: 0, - language: "en", + menuOpen: false, + language: 'en', + autoRefresh: true, + pausedForManual: false }; - // Global variable to store the captured CAPTCHA token. - let capturedCaptchaToken = null; + const sleep = ms => new Promise(r => setTimeout(r, ms)); + const 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 sleep(interval); + } + return null; + }; - // Intercept the original window.fetch function to "listen" for network requests. const originalFetch = window.fetch; - window.fetch = async (url, options) => { - // Check if the request is for painting a pixel on wplace. - if ( - typeof url === "string" && - url.includes("https://backend.wplace.live/s0/pixel/") - ) { + let capturedCaptchaToken = null; + window.fetch = async (url, options = {}) => { + if (typeof url === 'string' && url.includes('https://backend.wplace.live/s0/pixel/')) { try { - const payload = JSON.parse(options.body); - // If the request body contains the 't' field, it's our CAPTCHA token. + const payload = JSON.parse(options.body || '{}'); if (payload.t) { - console.log("✅ CAPTCHA Token Captured:", payload.t); - // Store the token for our bot to use. + console.log('✅ CAPTCHA Token Captured:', payload.t); capturedCaptchaToken = payload.t; - // Notify the user that the token is captured and they can start the bot. - if ( - document - .querySelector("#statusText") - ?.textContent.includes("CAPTCHA") - ) { - Utils.showAlert( - "Token captured successfully! You can start the bot now.", - "success" + if (state.pausedForManual) { + state.pausedForManual = false; + state.running = true; + updateUI( + state.language === 'pt' ? '🚀 Pintura reiniciada!' : '🚀 Painting resumed!', + 'success' ); - updateUI("colorsFound", "success", { - count: state.availableColors.length, - }); + paintLoop(); } } } catch (e) { - /* Ignore errors if the request body isn't valid JSON */ } } - // Finally, execute the original request, whether we inspected it or not. return originalFetch(url, options); }; - async function detectLanguage() { + const fetchAPI = async (url, options = {}) => { try { - const response = await fetch("https://ipapi.co/json/"); - const data = await response.json(); - state.language = data.country === "BR" ? "pt" : "en"; - } catch { - state.language = "en"; - } - } - - // UTILITY FUNCTIONS - const Utils = { - sleep: (ms) => new Promise((r) => setTimeout(r, ms)), - - colorDistance: (a, b) => - Math.sqrt( - Math.pow(a[0] - b[0], 2) + - Math.pow(a[1] - b[1], 2) + - Math.pow(a[2] - b[2], 2) - ), - - createImageUploader: () => - new Promise((resolve) => { - const input = document.createElement("input"); - input.type = "file"; - input.accept = "image/png,image/jpeg"; - input.onchange = () => { - const fr = new FileReader(); - fr.onload = () => resolve(fr.result); - fr.readAsDataURL(input.files[0]); - }; - input.click(); - }), - - extractAvailableColors: () => { - const colorElements = document.querySelectorAll('[id^="color-"]'); - return Array.from(colorElements) - .filter((el) => !el.querySelector("svg")) - .filter((el) => { - const id = parseInt(el.id.replace("color-", "")); - return id !== 0 && id !== 5; - }) - .map((el) => { - const id = parseInt(el.id.replace("color-", "")); - const rgbStr = el.style.backgroundColor.match(/\d+/g); - const rgb = rgbStr ? rgbStr.map(Number) : [0, 0, 0]; - return { id, rgb }; - }); - }, - - formatTime: (ms) => { - const seconds = Math.floor((ms / 1000) % 60); - const minutes = Math.floor((ms / (1000 * 60)) % 60); - const hours = Math.floor((ms / (1000 * 60 * 60)) % 24); - const days = Math.floor(ms / (1000 * 60 * 60 * 24)); - - let result = ""; - if (days > 0) result += `${days}d `; - if (hours > 0 || days > 0) result += `${hours}h `; - if (minutes > 0 || hours > 0 || days > 0) result += `${minutes}m `; - result += `${seconds}s`; - - return result; - }, - - // Save/Load Progress Functions - saveProgress: () => { - try { - const progressData = { - version: "1.0", - timestamp: Date.now(), - state: { - imageLoaded: state.imageLoaded, - totalPixels: state.totalPixels, - paintedPixels: state.paintedPixels, - lastPosition: state.lastPosition, - startPosition: state.startPosition, - region: state.region, - paintedMap: state.paintedMap, - imageData: state.imageData, - availableColors: state.availableColors, - colorsChecked: state.colorsChecked, - language: state.language - } - }; - - localStorage.setItem('wplace-auto-image-progress', JSON.stringify(progressData)); - console.log('✅ Progress saved successfully'); - return true; - } catch (error) { - console.error('❌ Error saving progress:', error); - return false; - } - }, - - loadProgress: () => { - try { - const savedData = localStorage.getItem('wplace-auto-image-progress'); - if (!savedData) { - return null; - } - - const progressData = JSON.parse(savedData); - - // Validate data structure - if (!progressData.version || !progressData.state) { - return null; - } - - return progressData; - } catch (error) { - console.error('❌ Error loading progress:', error); - return null; - } - }, - - restoreProgress: (progressData) => { - try { - const savedState = progressData.state; - - // Restore state - state.imageLoaded = savedState.imageLoaded; - state.totalPixels = savedState.totalPixels; - state.paintedPixels = savedState.paintedPixels; - state.lastPosition = savedState.lastPosition || { x: 0, y: 0 }; - state.startPosition = savedState.startPosition; - state.region = savedState.region; - state.paintedMap = savedState.paintedMap; - state.imageData = savedState.imageData; - state.availableColors = savedState.availableColors; - state.language = savedState.language; - state.colorsChecked = savedState.availableColors && savedState.availableColors.length > 0; - - // Update UI to reflect restored state - if (state.imageLoaded) { - const initBotBtn = document.querySelector("#initBotBtn"); - const uploadBtn = document.querySelector("#uploadBtn"); - const resizeBtn = document.querySelector("#resizeBtn"); - const selectPosBtn = document.querySelector("#selectPosBtn"); - const startBtn = document.querySelector("#startBtn"); - const saveBtn = document.querySelector("#saveBtn"); - const progressBar = document.querySelector("#progressBar"); - - // Show/hide appropriate buttons based on state - if (state.colorsChecked) { - initBotBtn.style.display = "none"; - uploadBtn.disabled = false; - selectPosBtn.disabled = false; - } else { - initBotBtn.style.display = "block"; - uploadBtn.disabled = true; - selectPosBtn.disabled = true; - } - - resizeBtn.disabled = false; - saveBtn.disabled = false; - - if (state.startPosition && state.region) { - selectPosBtn.disabled = false; - startBtn.disabled = false; - } - - // Update progress bar - const progress = state.totalPixels > 0 ? Math.round((state.paintedPixels / state.totalPixels) * 100) : 0; - progressBar.style.width = `${progress}%`; - - // Update status message based on progress - if (state.paintedPixels > 0) { - if (state.lastPosition.x > 0 || state.lastPosition.y > 0) { - updateUI("paintingPaused", "warning", { - x: state.lastPosition.x, - y: state.lastPosition.y - }); - } else { - updateUI("paintingProgress", "default", { - painted: state.paintedPixels, - total: state.totalPixels, - }); - } - } else { - updateUI("imageLoaded", "success", { count: state.totalPixels }); - } - } - - // Update stats to show current progress - updateStats(); - return true; - } catch (error) { - console.error('❌ Error restoring progress:', error); - return false; - } - }, - - clearProgress: () => { - try { - localStorage.removeItem('wplace-auto-image-progress'); - console.log('✅ Progress data cleared'); - return true; - } catch (error) { - console.error('❌ Error clearing progress:', error); - return false; - } - }, - - hasSavedProgress: () => { - return localStorage.getItem('wplace-auto-image-progress') !== null; - }, - - showAlert: (message, type = "info") => { - const alert = document.createElement("div"); - alert.style.position = "fixed"; - alert.style.top = "20px"; - alert.style.left = "50%"; - alert.style.transform = "translateX(-50%)"; - alert.style.padding = "15px 20px"; - alert.style.background = CONFIG.THEME[type] || CONFIG.THEME.accent; - alert.style.color = CONFIG.THEME.text; - alert.style.borderRadius = "5px"; - alert.style.zIndex = "10000"; - alert.style.boxShadow = "0 3px 10px rgba(0,0,0,0.3)"; - alert.style.display = "flex"; - alert.style.alignItems = "center"; - alert.style.gap = "10px"; - - const icons = { - error: "exclamation-circle", - success: "check-circle", - warning: "exclamation-triangle", - info: "info-circle", - }; - - alert.innerHTML = ` - - ${message} - `; - - document.body.appendChild(alert); - - setTimeout(() => { - alert.style.opacity = "0"; - alert.style.transition = "opacity 0.5s"; - setTimeout(() => alert.remove(), 500); - }, 3000); - }, - - calculateEstimatedTime: (remainingPixels, currentCharges, cooldown) => { - const pixelsPerCharge = currentCharges > 0 ? currentCharges : 0; - const fullCycles = Math.ceil( - (remainingPixels - pixelsPerCharge) / Math.max(currentCharges, 1) - ); - return fullCycles * cooldown + (remainingPixels - 1) * 100; - }, - - isWhitePixel: (r, g, b) => { - return ( - r >= CONFIG.WHITE_THRESHOLD && - g >= CONFIG.WHITE_THRESHOLD && - b >= CONFIG.WHITE_THRESHOLD - ); - }, - - t: (key, params = {}) => { - let text = TEXTS[state.language][key] || TEXTS.en[key] || key; - for (const [k, v] of Object.entries(params)) { - text = text.replace(`{${k}}`, v); - } - return text; - }, - }; - - // WPLACE API SERVICE - const WPlaceService = { - async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color) { - try { - // Construct the payload including the captured 't' token. - const payload = { - coords: [pixelX, pixelY], - colors: [color], - t: capturedCaptchaToken, - }; - const res = await fetch( - `https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, - { - method: "POST", - headers: { "Content-Type": "text/plain;charset=UTF-8" }, - credentials: "include", - body: JSON.stringify(payload), - } - ); - - // If we get a 403 Forbidden error, our token is likely expired. - if (res.status === 403) { - console.error( - "❌ 403 Forbidden. CAPTCHA token might be invalid or expired." - ); - capturedCaptchaToken = null; // Invalidate our stored token. - return "token_error"; // Return a special status to stop the bot. - } - - const data = await res.json(); - return data?.painted === 1; - } catch (e) { - console.error("Paint request failed:", e); - return false; - } - }, - - async getCharges() { - try { - const res = await fetch("https://backend.wplace.live/me", { - credentials: "include", - }); - const data = await res.json(); - return { - charges: data.charges?.count || 0, - cooldown: data.charges?.cooldownMs || CONFIG.COOLDOWN_DEFAULT, - }; - } catch { - return { charges: 0, cooldown: CONFIG.COOLDOWN_DEFAULT }; - } - }, - }; - - class ImageProcessor { - constructor(imageSrc) { - this.imageSrc = imageSrc; - this.img = new Image(); - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); - this.previewCanvas = document.createElement("canvas"); - this.previewCtx = this.previewCanvas.getContext("2d"); - } - - async load() { - return new Promise((resolve, reject) => { - this.img.onload = () => { - this.canvas.width = this.img.width; - this.canvas.height = this.img.height; - this.ctx.drawImage(this.img, 0, 0); - resolve(); - }; - this.img.onerror = reject; - this.img.src = this.imageSrc; + const res = await fetch(url, { + credentials: 'include', + ...options }); + return await res.json(); + } catch (e) { + return null; } + }; - getPixelData() { - return this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height) - .data; + const getRandomPosition = () => ({ + x: Math.floor(Math.random() * CONFIG.PIXELS_PER_LINE), + y: Math.floor(Math.random() * CONFIG.PIXELS_PER_LINE) + }); + + const paintPixel = async (x, y) => { + const randomColor = Math.floor(Math.random() * 31) + 1; + const url = `https://backend.wplace.live/s0/pixel/${CONFIG.START_X}/${CONFIG.START_Y}`; + const payload = JSON.stringify({ coords: [x, y], colors: [randomColor], t: capturedCaptchaToken }); + try { + const res = await originalFetch(url, { + method: 'POST', + headers: { 'Content-Type': 'text/plain;charset=UTF-8' }, + credentials: 'include', + body: payload + }); + if (res.status === 403) { + console.error('❌ 403 Forbidden. CAPTCHA token might be invalid or expired.'); + capturedCaptchaToken = null; + stoppedForToken = true; + return 'token_error'; + } + const data = await res.json(); + return data; + } catch (e) { + return null; } + }; - getDimensions() { - return { width: this.canvas.width, height: this.canvas.height }; + const getCharge = async () => { + const data = await fetchAPI('https://backend.wplace.live/me'); + if (data) { + state.userInfo = data; + state.charges = { + count: Math.floor(data.charges.count), + max: Math.floor(data.charges.max), + cooldownMs: data.charges.cooldownMs + }; + if (state.userInfo.level) { + state.userInfo.level = Math.floor(state.userInfo.level); + } } + return state.charges; + }; - resize(newWidth, newHeight) { - const tempCanvas = document.createElement("canvas"); - tempCanvas.width = newWidth; - tempCanvas.height = newHeight; - const tempCtx = tempCanvas.getContext("2d"); - - tempCtx.drawImage(this.img, 0, 0, newWidth, newHeight); - - this.canvas.width = newWidth; - this.canvas.height = newHeight; - this.ctx.drawImage(tempCanvas, 0, 0); - - return this.getPixelData(); + const detectUserLocation = async () => { + try { + const response = await fetch('https://ipapi.co/json/'); + const data = await response.json(); + if (data.country === 'BR') { + state.language = 'pt'; + } else if (data.country === 'US') { + state.language = 'en'; + } else { + state.language = 'en'; + } + } catch { + state.language = 'en'; } + }; - generatePreview(newWidth, newHeight) { - this.previewCanvas.width = newWidth; - this.previewCanvas.height = newHeight; - this.previewCtx.imageSmoothingEnabled = false; - this.previewCtx.drawImage(this.img, 0, 0, newWidth, newHeight); - return this.previewCanvas.toDataURL(); + const paintLoop = async () => { + while (state.running) { + const { count, cooldownMs } = state.charges; + + if (count < 1 && state.autoRefresh) { + if (!state.pausedForManual) { + updateUI( + state.language === 'pt' + ? '⚡ Aguardando pelo menos 2 cargas para auto-refresh...' + : 'Waiting for at least 2 charges for auto-refresh...', + 'status' + ); + state.pausedForManual = true; + } + while (state.charges.count < 2) { + await sleep(60000); + await getCharge(); + updateStats(); + } + state.pausedForManual = false; + updateUI( + state.language === 'pt' ? '🔋 Cargas suficientes. Retomando...' : 'Sufficient charges. Resuming...', + 'success' + ); + continue; + } else if (count < 1) { + // manual mode fallback + updateUI(state.language === 'pt' ? `⌛ Sem cargas. Esperando ${Math.ceil(cooldownMs/1000)}s...` : `⌛ No charges. Waiting ${Math.ceil(cooldownMs/1000)}s...`, 'status'); + await sleep(cooldownMs); + await getCharge(); + continue; + } + + const randomPos = getRandomPosition(); + const paintResult = await paintPixel(randomPos.x, randomPos.y); + if (paintResult === 'token_error') { + if (state.autoRefresh) { + await getCharge(); + if (state.charges.count < 2) { + if (!state.pausedForManual) { + updateUI( + state.language === 'pt' + ? '⚡ Aguardando pelo menos 2 cargas para auto-refresh...' + : 'Waiting for at least 2 charges for auto-refresh...', + 'status' + ); + state.pausedForManual = true; + } + while (state.charges.count < 2) { + await sleep(60000); + await getCharge(); + updateStats(); + } + state.pausedForManual = false; + } + updateUI( + state.language === 'pt' + ? '❌ Token expirado. Aguardando elemento Paint...' + : '❌ CAPTCHA token expired. Waiting for Paint button...', + 'error' + ); + const mainPaintBtn = await waitForSelector('button.btn.btn-primary.btn-lg, button.btn-primary.sm\\:btn-xl'); + if (mainPaintBtn) mainPaintBtn.click(); + await sleep(500); + updateUI( + state.language === 'pt' ? 'Selecionando transparente...' : 'Selecting transparent...', + 'status' + ); + const transBtn = await waitForSelector('button#color-0'); + if (transBtn) transBtn.click(); + await sleep(500); + const canvas = await waitForSelector('canvas'); + if (canvas) { + 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); + const moveEvt = new MouseEvent('mousemove', { + clientX: centerX, + clientY: centerY, + bubbles: true + }); + canvas.dispatchEvent(moveEvt); + const keyDown = new KeyboardEvent('keydown', { key: ' ', code: 'Space', bubbles: true }); + const keyUp = new KeyboardEvent('keyup', { key: ' ', code: 'Space', bubbles: true }); + canvas.dispatchEvent(keyDown); + canvas.dispatchEvent(keyUp); + } + await sleep(500); + updateUI( + state.language === 'pt' ? 'Confirmando pintura...' : 'Confirming paint...', + 'status' + ); + let confirmBtn = await 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; + } + confirmBtn?.click(); + } else { + // insufficient charges or auto-refresh disabled + if (state.autoRefresh && state.charges.count < 2) { + updateUI( + state.language === 'pt' + ? '⚡ Cargas insuficientes para auto-refresh. Por favor, clique manualmente.' + : 'Insufficient charges for auto-refresh. Please click manually.', + 'error' + ); + } + if (!state.pausedForManual) { + updateUI( + state.language === 'pt' + ? 'Auto-refresh desativado. Por favor, clique no botão pintura manualmente.' + : 'Auto-refresh disabled. Please click the Paint button manually.', + 'status' + ); + state.pausedForManual = true; + } + state.running = false; + return; + } + await sleep(1000); + continue; + } + + if (paintResult?.painted === 1) { + state.paintedCount++; + state.lastPixel = { + x: CONFIG.START_X + randomPos.x, + y: CONFIG.START_Y + randomPos.y, + time: new Date() + }; + state.charges.count--; + + document.getElementById('paintEffect').style.animation = 'pulse 0.5s'; + setTimeout(() => { + document.getElementById('paintEffect').style.animation = ''; + }, 500); + + updateUI(state.language === 'pt' ? '✅ Pixel pintado!' : '✅ Pixel painted!', 'success'); + } else { + updateUI(state.language === 'pt' ? '❌ Falha ao pintar' : '❌ Failed to paint', 'error'); + } + + await sleep(CONFIG.DELAY); + updateStats(); } - } + }; - function findClosestColor(rgb, palette) { - return palette.reduce( - (closest, current) => { - const currentDistance = Utils.colorDistance(rgb, current.rgb); - return currentDistance < closest.distance - ? { color: current, distance: currentDistance } - : closest; - }, - { color: palette[0], distance: Utils.colorDistance(rgb, palette[0].rgb) } - ).color.id; - } + const createUI = () => { + if (state.menuOpen) return; + state.menuOpen = true; - async function createUI() { - await detectLanguage(); - - const fontAwesome = document.createElement("link"); - fontAwesome.rel = "stylesheet"; - fontAwesome.href = - "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"; + const fontAwesome = document.createElement('link'); + fontAwesome.rel = 'stylesheet'; + fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'; document.head.appendChild(fontAwesome); - const style = document.createElement("style"); + const style = document.createElement('style'); style.textContent = ` @keyframes pulse { - 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); } + 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); }n 70% { box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); } 100% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); } } @@ -586,17 +309,17 @@ from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } - #wplace-image-bot-container { + .wplace-bot-panel { position: fixed; top: 20px; right: 20px; - width: 300px; + width: 250px; background: ${CONFIG.THEME.primary}; border: 1px solid ${CONFIG.THEME.accent}; border-radius: 8px; padding: 0; box-shadow: 0 5px 15px rgba(0,0,0,0.5); - z-index: 9998; + z-index: 9999; font-family: 'Segoe UI', Roboto, sans-serif; color: ${CONFIG.THEME.text}; animation: slideIn 0.4s ease-out; @@ -636,15 +359,15 @@ } .wplace-content { padding: 15px; - display: block; + display: ${state.minimized ? 'none' : 'block'}; } .wplace-controls { display: flex; - flex-direction: column; gap: 10px; margin-bottom: 15px; } .wplace-btn { + flex: 1; padding: 10px; border: none; border-radius: 6px; @@ -663,28 +386,10 @@ background: ${CONFIG.THEME.accent}; color: white; } - .wplace-btn-upload { - background: ${CONFIG.THEME.secondary}; - color: white; - border: 1px dashed ${CONFIG.THEME.text}; - } - .wplace-btn-start { - background: ${CONFIG.THEME.success}; - color: white; - } .wplace-btn-stop { background: ${CONFIG.THEME.error}; color: white; } - .wplace-btn-select { - background: ${CONFIG.THEME.highlight}; - color: black; - } - .wplace-btn:disabled { - opacity: 0.5; - cursor: not-allowed; - transform: none !important; - } .wplace-stats { background: ${CONFIG.THEME.secondary}; padding: 12px; @@ -703,18 +408,6 @@ gap: 6px; opacity: 0.8; } - .wplace-progress { - width: 100%; - background: ${CONFIG.THEME.secondary}; - border-radius: 4px; - margin: 10px 0; - overflow: hidden; - } - .wplace-progress-bar { - height: 10px; - background: ${CONFIG.THEME.highlight}; - transition: width 0.3s; - } .wplace-status { padding: 8px; border-radius: 4px; @@ -732,10 +425,6 @@ background: rgba(255, 0, 0, 0.1); color: ${CONFIG.THEME.error}; } - .status-warning { - background: rgba(255, 165, 0, 0.1); - color: orange; - } #paintEffect { position: absolute; top: 0; @@ -745,850 +434,213 @@ pointer-events: none; border-radius: 8px; } - .position-info { - font-size: 13px; - margin-top: 5px; - padding: 5px; - background: ${CONFIG.THEME.secondary}; - border-radius: 4px; - text-align: center; - } - .wplace-minimized .wplace-content { - display: none; - } - .resize-container { - display: none; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: ${CONFIG.THEME.primary}; - padding: 20px; - border-radius: 8px; - z-index: 10000; - box-shadow: 0 0 20px rgba(0,0,0,0.5); - max-width: 90%; - max-height: 90%; - overflow: auto; - } - .resize-preview { - max-width: 100%; - max-height: 300px; - margin: 10px 0; - border: 1px solid ${CONFIG.THEME.accent}; - } - .resize-controls { - display: flex; - flex-direction: column; - gap: 10px; - margin-top: 15px; - } - .resize-slider { - width: 100%; - } - .resize-buttons { - display: flex; - gap: 10px; - } - .resize-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0,0,0,0.7); - z-index: 9999; - display: none; - } `; document.head.appendChild(style); - const container = document.createElement("div"); - container.id = "wplace-image-bot-container"; - container.innerHTML = ` + const translations = { + pt: { + title: "WPlace Auto-Farm", + start: "Iniciar", + stop: "Parar", + ready: "Pronto para começar", + user: "Usuário", + pixels: "Pixels", + charges: "Cargas", + level: "Level" + }, + en: { + title: "WPlace Auto-Farm", + start: "Start", + stop: "Stop", + ready: "Ready to start", + user: "User", + pixels: "Pixels", + charges: "Charges", + level: "Level" + } + }; + + const t = translations[state.language] || translations.en; + + const panel = document.createElement('div'); + panel.className = 'wplace-bot-panel'; + panel.innerHTML = ` +