From 2ced1e780ef8bff693f77a6ed21c30612cbd79f5 Mon Sep 17 00:00:00 2001 From: DarkMode Date: Wed, 6 Aug 2025 02:22:07 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=96=BC=EF=B8=8F=E2=94=83Auto-Image=20(=20?= =?UTF-8?q?V01=20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Auto-Image.js | 822 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 822 insertions(+) create mode 100644 Auto-Image.js diff --git a/Auto-Image.js b/Auto-Image.js new file mode 100644 index 0000000..1081e7e --- /dev/null +++ b/Auto-Image.js @@ -0,0 +1,822 @@ +(async () => { + // Configurações + const CONFIG = { + COOLDOWN_DEFAULT: 31000, + TRANSPARENCY_THRESHOLD: 100, + LOG_INTERVAL: 10, + THEME: { + primary: '#000000', + secondary: '#111111', + accent: '#222222', + text: '#ffffff', + highlight: '#4cc9f0', + success: '#00ff00', + error: '#ff0000', + warning: '#ffaa00' + } + }; + + // Estado do aplicativo + 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, + minimized: false, + lastPosition: { x: 0, y: 0 } // Armazena a última posição pintada + }; + + // Funções utilitárias + 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-"]'); + const availableColors = []; + + colorElements.forEach(el => { + if (!el.querySelector('svg')) { + const id = parseInt(el.id.replace('color-', '')); + const rgbStr = el.style.backgroundColor.match(/\d+/g); + const rgb = rgbStr ? rgbStr.map(Number) : [0, 0, 0]; + availableColors.push({ id, rgb }); + } + }); + + return availableColors; + }, + + formatTime: ms => { + const seconds = Math.floor((ms / 1000) % 60); + const minutes = Math.floor((ms / (1000 * 60)) % 60); + return `${minutes}m ${seconds}s`; + }, + + 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 = type === 'error' ? CONFIG.THEME.error : + type === 'success' ? CONFIG.THEME.success : + type === 'warning' ? CONFIG.THEME.warning : 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'; + alert.innerHTML = ` + + ${message} + `; + + document.body.appendChild(alert); + + setTimeout(() => { + alert.style.opacity = '0'; + alert.style.transition = 'opacity 0.5s'; + setTimeout(() => alert.remove(), 500); + }, 3000); + } + }; + + // Serviço de comunicação com a API + const WPlaceService = { + async paintPixelInRegion(regionX, regionY, pixelX, pixelY, color) { + try { + 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({ coords: [pixelX, pixelY], colors: [color] }) + }); + const data = await res.json(); + return data?.painted === 1; + } catch (error) { + console.error('Erro ao pintar pixel:', error); + 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 (error) { + console.error('Erro ao obter cargas:', error); + return { charges: 0, cooldown: CONFIG.COOLDOWN_DEFAULT }; + } + } + }; + + // Processador de imagem + class ImageProcessor { + constructor(imageSrc) { + this.imageSrc = imageSrc; + this.img = new Image(); + this.canvas = document.createElement('canvas'); + this.ctx = this.canvas.getContext('2d'); + } + + async load() { + return new Promise(resolve => { + 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.src = this.imageSrc; + }); + } + + getPixelData() { + return this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height).data; + } + + getDimensions() { + return { width: this.canvas.width, height: this.canvas.height }; + } + } + + // Encontra a cor mais próxima + function findClosestColor(rgb, palette) { + let closestColor = palette[0]; + let minDistance = Utils.colorDistance(rgb, palette[0].rgb); + + for (let i = 1; i < palette.length; i++) { + const distance = Utils.colorDistance(rgb, palette[i].rgb); + if (distance < minDistance) { + minDistance = distance; + closestColor = palette[i]; + } + } + + return closestColor.id; + } + + // Cria a interface do usuário + function createUI() { + // Adiciona Font Awesome + 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); + + // Adiciona estilos + const style = document.createElement('style'); + style.textContent = ` + @keyframes pulse { + 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); } + 70% { box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); } + 100% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); } + } + @keyframes slideIn { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } + } + #wplace-image-bot-container { + position: fixed; + top: 20px; + right: 20px; + width: 300px; + 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; + font-family: 'Segoe UI', Roboto, sans-serif; + color: ${CONFIG.THEME.text}; + animation: slideIn 0.4s ease-out; + overflow: hidden; + } + .wplace-header { + padding: 12px 15px; + background: ${CONFIG.THEME.secondary}; + color: ${CONFIG.THEME.highlight}; + font-size: 16px; + font-weight: 600; + display: flex; + justify-content: space-between; + align-items: center; + cursor: move; + user-select: none; + } + .wplace-header-title { + display: flex; + align-items: center; + gap: 8px; + } + .wplace-header-controls { + display: flex; + gap: 10px; + } + .wplace-header-btn { + background: none; + border: none; + color: ${CONFIG.THEME.text}; + cursor: pointer; + opacity: 0.7; + transition: opacity 0.2s; + } + .wplace-header-btn:hover { + opacity: 1; + } + .wplace-content { + padding: 15px; + display: block; + } + .wplace-controls { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 15px; + } + .wplace-btn { + padding: 10px; + border: none; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + transition: all 0.2s; + } + .wplace-btn:hover { + transform: translateY(-2px); + } + .wplace-btn-primary { + 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; + border-radius: 6px; + margin-bottom: 15px; + } + .wplace-stat-item { + display: flex; + justify-content: space-between; + padding: 6px 0; + font-size: 14px; + } + .wplace-stat-label { + display: flex; + align-items: center; + 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; + text-align: center; + font-size: 13px; + } + .status-default { + background: rgba(255,255,255,0.1); + } + .status-success { + background: rgba(0, 255, 0, 0.1); + color: ${CONFIG.THEME.success}; + } + .status-error { + 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; + left: 0; + width: 100%; + height: 100%; + 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; + } + `; + document.head.appendChild(style); + + // Cria o container principal + const container = document.createElement('div'); + container.id = 'wplace-image-bot-container'; + container.innerHTML = ` +
+
+
+ + WPlace Auto-Image +
+
+ +
+
+
+
+ + + + + + +
+ +
+
+
+ +
+
+
+
Clique em "Iniciar Auto-BOT" para começar
+
+
+
+ +
+ Aguardando inicialização... +
+
+ `; + + document.body.appendChild(container); + + // Tornar arrastável + const header = container.querySelector('.wplace-header'); + let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + + header.onmousedown = dragMouseDown; + + function dragMouseDown(e) { + if (e.target.closest('.wplace-header-btn')) return; + + e = e || window.event; + e.preventDefault(); + pos3 = e.clientX; + pos4 = e.clientY; + document.onmouseup = closeDragElement; + document.onmousemove = elementDrag; + } + + function elementDrag(e) { + e = e || window.event; + e.preventDefault(); + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + container.style.top = (container.offsetTop - pos2) + "px"; + container.style.left = (container.offsetLeft - pos1) + "px"; + } + + function closeDragElement() { + document.onmouseup = null; + document.onmousemove = null; + } + + // Elementos da UI + const initBotBtn = container.querySelector('#initBotBtn'); + const uploadBtn = container.querySelector('#uploadBtn'); + const selectPosBtn = container.querySelector('#selectPosBtn'); + const startBtn = container.querySelector('#startBtn'); + const stopBtn = container.querySelector('#stopBtn'); + const minimizeBtn = container.querySelector('#minimizeBtn'); + const statusText = container.querySelector('#statusText'); + const progressBar = container.querySelector('#progressBar'); + const statsArea = container.querySelector('#statsArea'); + const positionInfo = container.querySelector('#positionInfo'); + const content = container.querySelector('.wplace-content'); + + // Minimizar/Maximizar + minimizeBtn.addEventListener('click', () => { + state.minimized = !state.minimized; + if (state.minimized) { + container.classList.add('wplace-minimized'); + minimizeBtn.innerHTML = ''; + } else { + container.classList.remove('wplace-minimized'); + minimizeBtn.innerHTML = ''; + } + }); + + // Atualiza a UI + window.updateUI = (message, type = 'default') => { + statusText.textContent = message; + statusText.className = `wplace-status status-${type}`; + statusText.style.animation = 'none'; + void statusText.offsetWidth; + statusText.style.animation = 'slideIn 0.3s ease-out'; + }; + + // Atualiza as estatísticas + window.updateStats = async () => { + if (!state.colorsChecked) return; + + const { charges, cooldown } = await WPlaceService.getCharges(); + state.currentCharges = Math.floor(charges); // Arredonda para baixo + state.cooldown = cooldown; + + const progress = state.totalPixels > 0 ? + Math.round((state.paintedPixels / state.totalPixels) * 100) : 0; + const remainingPixels = state.totalPixels - state.paintedPixels; + + progressBar.style.width = `${progress}%`; + + statsArea.innerHTML = ` +
+
Progresso
+
${progress}%
+
+
+
Pixels
+
${state.paintedPixels}/${state.totalPixels}
+
+
+
Cargas
+
${Math.floor(state.currentCharges)}
+
+
+
Restantes
+
${remainingPixels} pixels
+
+ `; + }; + + // Atualiza a informação da posição + function updatePositionInfo() { + if (state.startPosition && state.region) { + positionInfo.style.display = 'block'; + positionInfo.innerHTML = ` + + Posição: (${state.startPosition.x}, ${state.startPosition.y}) | Região: ${state.region.x}/${state.region.y} + `; + } else { + positionInfo.style.display = 'none'; + } + } + + // Evento do botão Iniciar Auto-BOT + initBotBtn.addEventListener('click', async () => { + try { + updateUI('🔍 Verificando cores disponíveis...', 'default'); + + state.availableColors = Utils.extractAvailableColors(); + + if (state.availableColors.length === 0) { + Utils.showAlert('Abra a paleta de cores no site e tente novamente!', 'error'); + updateUI('❌ Nenhuma cor disponível encontrada', 'error'); + return; + } + + state.colorsChecked = true; + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + initBotBtn.style.display = 'none'; // Esconde o botão após iniciar + + updateUI(`✅ ${state.availableColors.length} cores disponíveis encontradas`, 'success'); + updateStats(); + + } catch (error) { + console.error('Erro ao verificar cores:', error); + updateUI('❌ Erro ao verificar cores', 'error'); + } + }); + + // Evento do botão Upload da Imagem + uploadBtn.addEventListener('click', async () => { + try { + updateUI('🖼️ Carregando imagem...', 'default'); + const imageSrc = await Utils.createImageUploader(); + + const processor = new ImageProcessor(imageSrc); + await processor.load(); + + const { width, height } = processor.getDimensions(); + const pixels = processor.getPixelData(); + + state.imageData = { + width, + height, + pixels, + totalPixels: width * height + }; + + state.totalPixels = state.imageData.totalPixels; + state.paintedPixels = 0; + state.imageLoaded = true; + state.lastPosition = { x: 0, y: 0 }; // Reseta a última posição + + if (state.startPosition) { + startBtn.disabled = false; + } + + updateStats(); + updateUI('✅ Imagem carregada com sucesso!', 'success'); + } catch (error) { + console.error('Erro ao carregar imagem:', error); + updateUI('❌ Erro ao carregar imagem', 'error'); + } + }); + + // Evento do botão Selecionar Posição + selectPosBtn.addEventListener('click', async () => { + if (state.selectingPosition) return; + + state.selectingPosition = true; + state.startPosition = null; + state.region = null; + startBtn.disabled = true; + updatePositionInfo(); + + Utils.showAlert('Pinte o primeiro pixel na localização onde deseja que a arte comece!', 'info'); + updateUI('👆 Aguardando você pintar o pixel de referência...', 'default'); + + const originalFetch = window.fetch; + + window.fetch = async (url, options) => { + if (typeof url === 'string' && + url.includes('https://backend.wplace.live/s0/pixel/') && + options?.method?.toUpperCase() === 'POST') { + + try { + const response = await originalFetch(url, options); + const clonedResponse = response.clone(); + const data = await clonedResponse.json(); + + if (data?.painted === 1) { + const regionMatch = url.match(/\/pixel\/(\d+)\/(\d+)/); + if (regionMatch && regionMatch.length >= 3) { + state.region = { + x: parseInt(regionMatch[1]), + y: parseInt(regionMatch[2]) + }; + } + + const payload = JSON.parse(options.body); + if (payload?.coords && Array.isArray(payload.coords)) { + state.startPosition = { + x: payload.coords[0], + y: payload.coords[1] + }; + state.lastPosition = { x: 0, y: 0 }; // Reseta a última posição + + updatePositionInfo(); + updateUI('✅ Posição definida com sucesso!', 'success'); + Utils.showAlert(`Posição capturada na região ${state.region.x}/${state.region.y}!`, 'success'); + + if (state.imageLoaded) { + startBtn.disabled = false; + } + + window.fetch = originalFetch; + state.selectingPosition = false; + } + } + + return response; + } catch (error) { + console.error('Erro ao interceptar requisição:', error); + return originalFetch(url, options); + } + } + return originalFetch(url, options); + }; + + setTimeout(() => { + if (state.selectingPosition) { + window.fetch = originalFetch; + state.selectingPosition = false; + updateUI('❌ Tempo esgotado para selecionar posição', 'error'); + Utils.showAlert('Tempo esgotado! Clique em "Selecionar Posição" novamente.', 'error'); + } + }, 120000); + }); + + // Evento do botão Iniciar Pintura + startBtn.addEventListener('click', async () => { + if (!state.imageLoaded || !state.startPosition || !state.region) { + updateUI('❌ Carregue uma imagem e selecione uma posição primeiro', 'error'); + return; + } + + state.running = true; + state.stopFlag = false; + startBtn.disabled = true; + stopBtn.disabled = false; + uploadBtn.disabled = true; + selectPosBtn.disabled = true; + + updateUI(`🎨 Iniciando pintura na região ${state.region.x}/${state.region.y}...`, 'success'); + + try { + await processImage(); + } catch (error) { + console.error('Erro durante a pintura:', error); + updateUI('❌ Erro durante a pintura', 'error'); + } finally { + state.running = false; + stopBtn.disabled = true; + + if (!state.stopFlag) { + startBtn.disabled = true; + uploadBtn.disabled = false; + selectPosBtn.disabled = false; + } else { + startBtn.disabled = false; + } + } + }); + + // Evento do botão Parar Pintura + stopBtn.addEventListener('click', () => { + state.stopFlag = true; + state.running = false; + stopBtn.disabled = true; + updateUI('⏹️ Pintura interrompida pelo usuário', 'warning'); + }); + } + + // Processa a imagem + async function processImage() { + const { width, height, pixels } = state.imageData; + const { x: startX, y: startY } = state.startPosition; + const { x: regionX, y: regionY } = state.region; + + // Começa da última posição ou do início + let startRow = state.lastPosition.y || 0; + let startCol = state.lastPosition.x || 0; + + outerLoop: + for (let y = startRow; y < height; y++) { + for (let x = (y === startRow ? startCol : 0); x < width; x++) { + if (state.stopFlag) { + // Salva a última posição pintada + state.lastPosition = { x, y }; + break outerLoop; + } + + const idx = (y * width + x) * 4; + const rgb = [pixels[idx], pixels[idx + 1], pixels[idx + 2]]; + const alpha = pixels[idx + 3]; + + if (alpha < CONFIG.TRANSPARENCY_THRESHOLD) continue; + + const colorId = findClosestColor(rgb, state.availableColors); + + if (state.currentCharges < 1) { + updateUI(`⌛ Sem cargas. Aguardando ${Utils.formatTime(state.cooldown)}...`, 'warning'); + await Utils.sleep(state.cooldown); + + const chargeUpdate = await WPlaceService.getCharges(); + state.currentCharges = chargeUpdate.charges; + state.cooldown = chargeUpdate.cooldown; + } + + const pixelX = startX + x; + const pixelY = startY + y; + + const success = await WPlaceService.paintPixelInRegion( + regionX, + regionY, + pixelX, + pixelY, + colorId + ); + + if (success) { + state.paintedPixels++; + state.currentCharges--; + + if (state.paintedPixels % CONFIG.LOG_INTERVAL === 0) { + updateStats(); + updateUI(`🧱 Progresso: ${state.paintedPixels}/${state.totalPixels} pixels...`, 'default'); + } + } + } + } + + if (state.stopFlag) { + updateUI('⏹️ Pintura interrompida pelo usuário', 'warning'); + } else { + updateUI(`✅ Pintura concluída! ${state.paintedPixels} pixels pintados.`, 'success'); + state.lastPosition = { x: 0, y: 0 }; // Reseta para próxima execução + } + + updateStats(); + } + + // Inicialização + createUI(); + console.log('WPlace Image Bot - Interface carregada com sucesso!'); +})();