diff --git a/Auto-Image.js b/Auto-Image.js index f164dcf..fafa068 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -11,6 +11,8 @@ DEFAULT: 5, // Default 5 pixels per second }, PAINTING_SPEED_ENABLED: false, + AUTO_CAPTCHA_ENABLED: false, // Disabled by default + COOLDOWN_CHARGE_THRESHOLD: 1, // Default wait threshold // --- START: Color data from colour-converter.js --- COLOR_PALETTE: [ [0,0,0],[60,60,60],[120,120,120],[170,170,170],[210,210,210],[255,255,255], @@ -231,6 +233,16 @@ themeSettings: "Theme Settings", themeSettingsDesc: "Choose your preferred color theme for the interface.", languageSelectDesc: "Select your preferred language. Changes will take effect immediately.", + autoCaptcha: "Auto-CAPTCHA Solver", + autoCaptchaDesc: "Automatically attempts to solve the CAPTCHA by simulating a manual pixel placement when the token expires.", + applySettings: "Apply Settings", + settingsSaved: "✅ Settings saved successfully!", + cooldownSettings: "Cooldown Settings", + waitCharges: "Wait until charges reach", + captchaSolving: "🤖 Attempting to solve CAPTCHA...", + captchaFailed: "❌ Auto-CAPTCHA failed. Paint a pixel manually.", + automation: "Automation", + noChargesThreshold: "⌛ Waiting for charges to reach {threshold}. Currently {current}. Next in {time}...", }, ru: { title: "WPlace Авто-Изображение", @@ -290,7 +302,17 @@ language: "Язык", themeSettings: "Настройки темы", themeSettingsDesc: "Выберите предпочтительную цветовую тему интерфейса.", - languageSelectDesc: "Выберите предпочтительный язык. Изменения вступят в силу немедленно." + languageSelectDesc: "Выберите предпочтительный язык. Изменения вступят в силу немедленно.", + autoCaptcha: "Авто-решение CAPTCHA", + autoCaptchaDesc: "Автоматически пытается решить CAPTCHA, симулируя ручное размещение пикселя, когда токен истекает.", + applySettings: "Применить настройки", + settingsSaved: "✅ Настройки успешно сохранены!", + cooldownSettings: "Настройки перезарядки", + waitCharges: "Ждать до накопления зарядов", + captchaSolving: "🤖 Пытаюсь решить CAPTCHA...", + captchaFailed: "❌ Не удалось решить CAPTCHA. Нарисуйте пиксель вручную.", + automation: "Автоматизация", + noChargesThreshold: "⌛ Ожидание зарядов до {threshold}. Сейчас {current}. Следующий через {time}...", }, pt: { title: "WPlace Auto-Image", @@ -351,6 +373,16 @@ themeSettings: "Configurações de Tema", themeSettingsDesc: "Escolha seu tema de cores preferido para a interface.", languageSelectDesc: "Selecione seu idioma preferido. As alterações terão efeito imediatamente.", + autoCaptcha: "Resolvedor de CAPTCHA Automático", + autoCaptchaDesc: "Tenta resolver o CAPTCHA automaticamente simulando a colocação manual de um pixel quando o token expira.", + applySettings: "Aplicar Configurações", + settingsSaved: "✅ Configurações salvas com sucesso!", + cooldownSettings: "Configurações de Cooldown", + waitCharges: "Aguardar até as cargas atingirem", + captchaSolving: "🤖 Tentando resolver o CAPTCHA...", + captchaFailed: "❌ Falha ao resolver CAPTCHA. Pinte um pixel manualmente.", + automation: "Automação", + noChargesThreshold: "⌛ Aguardando cargas atingirem {threshold}. Atual: {current}. Próxima em {time}...", }, vi: { title: "WPlace Auto-Image", @@ -411,6 +443,16 @@ themeSettings: "Cài đặt Giao diện", themeSettingsDesc: "Chọn chủ đề màu sắc yêu thích cho giao diện.", languageSelectDesc: "Chọn ngôn ngữ ưa thích. Thay đổi sẽ có hiệu lực ngay lập tức.", + autoCaptcha: "Tự động giải CAPTCHA", + autoCaptchaDesc: "Tự động cố gắng giải CAPTCHA bằng cách mô phỏng việc đặt pixel thủ công khi token hết hạn.", + applySettings: "Áp dụng cài đặt", + settingsSaved: "✅ Đã lưu cài đặt thành công!", + cooldownSettings: "Cài đặt thời gian chờ", + waitCharges: "Chờ cho đến khi số lần sạc đạt", + captchaSolving: "🤖 Đang cố gắng giải CAPTCHA...", + captchaFailed: "❌ Giải CAPTCHA tự động thất bại. Vui lòng vẽ một pixel thủ công.", + automation: "Tự động hóa", + noChargesThreshold: "⌛ Đang chờ số lần sạc đạt {threshold}. Hiện tại {current}. Lần tiếp theo trong {time}...", }, fr: { title: "WPlace Auto-Image", @@ -471,6 +513,16 @@ themeSettings: "Paramètres de Thème", themeSettingsDesc: "Choisissez votre thème de couleurs préféré pour l'interface.", languageSelectDesc: "Sélectionnez votre langue préférée. Les changements prendront effet immédiatement.", + autoCaptcha: "Résolveur de CAPTCHA automatique", + autoCaptchaDesc: "Tente automatiquement de résoudre le CAPTCHA en simulant un placement manuel de pixel lorsque le jeton expire.", + applySettings: "Appliquer les paramètres", + settingsSaved: "✅ Paramètres enregistrés avec succès !", + cooldownSettings: "Paramètres de recharge", + waitCharges: "Attendre que les charges atteignent", + captchaSolving: "🤖 Tentative de résolution du CAPTCHA...", + captchaFailed: "❌ Échec de l'Auto-CAPTCHA. Peignez un pixel manuellement.", + automation: "Automatisation", + noChargesThreshold: "⌛ En attente que les charges atteignent {threshold}. Actuel: {current}. Prochaine dans {time}...", }, } @@ -485,6 +537,7 @@ activeColorPalette: [], // User-selected colors for conversion paintWhitePixels: true, // Default to ON currentCharges: 0, + maxCharges: 1, // Default max charges cooldown: CONFIG.COOLDOWN_DEFAULT, imageData: null, stopFlag: false, @@ -497,6 +550,7 @@ estimatedTime: 0, language: "en", paintingSpeed: CONFIG.PAINTING_SPEED.DEFAULT, // pixels per second + cooldownChargeThreshold: CONFIG.COOLDOWN_CHARGE_THRESHOLD, } // Placeholder for the resize preview update function @@ -560,6 +614,16 @@ const Utils = { sleep: (ms) => new Promise((r) => setTimeout(r, ms)), + 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; + }, + // Optimized DOM creation helpers createElement: (tag, props = {}, children = []) => { const element = document.createElement(tag) @@ -1019,12 +1083,14 @@ const data = await res.json() return { charges: data.charges?.count || 0, + max: data.charges?.max || 1, cooldown: data.charges?.next || CONFIG.COOLDOWN_DEFAULT, } } catch (e) { console.error("Failed to get charges:", e) return { charges: 0, + max: 1, cooldown: CONFIG.COOLDOWN_DEFAULT, } } @@ -1160,6 +1226,71 @@ } // --- END: Color Palette Functions --- + // --- START: Auto-CAPTCHA Solver --- + async function handleCaptcha() { + return new Promise(async (resolve, reject) => { + if (!CONFIG.AUTO_CAPTCHA_ENABLED) { + return reject(new Error("Auto-CAPTCHA is disabled.")); + } + + try { + // Set a timeout for the entire operation + const timeoutPromise = Utils.sleep(20000).then(() => reject(new Error("Auto-CAPTCHA timed out."))); + + const solvePromise = (async () => { + // 1. Find and click the main "Paint" button on the screen + 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); + + // 2. Select the transparent color to avoid wasting a real color + 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); + + // 3. Find the canvas and simulate a click + 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); + + // 4. Find and click the confirmation button + 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) throw new Error("Could not find the confirmation button."); + confirmBtn.click(); + + // The fetch interceptor will capture the token. We wait for it. + await tokenPromise; + resolve(); + })(); + + await Promise.race([solvePromise, timeoutPromise]); + + } catch (error) { + console.error("Auto-CAPTCHA process failed:", error); + reject(error); + } + }); + } + // --- END: Auto-CAPTCHA Solver --- + + async function createUI() { await detectLanguage() @@ -1950,6 +2081,39 @@ } /* --- END: Color Palette Styles --- */ + /* Cooldown slider styles */ + .wplace-cooldown-control { + margin-top: 8px; + } + .wplace-cooldown-control label { + font-size: 11px; + margin-bottom: 4px; + display: block; + } + .wplace-slider-container { + display: flex; + align-items: center; + gap: 8px; + } + .wplace-slider { + flex: 1; + -webkit-appearance: none; + appearance: none; + height: 4px; + background: #444; + border-radius: 2px; + outline: none; + } + .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + background: ${theme.highlight}; + border-radius: 50%; + cursor: pointer; + } + + ${ CONFIG.currentTheme === "Neon Retro" ? ` @@ -2384,6 +2548,18 @@ + +