diff --git a/Extension/auto-image-styles.css b/Extension/auto-image-styles.css index dff366b..a4b057f 100644 --- a/Extension/auto-image-styles.css +++ b/Extension/auto-image-styles.css @@ -2037,22 +2037,23 @@ .wplace-account-item.current { background: var(--wplace-success)20; border-left: 3px solid var(--wplace-success); - box-shadow: 0 0 15px rgba(46, 204, 113, 0.6), - 0 0 25px rgba(46, 204, 113, 0.4), - 0 0 35px rgba(46, 204, 113, 0.2); + box-shadow: 0 0 15px rgba(46, 204, 113, 0.6), + 0 0 25px rgba(46, 204, 113, 0.4), + 0 0 35px rgba(46, 204, 113, 0.2); animation: currentAccountGlow 2s ease-in-out infinite alternate; } @keyframes currentAccountGlow { from { - box-shadow: 0 0 15px rgba(46, 204, 113, 0.6), - 0 0 25px rgba(46, 204, 113, 0.4), - 0 0 35px rgba(46, 204, 113, 0.2); + box-shadow: 0 0 15px rgba(46, 204, 113, 0.6), + 0 0 25px rgba(46, 204, 113, 0.4), + 0 0 35px rgba(46, 204, 113, 0.2); } + to { - box-shadow: 0 0 20px rgba(46, 204, 113, 0.8), - 0 0 30px rgba(46, 204, 113, 0.6), - 0 0 40px rgba(46, 204, 113, 0.4); + box-shadow: 0 0 20px rgba(46, 204, 113, 0.8), + 0 0 30px rgba(46, 204, 113, 0.6), + 0 0 40px rgba(46, 204, 113, 0.4); } } @@ -2061,6 +2062,7 @@ box-shadow: 0 0 10px rgba(46, 204, 113, 0.8); transform: scale(1); } + to { box-shadow: 0 0 15px rgba(46, 204, 113, 1.0); transform: scale(1.05); @@ -2191,4 +2193,47 @@ input[type="number"] { .wplace-notification-interval-input { -moz-appearance: textfield; appearance: textfield; +} + +.pill-container { + position: relative; + display: flex; + background: #2d1f40; + border-radius: 50px; + width: 100%; + max-width: 280px; + box-sizing: border-box; + padding: 9px; +} + +.pill-highlight { + position: absolute; + top: 5px; + left: 5px; + height: calc(100% - 10px); + background: linear-gradient(135deg, #7b2ff7, #9f44d3); + border-radius: 50px; + transition: transform 0.4s ease; + z-index: 0; + width: calc((100% - 10px) / 3); + /* evenly divide space for 3 buttons */ +} + + +.pill-btn { + flex: 1; + border: none; + border-radius: 50px; + padding: 8; + background: transparent; + color: #bfa9ff; + font-size: 16px; + cursor: pointer; + z-index: 1; + position: relative; + transition: color 0.3s ease; +} + +.pill-btn.active { + color: white; } \ No newline at end of file diff --git a/Extension/background.js b/Extension/background.js index bb0789e..fe15f65 100644 --- a/Extension/background.js +++ b/Extension/background.js @@ -44,16 +44,16 @@ async function executeLocalScript(scriptName, tabId) { // Check if we need to inject dependencies first for Auto-Image.js or Art-Extractor.js if (scriptName === 'Auto-Image.js' || scriptName === 'Art-Extractor.js') { console.log(`🔑 ${scriptName} detected, ensuring dependencies are loaded first...`); - + try { // First inject token-manager.js const tokenManagerUrl = chrome.runtime.getURL('scripts/token-manager.js'); const tokenManagerResponse = await fetch(tokenManagerUrl); - + if (tokenManagerResponse.ok) { const tokenManagerCode = await tokenManagerResponse.text(); console.log('🔑 Token manager loaded, injecting first...'); - + // Execute token manager first await chrome.scripting.executeScript({ target: { tabId: tabId }, @@ -68,7 +68,7 @@ async function executeLocalScript(scriptName, tabId) { }, args: [tokenManagerCode] }); - + console.log('✅ Token manager injected successfully'); } else { console.warn('⚠️ Could not load token-manager.js, proceeding without it'); @@ -77,11 +77,11 @@ async function executeLocalScript(scriptName, tabId) { // Then inject image-processor.js const imageProcessorUrl = chrome.runtime.getURL('scripts/image-processor.js'); const imageProcessorResponse = await fetch(imageProcessorUrl); - + if (imageProcessorResponse.ok) { const imageProcessorCode = await imageProcessorResponse.text(); console.log('🖼️ Image processor loaded, injecting second...'); - + // Execute image processor second await chrome.scripting.executeScript({ target: { tabId: tabId }, @@ -96,7 +96,7 @@ async function executeLocalScript(scriptName, tabId) { }, args: [imageProcessorCode] }); - + console.log('✅ Image processor injected successfully'); } else { console.warn('⚠️ Could not load image-processor.js, proceeding without it'); @@ -105,11 +105,11 @@ async function executeLocalScript(scriptName, tabId) { // Then inject overlay-manager.js const overlayManagerUrl = chrome.runtime.getURL('scripts/overlay-manager.js'); const overlayManagerResponse = await fetch(overlayManagerUrl); - + if (overlayManagerResponse.ok) { const overlayManagerCode = await overlayManagerResponse.text(); console.log('🎨 Overlay manager loaded, injecting third...'); - + // Execute overlay manager thirds await chrome.scripting.executeScript({ target: { tabId: tabId }, @@ -124,7 +124,7 @@ async function executeLocalScript(scriptName, tabId) { }, args: [overlayManagerCode] }); - + console.log('✅ Overlay manager injected successfully'); } else { console.warn('⚠️ Could not load overlay-manager.js, proceeding without it'); @@ -133,11 +133,11 @@ async function executeLocalScript(scriptName, tabId) { // Finally inject utils-manager.js const utilsManagerUrl = chrome.runtime.getURL('scripts/utils-manager.js'); const utilsManagerResponse = await fetch(utilsManagerUrl); - + if (utilsManagerResponse.ok) { const utilsManagerCode = await utilsManagerResponse.text(); console.log('🛠️ Utils manager loaded, injecting fourth...'); - + // Execute utils manager fourth await chrome.scripting.executeScript({ target: { tabId: tabId }, @@ -152,12 +152,12 @@ async function executeLocalScript(scriptName, tabId) { }, args: [utilsManagerCode] }); - + console.log('✅ Utils manager injected successfully'); } else { console.warn('⚠️ Could not load utils-manager.js, proceeding without it'); } - + // Small delay to ensure dependencies are fully initialized await new Promise(resolve => setTimeout(resolve, 200)); } catch (error) { @@ -270,15 +270,32 @@ async function executeLocalScript(scriptName, tabId) { // Helper function to get language data with detailed logging window.getLanguage = function (lang = 'en') { - const langFile = lang + '.json'; - const result = window.AUTOBOT_LANGUAGES[langFile] || window.AUTOBOT_LANGUAGES['en.json'] || {}; + const langFile = `${lang}.json`; + let sourceFile = langFile; + let successMessage = 'Found exact match'; + let result = window.AUTOBOT_LANGUAGES[langFile]; + + if (!result) { + const localeMatch = Object.keys(window.AUTOBOT_LANGUAGES).find(file => file.toLowerCase().startsWith(`${lang.toLowerCase()}-`)); + if (localeMatch) { + result = window.AUTOBOT_LANGUAGES[localeMatch]; + sourceFile = localeMatch; + successMessage = `Using regional fallback (${localeMatch.replace('.json', '')})`; + } + } + + if (!result) { + result = window.AUTOBOT_LANGUAGES['en.json'] || {}; + sourceFile = 'en.json'; + successMessage = 'Fallback to English'; + } console.group(`%c🔤 Language Access: ${lang.toUpperCase()}`, 'color: #06b6d4; font-weight: bold;'); console.log(` 📋 Requested: ${lang}`); - console.log(` 📄 File: ${langFile}`); + console.log(` 📄 File: ${sourceFile}`); console.log(` 📍 Source: Extension local file`); console.log(` 📏 Keys returned: ${Object.keys(result).length}`); - console.log(` ✅ Success: ${window.AUTOBOT_LANGUAGES[langFile] ? 'Found exact match' : 'Fallback to English'}`); + console.log(` ✅ Success: ${successMessage}`); console.log(` 📝 Data preview:`, result); console.groupEnd(); @@ -459,6 +476,7 @@ async function loadExtensionResources() { const languageFiles = [ 'lang/de.json', 'lang/en.json', + 'lang/es.json', 'lang/fr.json', 'lang/id.json', 'lang/ja.json', @@ -536,6 +554,12 @@ async function loadExtensionResources() { } } } + + // Ensure base Spanish file is available even if only regional variant exists + if (!resources.languages['es.json'] && resources.languages['es-MX.json']) { + resources.languages['es.json'] = resources.languages['es-MX.json']; + console.log(`%c✅ Added fallback mapping: es-MX.json ➝ es.json`, 'color: #10b981; font-weight: bold;'); + } console.groupEnd(); const loadTime = performance.now() - startTime; @@ -757,7 +781,7 @@ chrome.webNavigation.onCompleted.addListener( if (details.url.includes("wplace.live")) { console.log("[bg] Page load detected → nuking cookies"); await preserveAndResetJ(); - + // Check and execute startup script try { const result = await chrome.storage.local.get('startupScript'); @@ -804,7 +828,7 @@ async function setCookie(value) { }); } }); - + resolve(cookie); } } diff --git a/Extension/lang/de.json b/Extension/lang/de.json index b2d2f4b..5db2238 100644 --- a/Extension/lang/de.json +++ b/Extension/lang/de.json @@ -54,6 +54,7 @@ "paintingSpeed": "Malgeschwindigkeit", "pixelsPerSecond": "Pixel/Sekunde", "speedSetting": "Geschwindigkeit: {speed} Pixel/Sek", + "keepAspectRatio": "Seitenverhältnis beibehalten", "settings": "Einstellungen", "botSettings": "Bot-Einstellungen", "close": "Schließen", @@ -70,5 +71,86 @@ "captchaSolving": "🔑 Generiere Turnstile-Token...", "captchaFailed": "❌ Turnstile-Token-Generierung fehlgeschlagen. Versuche Fallback-Methode...", "automation": "Automatisierung", - "noChargesThreshold": "⌛ Warte bis {threshold} Ladungen erreicht sind. Aktuell {current}. Nächste in {time}..." + "noChargesThreshold": "⌛ Warte bis {threshold} Ladungen erreicht sind. Aktuell {current}. Nächste in {time}...", + "tokenCapturedSuccess": "Token erfolgreich erfasst! Du kannst jetzt mit dem Bot beginnen.", + "notificationsNotSupported": "Benachrichtigungen werden in diesem Browser nicht unterstützt.", + "chargesReadyNotification": "WPlace — Ladungen bereit", + "chargesReadyMessage": "Ladungen bereit: {current} / {max}. Schwellenwert: {threshold}.", + "testNotificationTitle": "WPlace — Test", + "testNotificationMessage": "Dies ist eine Test-Benachrichtigung.", + "showStats": "Statistiken anzeigen", + "compactMode": "Kompaktmodus", + "refreshCharges": "Ladungen aktualisieren", + "closeStats": "Statistiken schließen", + "zoomOut": "Verkleinern", + "zoomIn": "Vergrößern", + "fitToView": "An Ansicht anpassen", + "actualSize": "Tatsächliche Größe (100%)", + "panMode": "Schwenken (ziehen zum Bewegen)", + "clearIgnoredPixels": "Alle ignorierten Pixel löschen", + "invertMask": "Maske invertieren", + "waitingSetupComplete": "🔄 Warte auf Abschluss der Ersteinrichtung...", + "waitingTokenGenerator": "🔄 Warte auf Token-Generator-Initialisierung...", + "uploadImageFirst": "Lade zuerst ein Bild hoch, um verfügbare Farben zu erfassen", + "pleaseWaitInitialSetup": "🔄 Bitte warte, bis die Ersteinrichtung abgeschlossen ist, bevor du Fortschritt lädst.", + "pleaseWaitFileSetup": "🔄 Bitte warte, bis die Ersteinrichtung abgeschlossen ist, bevor du aus Datei lädst.", + "errorSavingProgress": "❌ Fehler beim Speichern des Fortschritts", + "errorLoadingProgress": "❌ Fehler beim Laden des Fortschritts", + "fileOperationsAvailable": "📂 Dateioperationen (Laden/Hochladen) sind jetzt verfügbar!", + "tokenGeneratorReady": "🔑 Token-Generator bereit!", + "paintingStats": "Malstatistiken", + "enablePaintingSpeedLimit": "Malgeschwindigkeitslimit aktivieren (Batch-Größenkontrolle)", + "enableNotifications": "Benachrichtigungen aktivieren", + "notifyOnChargesThreshold": "Benachrichtigen, wenn Ladungen Schwellenwert erreichen", + "onlyWhenNotFocused": "Nur wenn Tab nicht fokussiert ist", + "repeatEvery": "Wiederholen alle", + "minutesPl": "Minute(n)", + "grantPermission": "Berechtigung erteilen", + "test": "Test", + "showAllColorsIncluding": "Alle Farben anzeigen (auch nicht verfügbare)", + "chromaWeight": "Chroma-Gewicht", + "downloadPreview": "Vorschau herunterladen", + "apply": "Anwenden", + "cancel": "Abbrechen", + "fit": "Anpassen", + "hundred": "100%", + "clear": "Löschen", + "invert": "Invertieren", + "reprocessingOverlay": "Overlay wird neu verarbeitet...", + "overlayUpdated": "Overlay aktualisiert!", + "notificationsEnabled": "Benachrichtigungen aktiviert.", + "notificationsPermissionDenied": "Benachrichtigungsberechtigung verweigert.", + "overlayEnabled": "Overlay aktiviert.", + "overlayDisabled": "Overlay deaktiviert.", + "tokenSourceSet": "Token-Quelle auf {source} gesetzt", + "batchModeSet": "Batch-Modus auf {mode} gesetzt", + "randomRange": "Zufallsbereich", + "normalFixedSize": "Normale feste Größe", + "advancedColorSettingsReset": "Erweiterte Farbeinstellungen zurückgesetzt.", + "shiftRowAltColumn": "Shift = Zeile • Alt = Spalte", + "hideTurnstileBtn": "Ausblenden", + "turnstileInstructions": "Cloudflare Turnstile — bitte schließe die Überprüfung ab, falls angezeigt", + "uploadImageFirstColors": "Bitte lade zuerst ein Bild hoch, um verfügbare Farben zu erfassen", + "availableColors": "Verfügbare Farben ({count})", + "colorTooltip": "ID: {id}\nRGB: {rgb}", + "expandMode": "Erweiterungsmodus", + "minimize": "Minimieren", + "restore": "Wiederherstellen", + "hideStats": "Statistiken ausblenden", + "paintOptions": "Mal-Optionen", + "paintWhitePixels": "Weiße Pixel malen", + "paintWhitePixelsDescription": "Wenn aktiviert, werden weiße Template-Pixel gemalt.", + "paintTransparentPixels": "Transparente Pixel malen", + "paintTransparentPixelsDescription": "Wenn aktiviert, werden transparente Template-Pixel gemalt", + "paintUnavailablePixels": "Nicht verfügbare Pixel malen", + "paintUnavailablePixelsDescription": "Wenn aktiviert, werden nicht verfügbare Template-Farben mit der nächsten verfügbaren Farbe gemalt", + "colorsUpdated": "✅ Verfügbare Farben erhöht {oldCount} -> {newCount}, {diffCount} neue Farben gefunden", + "overlayTilesNotLoaded": "❌ Erforderliche Kartenkacheln nicht geladen. Verbindung prüfen oder erneut versuchen.", + "paintingBatchFailed": "❌ Batch-Senden nach mehreren Versuchen fehlgeschlagen. Malen gestoppt.", + "paintingPixelCheckFailed": "❌ Pixel bei ({x}, {y}) konnte nicht gelesen werden. Malen gestoppt.", + "paintingFinalBatchFailed": "⚠️ Finaler Batch von {count} Pixeln nach mehreren Versuchen fehlgeschlagen.", + "fullChargeIn": "Vollständige Aufladung in", + "batchSize": "Batch-Größe", + "speedOn": "An", + "speedOff": "Aus" } \ No newline at end of file diff --git a/Extension/lang/en.json b/Extension/lang/en.json index 24bc835..b254eff 100644 --- a/Extension/lang/en.json +++ b/Extension/lang/en.json @@ -60,6 +60,8 @@ "paintingSpeed": "Painting Speed", "pixelsPerSecond": "pixels/second", "speedSetting": "Speed: {speed} pixels/sec", + "batchSize": "Batch Size", + "keepAspectRatio": "Keep Aspect Ratio", "settings": "Settings", "botSettings": "Bot Settings", "close": "Close", diff --git a/Extension/lang/es.json b/Extension/lang/es.json new file mode 100644 index 0000000..38c9b54 --- /dev/null +++ b/Extension/lang/es.json @@ -0,0 +1,156 @@ +{ + "title": "WPlace Auto-Imagen", + "toggleOverlay": "Alternar superposición", + "scanColors": "Escanear colores", + "uploadImage": "Subir imagen", + "resizeImage": "Cambiar tamaño", + "selectPosition": "Seleccionar posición", + "startPainting": "Iniciar", + "stopPainting": "Detener", + "checkingColors": "🔍 Verificando colores disponibles...", + "noColorsFound": "❌ ¡Abre la paleta de colores en el sitio e inténtalo de nuevo!", + "colorsUpdated": "✅ Colores disponibles aumentados {oldCount} -> {newCount}, {diffCount} nuevos colores encontrados", + "colorsFound": "✅ Se encontraron {count} colores disponibles. Listo para subir.", + "loadingImage": "🖼️ Cargando imagen...", + "imageLoaded": "✅ Imagen cargada con {count} píxeles válidos", + "imageError": "❌ Error al cargar la imagen", + "selectPositionAlert": "¡Pinta el primer píxel en la ubicación donde quieres que comience el arte!", + "waitingPosition": "👆 Esperando que pintes el píxel de referencia...", + "positionSet": "✅ ¡Posición establecida con éxito!", + "positionTimeout": "❌ Tiempo de espera para seleccionar la posición agotado", + "startPaintingMsg": "🎨 Iniciando pintado...", + "paintingProgress": "🧱 Progreso: {painted}/{total} píxeles...", + "noCharges": "⌛ Sin cargas. Esperando {time}...", + "overlayTilesNotLoaded": "❌ Bloques de mapa necesarios no cargados. Verifica la conexión o intenta de nuevo.", + "paintingStoppedByUser": "⏹️ Pintado detenido por el usuario", + "paintingBatchFailed": "❌ Falló el envío de lote después de varios intentos. Pintado detenido.", + "paintingPixelCheckFailed": "❌ No se pudo leer el píxel en ({x}, {y}). Pintado detenido.", + "paintingFinalBatchFailed": "⚠️ Lote final de {count} píxeles falló después de varios intentos.", + "paintingComplete": "✅ ¡Pintado completo! Se pintaron {count} píxeles.", + "paintingError": "❌ Error durante el pintado", + "missingRequirements": "❌ Primero carga una imagen y selecciona una posición", + "progress": "Progreso", + "pixels": "Píxeles", + "charges": "Cargas", + "batchSize": "Tamaño de lote", + "fullChargeIn": "Carga completa en", + "estimatedTime": "Tiempo estimado", + "initMessage": "Haz clic en 'Subir imagen' para comenzar", + "waitingInit": "Esperando inicialización...", + "initializingToken": "🔧 Inicializando generador de token Turnstile...", + "tokenReady": "✅ ¡Generador de token listo! Ya puedes empezar a pintar.", + "tokenRetryLater": "⚠️ El generador de token volverá a intentarlo cuando sea necesario", + "resizeSuccess": "✅ Imagen redimensionada a {width}x{height}", + "paintingPaused": "⏸️ Pintado pausado en la posición X: {x}, Y: {y}", + "captchaNeeded": "❗ Falló la generación de token. Por favor, inténtalo de nuevo en un momento.", + "saveData": "Guardar progreso", + "loadData": "Cargar progreso", + "saveToFile": "Guardar en archivo", + "loadFromFile": "Cargar desde archivo", + "dataManager": "Gestor de datos", + "autoSaved": "✅ Progreso guardado automáticamente", + "dataLoaded": "✅ Progreso cargado con éxito", + "fileSaved": "✅ Progreso guardado en archivo con éxito", + "fileLoaded": "✅ Progreso cargado desde archivo con éxito", + "noSavedData": "❌ No se encontró progreso guardado", + "savedDataFound": "✅ ¡Se encontró progreso guardado! ¿Cargar para continuar?", + "savedDate": "Guardado el: {date}", + "clickLoadToContinue": "Haz clic en 'Cargar progreso' para continuar.", + "fileError": "❌ Error al procesar el archivo", + "invalidFileFormat": "❌ Formato de archivo inválido", + "paintingSpeed": "Velocidad de pintado", + "pixelsPerSecond": "píxeles/segundo", + "speedSetting": "Velocidad: {speed} píxeles/seg", + "keepAspectRatio": "Mantener proporción", + "settings": "Ajustes", + "botSettings": "Ajustes del bot", + "close": "Cerrar", + "language": "Idioma", + "themeSettings": "Ajustes de tema", + "themeSettingsDesc": "Elige tu tema de color preferido para la interfaz.", + "languageSelectDesc": "Selecciona tu idioma preferido. Los cambios se aplicarán inmediatamente.", + "autoCaptcha": "Solucionador automático de CAPTCHA (Turnstile)", + "autoCaptchaDesc": "Genera automáticamente tokens de Turnstile usando el generador integrado. Utiliza la automatización del navegador como alternativa si es necesario.", + "applySettings": "Aplicar ajustes", + "settingsSaved": "✅ ¡Ajustes guardados con éxito!", + "speedOn": "Activado", + "speedOff": "Desactivado", + "cooldownSettings": "Ajustes de tiempo de espera", + "waitCharges": "Esperar hasta que las cargas lleguen a", + "captchaSolving": "🔑 Generando token de Turnstile...", + "captchaFailed": "❌ Falló la generación de token de Turnstile. Probando método alternativo...", + "automation": "Automatización", + "noChargesThreshold": "⌛ Esperando que las cargas lleguen a {threshold}. Actual: {current}. Siguiente en {time}...", + "tokenCapturedSuccess": "¡Token capturado con éxito! Ya puedes iniciar el bot.", + "notificationsNotSupported": "Las notificaciones no son compatibles con este navegador.", + "chargesReadyNotification": "WPlace — Cargas listas", + "chargesReadyMessage": "Cargas listas: {current} / {max}. Umbral: {threshold}.", + "testNotificationTitle": "WPlace — Prueba", + "testNotificationMessage": "Esta es una notificación de prueba.", + "showStats": "Mostrar estadísticas", + "compactMode": "Modo compacto", + "refreshCharges": "Actualizar cargas", + "closeStats": "Cerrar estadísticas", + "zoomOut": "Alejar", + "zoomIn": "Acercar", + "fitToView": "Ajustar a la vista", + "actualSize": "Tamaño real (100%)", + "panMode": "Desplazamiento (arrastra para mover la vista)", + "clearIgnoredPixels": "Limpiar todos los píxeles ignorados", + "invertMask": "Invertir máscara", + "waitingSetupComplete": "🔄 Esperando que se complete la configuración inicial...", + "waitingTokenGenerator": "🔄 Esperando que se inicialice el generador de tokens...", + "uploadImageFirst": "Sube una imagen primero para capturar los colores disponibles", + "pleaseWaitInitialSetup": "🔄 Por favor, espera a que se complete la configuración inicial antes de cargar el progreso.", + "pleaseWaitFileSetup": "🔄 Por favor, espera a que se complete la configuración inicial antes de cargar desde un archivo.", + "errorSavingProgress": "❌ Error al guardar el progreso", + "errorLoadingProgress": "❌ Error al cargar el progreso", + "fileOperationsAvailable": "📂 ¡Las operaciones de archivo (Cargar/Subir) ya están disponibles!", + "tokenGeneratorReady": "🔑 ¡Generador de tokens listo!", + "paintingStats": "Estadísticas de pintado", + "enablePaintingSpeedLimit": "Activar límite de velocidad de pintado (control de lotes)", + "enableNotifications": "Activar notificaciones", + "notifyOnChargesThreshold": "Notificar cuando las cargas alcancen el umbral", + "onlyWhenNotFocused": "Solo cuando la pestaña no esté en foco", + "repeatEvery": "Repetir cada", + "minutesPl": "minuto(s)", + "grantPermission": "Otorgar permiso", + "test": "Probar", + "showAllColorsIncluding": "Mostrar todos los colores (incluidos los no disponibles)", + "chromaWeight": "Peso de croma", + "downloadPreview": "Descargar vista previa", + "apply": "Aplicar", + "cancel": "Cancelar", + "fit": "Ajustar", + "hundred": "100%", + "clear": "Limpiar", + "invert": "Invertir", + "reprocessingOverlay": "Reprocesando superposición...", + "overlayUpdated": "¡Superposición actualizada!", + "notificationsEnabled": "Notificaciones activadas.", + "notificationsPermissionDenied": "Permiso de notificaciones denegado.", + "overlayEnabled": "Superposición activada.", + "overlayDisabled": "Superposición desactivada.", + "tokenSourceSet": "Fuente de token establecida en: {source}", + "batchModeSet": "Modo por lotes establecido en: {mode}", + "randomRange": "Rango aleatorio", + "normalFixedSize": "Tamaño fijo normal", + "advancedColorSettingsReset": "Ajustes de color avanzados restablecidos.", + "shiftRowAltColumn": "Shift = Fila • Alt = Columna", + "hideTurnstileBtn": "Ocultar", + "turnstileInstructions": "Cloudflare Turnstile — por favor, completa la verificación si se muestra", + "uploadImageFirstColors": "Por favor, sube una imagen primero para capturar los colores disponibles", + "availableColors": "Colores disponibles ({count})", + "colorTooltip": "ID: {id}\nRGB: {rgb}", + "expandMode": "Modo expandido", + "minimize": "Minimizar", + "restore": "Restaurar", + "hideStats": "Ocultar estadísticas", + "paintOptions": "Opciones de pintado", + "paintWhitePixels": "Pintar píxeles blancos", + "paintWhitePixelsDescription": "Si está activado, se pintarán los píxeles blancos de la plantilla.", + "paintTransparentPixels": "Pintar píxeles transparentes", + "paintTransparentPixelsDescription": "Si está activado, se pintarán los píxeles transparentes de la plantilla", + "paintUnavailablePixels": "Pintar píxeles no disponibles", + "paintUnavailablePixelsDescription": "Si está activado, los colores de plantilla no disponibles se pintarán usando el color disponible más cercano" +} diff --git a/Extension/lang/fr.json b/Extension/lang/fr.json index af1cba1..f550f79 100644 --- a/Extension/lang/fr.json +++ b/Extension/lang/fr.json @@ -54,6 +54,7 @@ "paintingSpeed": "Vitesse de peinture", "pixelsPerSecond": "pixels/seconde", "speedSetting": "Vitesse: {speed} pixels/sec", + "keepAspectRatio": "Conserver le rapport d'aspect", "settings": "Paramètres", "botSettings": "Paramètres du Bot", "close": "Fermer", @@ -140,5 +141,18 @@ "hideStats": "Masquer les Stats", "paintOptions": "Options de peinture", "paintWhitePixels": "Peindre les pixels blancs", - "paintTransparentPixels": "Peindre les pixels transparents" + "paintWhitePixelsDescription": "Si activé, les pixels blancs du modèle seront peints.", + "paintTransparentPixels": "Peindre les pixels transparents", + "paintTransparentPixelsDescription": "Si activé, les pixels transparents du modèle seront peints", + "paintUnavailablePixels": "Peindre les pixels indisponibles", + "paintUnavailablePixelsDescription": "Si activé, les couleurs de modèle non disponibles seront peintes avec la couleur disponible la plus proche", + "colorsUpdated": "✅ Couleurs disponibles augmentées {oldCount} -> {newCount}, {diffCount} nouvelles couleurs trouvées", + "overlayTilesNotLoaded": "❌ Tuiles de carte requises non chargées. Vérifiez la connexion ou réessayez.", + "paintingBatchFailed": "❌ Échec de l'envoi du lot après plusieurs tentatives. Peinture arrêtée.", + "paintingPixelCheckFailed": "❌ Impossible de lire le pixel à ({x}, {y}). Peinture arrêtée.", + "paintingFinalBatchFailed": "⚠️ Lot final de {count} pixels échoué après plusieurs tentatives.", + "paintingStoppedByUser": "⏹️ Peinture arrêtée par l'utilisateur", + "fullChargeIn": "Pleine charge dans", + "batchSize": "Taille du lot" } + diff --git a/Extension/lang/id.json b/Extension/lang/id.json index bf09135..b2835ed 100644 --- a/Extension/lang/id.json +++ b/Extension/lang/id.json @@ -54,6 +54,7 @@ "paintingSpeed": "Kecepatan Melukis", "pixelsPerSecond": "piksel/detik", "speedSetting": "Kecepatan: {speed} piksel/detik", + "keepAspectRatio": "Pertahankan rasio aspek", "settings": "Pengaturan", "botSettings": "Pengaturan Bot", "close": "Tutup", @@ -140,5 +141,18 @@ "hideStats": "Sembunyikan Statistik", "paintOptions": "Opsi Pewarnaan", "paintWhitePixels": "Warnai piksel putih", - "paintTransparentPixels": "Warnai piksel transparan" + "paintWhitePixelsDescription": "Jika diaktifkan, piksel putih template akan dicat.", + "paintTransparentPixels": "Warnai piksel transparan", + "paintTransparentPixelsDescription": "Jika diaktifkan, piksel transparan template akan dicat", + "paintUnavailablePixels": "Cat piksel tidak tersedia", + "paintUnavailablePixelsDescription": "Jika diaktifkan, warna template yang tidak tersedia akan dicat dengan warna tersedia terdekat", + "colorsUpdated": "✅ Warna tersedia meningkat {oldCount} -> {newCount}, {diffCount} warna baru ditemukan", + "overlayTilesNotLoaded": "❌ Ubin peta yang diperlukan tidak dimuat. Periksa koneksi atau coba lagi.", + "paintingBatchFailed": "❌ Gagal mengirim batch setelah beberapa percobaan. Melukis dihentikan.", + "paintingPixelCheckFailed": "❌ Gagal membaca piksel di ({x}, {y}). Melukis dihentikan.", + "paintingFinalBatchFailed": "⚠️ Batch akhir {count} piksel gagal setelah beberapa percobaan.", + "paintingStoppedByUser": "⏹️ Melukis dihentikan oleh pengguna", + "fullChargeIn": "Muatan penuh dalam", + "batchSize": "Ukuran batch" } + diff --git a/Extension/lang/ja.json b/Extension/lang/ja.json index c95a308..48c0d61 100644 --- a/Extension/lang/ja.json +++ b/Extension/lang/ja.json @@ -54,6 +54,7 @@ "paintingSpeed": "描画速度", "pixelsPerSecond": "ピクセル/秒", "speedSetting": "速度: {speed} ピクセル/秒", + "keepAspectRatio": "アスペクト比を維持", "settings": "設定", "botSettings": "ボット設定", "close": "閉じる", @@ -140,5 +141,18 @@ "hideStats": "統計を非表示", "paintOptions": "描画オプション", "paintWhitePixels": "白いピクセルを描画", - "paintTransparentPixels": "透明ピクセルを描画" + "paintWhitePixelsDescription": "有効にすると、テンプレートの白いピクセルが描画されます。", + "paintTransparentPixels": "透明ピクセルを描画", + "paintTransparentPixelsDescription": "有効にすると、テンプレートの透明ピクセルが描画されます", + "paintUnavailablePixels": "利用不可のピクセルを描画", + "paintUnavailablePixelsDescription": "有効にすると、利用できないテンプレートの色は最も近い利用可能な色で描画されます", + "colorsUpdated": "✅ 利用可能な色が増加 {oldCount} -> {newCount}、{diffCount} 個の新しい色が見つかりました", + "overlayTilesNotLoaded": "❌ 必要なマップタイルがロードされていません。接続を確認するか、再試行してください。", + "paintingBatchFailed": "❌ 複数回の試行後、バッチ送信に失敗しました。描画を停止しました。", + "paintingPixelCheckFailed": "❌ ({x}, {y}) のピクセルを読み取れませんでした。描画を停止しました。", + "paintingFinalBatchFailed": "⚠️ {count} ピクセルの最終バッチが複数回の試行後に失敗しました。", + "paintingStoppedByUser": "⏹️ ユーザーにより停止されました", + "fullChargeIn": "フル充電まで", + "batchSize": "バッチサイズ" } + diff --git a/Extension/lang/ko.json b/Extension/lang/ko.json index f437b20..20b27b5 100644 --- a/Extension/lang/ko.json +++ b/Extension/lang/ko.json @@ -54,6 +54,7 @@ "paintingSpeed": "그리기 속도", "pixelsPerSecond": "픽셀/초", "speedSetting": "속도: {speed} 픽셀/초", + "keepAspectRatio": "종횡비 유지", "settings": "설정", "botSettings": "봇 설정", "close": "닫기", @@ -140,5 +141,18 @@ "hideStats": "통계 숨김", "paintOptions": "페인트 옵션", "paintWhitePixels": "흰색 픽셀 칠하기", - "paintTransparentPixels": "투명 픽셀 칠하기" + "paintWhitePixelsDescription": "활성화하면 템플릿 흰색 픽셀이 칠해집니다.", + "paintTransparentPixels": "투명 픽셀 칠하기", + "paintTransparentPixelsDescription": "활성화하면 템플릿 투명 픽셀이 칠해집니다", + "paintUnavailablePixels": "사용 불가능한 픽셀 칠하기", + "paintUnavailablePixelsDescription": "활성화하면 사용할 수 없는 템플릿 색상을 가장 가까운 사용 가능한 색상으로 칠합니다", + "colorsUpdated": "✅ 사용 가능한 색상 증가 {oldCount} -> {newCount}, {diffCount}개의 새 색상 발견", + "overlayTilesNotLoaded": "❌ 필요한 맵 타일이 로드되지 않았습니다. 연결을 확인하거나 다시 시도하세요.", + "paintingBatchFailed": "❌ 여러 번 시도 후 배치 전송 실패. 그리기가 중지되었습니다.", + "paintingPixelCheckFailed": "❌ ({x}, {y})의 픽셀을 읽을 수 없습니다. 그리기가 중지되었습니다.", + "paintingFinalBatchFailed": "⚠️ {count}개 픽셀의 최종 배치가 여러 번 시도 후 실패했습니다.", + "paintingStoppedByUser": "⏹️ 사용자에 의해 중지됨", + "fullChargeIn": "완전 충전까지", + "batchSize": "배치 크기" } + diff --git a/Extension/lang/pt.json b/Extension/lang/pt.json index f073d02..6f55df4 100644 --- a/Extension/lang/pt.json +++ b/Extension/lang/pt.json @@ -54,6 +54,7 @@ "paintingSpeed": "Velocidade de Pintura", "pixelsPerSecond": "pixels/segundo", "speedSetting": "Velocidade: {speed} pixels/seg", + "keepAspectRatio": "Manter proporção", "settings": "Configurações", "botSettings": "Configurações do Bot", "close": "Fechar", @@ -139,5 +140,17 @@ "hideStats": "Ocultar Estatísticas", "paintOptions": "Opções de pintura", "paintWhitePixels": "Pintar pixels brancos", - "paintTransparentPixels": "Pintar pixels transparentes" + "paintWhitePixelsDescription": "Se ativado, pixels brancos do modelo serão pintados.", + "paintTransparentPixels": "Pintar pixels transparentes", + "paintTransparentPixelsDescription": "Se ativado, pixels transparentes do modelo serão pintados", + "paintUnavailablePixels": "Pintar pixels indisponíveis", + "paintUnavailablePixelsDescription": "Se ativado, cores de modelo indisponíveis serão pintadas usando a cor disponível mais próxima", + "colorsUpdated": "✅ Cores disponíveis aumentadas {oldCount} -> {newCount}, {diffCount} novas cores encontradas", + "overlayTilesNotLoaded": "❌ Blocos de mapa necessários não carregados. Verifique a conexão ou tente novamente.", + "paintingBatchFailed": "❌ Falha ao enviar lote após várias tentativas. Pintura interrompida.", + "paintingPixelCheckFailed": "❌ Falha ao ler pixel em ({x}, {y}). Pintura interrompida.", + "paintingFinalBatchFailed": "⚠️ Lote final de {count} pixels falhou após várias tentativas.", + "paintingStoppedByUser": "⏹️ Pintura interrompida pelo usuário", + "fullChargeIn": "Carga completa em", + "batchSize": "Tamanho do lote" } \ No newline at end of file diff --git a/Extension/lang/ru.json b/Extension/lang/ru.json index 30fc553..8f58ce7 100644 --- a/Extension/lang/ru.json +++ b/Extension/lang/ru.json @@ -54,6 +54,7 @@ "paintingSpeed": "Скорость рисования", "pixelsPerSecond": "пикселей/сек", "speedSetting": "Скорость: {speed} пикс./сек", + "keepAspectRatio": "Сохранять пропорции", "settings": "Настройки", "botSettings": "Настройки бота", "close": "Закрыть", @@ -139,5 +140,17 @@ "hideStats": "Скрыть статистику", "paintOptions": "Параметры рисования", "paintWhitePixels": "Рисовать белые пиксели", - "paintTransparentPixels": "Рисовать прозрачные пиксели" + "paintWhitePixelsDescription": "Если включено, белые пиксели шаблона будут рисоваться.", + "paintTransparentPixels": "Рисовать прозрачные пиксели", + "paintTransparentPixelsDescription": "Если включено, прозрачные пиксели шаблона будут рисоваться", + "paintUnavailablePixels": "Рисовать недоступные пиксели", + "paintUnavailablePixelsDescription": "Если включено, недоступные цвета шаблона будут рисоваться ближайшим доступным цветом", + "colorsUpdated": "✅ Доступные цвета увеличены {oldCount} -> {newCount}, найдено {diffCount} новых цветов", + "overlayTilesNotLoaded": "❌ Необходимые тайлы карты не загружены. Проверьте соединение или попробуйте снова.", + "paintingBatchFailed": "❌ Не удалось отправить пакет после нескольких попыток. Рисование остановлено.", + "paintingPixelCheckFailed": "❌ Не удалось прочитать пиксель в ({x}, {y}). Рисование остановлено.", + "paintingFinalBatchFailed": "⚠️ Финальный пакет из {count} пикселей не удалось отправить после нескольких попыток.", + "paintingStoppedByUser": "⏹️ Рисование остановлено пользователем", + "fullChargeIn": "Полная зарядка через", + "batchSize": "Размер пакета" } \ No newline at end of file diff --git a/Extension/lang/tr.json b/Extension/lang/tr.json index 558b31f..b10a481 100644 --- a/Extension/lang/tr.json +++ b/Extension/lang/tr.json @@ -54,6 +54,7 @@ "paintingSpeed": "Boyama Hızı", "pixelsPerSecond": "piksel/saniye", "speedSetting": "Hız: {speed} piksel/sn", + "keepAspectRatio": "En-boy oranını koru", "settings": "Ayarlar", "botSettings": "Bot Ayarları", "close": "Kapat", @@ -140,5 +141,18 @@ "hideStats": "İstatistikleri Gizle", "paintOptions": "Boya Seçenekleri", "paintWhitePixels": "Beyaz pikselleri boya", - "paintTransparentPixels": "Şeffaf pikselleri boya" + "paintWhitePixelsDescription": "Etkinleştirilirse, şablon beyaz pikselleri boyanacaktır.", + "paintTransparentPixels": "Şeffaf pikselleri boya", + "paintTransparentPixelsDescription": "Etkinleştirilirse, şablon şeffaf pikselleri boyanacaktır", + "paintUnavailablePixels": "Kullanılamayan pikselleri boya", + "paintUnavailablePixelsDescription": "Etkinleştirilirse, kullanılamayan şablon renkleri en yakın uygun renkle boyanacaktır", + "colorsUpdated": "✅ Uygun renkler arttı {oldCount} -> {newCount}, {diffCount} yeni renk bulundu", + "overlayTilesNotLoaded": "❌ Gerekli harita karoları yüklenmedi. Bağlantıyı kontrol edin veya yeniden deneyin.", + "paintingBatchFailed": "❌ Toplu gönderim birkaç denemeden sonra başarısız oldu. Boyama durduruldu.", + "paintingPixelCheckFailed": "❌ ({x}, {y}) konumundaki piksel okunamadı. Boyama durduruldu.", + "paintingFinalBatchFailed": "⚠️ {count} pikselden oluşan son topluluk birkaç denemeden sonra başarısız oldu.", + "paintingStoppedByUser": "⏹️ Boyama kullanıcı tarafından durduruldu", + "fullChargeIn": "Tam dolum süresi", + "batchSize": "Topluluk boyutu" } + diff --git a/Extension/lang/uk.json b/Extension/lang/uk.json index 0a13c41..2474c2a 100644 --- a/Extension/lang/uk.json +++ b/Extension/lang/uk.json @@ -54,6 +54,7 @@ "paintingSpeed": "Швидкість малювання", "pixelsPerSecond": "пікселів/секунда", "speedSetting": "Швидкість: {speed} пікселів/сек", + "keepAspectRatio": "Зберігати співвідношення сторін", "settings": "Налаштування", "botSettings": "Налаштування бота", "close": "Закрити", @@ -140,5 +141,18 @@ "hideStats": "Приховати статистику", "paintOptions": "Параметри малювання", "paintWhitePixels": "Малювати білі пікселі", - "paintTransparentPixels": "Малювати прозорі пікселі" + "paintWhitePixelsDescription": "Якщо ввімкнено, білі пікселі шаблону будуть намальовані.", + "paintTransparentPixels": "Малювати прозорі пікселі", + "paintTransparentPixelsDescription": "Якщо ввімкнено, прозорі пікселі шаблону будуть намальовані", + "paintUnavailablePixels": "Малювати недоступні пікселі", + "paintUnavailablePixelsDescription": "Якщо ввімкнено, недоступні кольори шаблону будуть намальовані найближчим доступним кольором", + "colorsUpdated": "✅ Доступні кольори збільшено {oldCount} -> {newCount}, знайдено {diffCount} нових кольорів", + "overlayTilesNotLoaded": "❌ Необхідні тайли карти не завантажені. Перевір з'єднання або спробуй знову.", + "paintingBatchFailed": "❌ Не вдалося відправити пакет після кількох спроб. Малювання зупинено.", + "paintingPixelCheckFailed": "❌ Не вдалося прочитати піксель у ({x}, {y}). Малювання зупинено.", + "paintingFinalBatchFailed": "⚠️ Фінальний пакет з {count} пікселів не вдалося відправити після кількох спроб.", + "paintingStoppedByUser": "⏹️ Малювання зупинено користувачем", + "fullChargeIn": "Повний заряд через", + "batchSize": "Розмір пакета" } + diff --git a/Extension/lang/vi.json b/Extension/lang/vi.json index 5cbde24..a29472a 100644 --- a/Extension/lang/vi.json +++ b/Extension/lang/vi.json @@ -54,6 +54,7 @@ "paintingSpeed": "Tốc độ vẽ", "pixelsPerSecond": "pixel/giây", "speedSetting": "Tốc độ: {speed} pixel/giây", + "keepAspectRatio": "Giữ tỷ lệ khung hình", "settings": "Cài đặt", "botSettings": "Cài đặt Bot", "close": "Đóng", @@ -140,5 +141,18 @@ "hideStats": "Ẩn thống kê", "paintOptions": "Tùy chọn vẽ", "paintWhitePixels": "Vẽ điểm ảnh trắng", - "paintTransparentPixels": "Vẽ điểm ảnh trong suốt" + "paintWhitePixelsDescription": "Nếu được bật, các pixel trắng của mẫu sẽ được vẽ.", + "paintTransparentPixels": "Vẽ điểm ảnh trong suốt", + "paintTransparentPixelsDescription": "Nếu được bật, các pixel trong suốt của mẫu sẽ được vẽ", + "paintUnavailablePixels": "Vẽ pixel không khả dụng", + "paintUnavailablePixelsDescription": "Nếu được bật, màu mẫu không khả dụng sẽ được vẽ bằng màu khả dụng gần nhất", + "colorsUpdated": "✅ Màu khả dụng tăng {oldCount} -> {newCount}, tìm thấy {diffCount} màu mới", + "overlayTilesNotLoaded": "❌ Ô bản đồ cần thiết chưa được tải. Kiểm tra kết nối hoặc thử lại.", + "paintingBatchFailed": "❌ Gửi lô thất bại sau nhiều lần thử. Dừng vẽ.", + "paintingPixelCheckFailed": "❌ Không thể đọc pixel tại ({x}, {y}). Dừng vẽ.", + "paintingFinalBatchFailed": "⚠️ Lô cuối cùng gồm {count} pixel thất bại sau nhiều lần thử.", + "paintingStoppedByUser": "⏹️ Người dùng đã dừng vẽ", + "fullChargeIn": "Sạc đầy trong", + "batchSize": "Kích thước lô" } + diff --git a/Extension/lang/zh-CN.json b/Extension/lang/zh-CN.json index 948bb8c..e3e1ce8 100644 --- a/Extension/lang/zh-CN.json +++ b/Extension/lang/zh-CN.json @@ -54,6 +54,7 @@ "paintingSpeed": "绘制速度", "pixelsPerSecond": "像素/秒", "speedSetting": "速度: {speed} 像素/秒", + "keepAspectRatio": "保持纵横比", "settings": "设置", "botSettings": "机器人设置", "close": "关闭", @@ -140,5 +141,18 @@ "hideStats": "隐藏统计", "paintOptions": "绘图选项", "paintWhitePixels": "绘制白色像素", - "paintTransparentPixels": "绘制透明像素" + "paintWhitePixelsDescription": "如果启用,将绘制模板白色像素。", + "paintTransparentPixels": "绘制透明像素", + "paintTransparentPixelsDescription": "如果启用,将绘制模板透明像素", + "paintUnavailablePixels": "绘制不可用像素", + "paintUnavailablePixelsDescription": "如果启用,不可用的模板颜色将使用最接近的可用颜色绘制", + "colorsUpdated": "✅ 可用颜色增加 {oldCount} -> {newCount},找到 {diffCount} 个新颜色", + "overlayTilesNotLoaded": "❌ 所需的地图图块未加载。检查连接或重试。", + "paintingBatchFailed": "❌ 多次尝试后批量发送失败。绘制已停止。", + "paintingPixelCheckFailed": "❌ 无法读取 ({x}, {y}) 处的像素。绘制已停止。", + "paintingFinalBatchFailed": "⚠️ {count} 个像素的最终批次在多次尝试后失败。", + "paintingStoppedByUser": "⏹️ 已被用户停止", + "fullChargeIn": "完全充电时间", + "batchSize": "批次大小" } + diff --git a/Extension/lang/zh-TW.json b/Extension/lang/zh-TW.json index 681f011..b79d60b 100644 --- a/Extension/lang/zh-TW.json +++ b/Extension/lang/zh-TW.json @@ -54,6 +54,7 @@ "paintingSpeed": "繪製速度", "pixelsPerSecond": "像素/秒", "speedSetting": "速度: {speed} 像素/秒", + "keepAspectRatio": "保持縱橫比", "settings": "設定", "botSettings": "機器人設定", "close": "關閉", @@ -140,5 +141,18 @@ "hideStats": "隱藏統計", "paintOptions": "繪圖選項", "paintWhitePixels": "繪製白色像素", - "paintTransparentPixels": "繪製透明像素" + "paintWhitePixelsDescription": "如果啟用,將繪製範本白色像素。", + "paintTransparentPixels": "繪製透明像素", + "paintTransparentPixelsDescription": "如果啟用,將繪製範本透明像素", + "paintUnavailablePixels": "繪製不可用像素", + "paintUnavailablePixelsDescription": "如果啟用,不可用的範本顏色將使用最接近的可用顏色繪製", + "colorsUpdated": "✅ 可用顏色增加 {oldCount} -> {newCount},找到 {diffCount} 個新顏色", + "overlayTilesNotLoaded": "❌ 所需的地圖圖塊未載入。檢查連線或重試。", + "paintingBatchFailed": "❌ 多次嘗試後批次傳送失敗。繪製已停止。", + "paintingPixelCheckFailed": "❌ 無法讀取 ({x}, {y}) 處的像素。繪製已停止。", + "paintingFinalBatchFailed": "⚠️ {count} 個像素的最終批次在多次嘗試後失敗。", + "paintingStoppedByUser": "⏹️ 已被使用者停止", + "fullChargeIn": "完全充電時間", + "batchSize": "批次大小" } + diff --git a/Extension/manifest.json b/Extension/manifest.json index ab998e0..3d9a0f4 100644 --- a/Extension/manifest.json +++ b/Extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "WPlace AutoBOT Script Launcher", - "version": "2.0.5", + "version": "2.0.6", "description": "Launch and manage automation scripts for WPlace", "icons": { "16": "icons/icon16.png", diff --git a/Extension/scripts/Auto-Farm.js b/Extension/scripts/Auto-Farm.js index ee1e5db..82c8bfb 100644 --- a/Extension/scripts/Auto-Farm.js +++ b/Extension/scripts/Auto-Farm.js @@ -1,10 +1,31 @@ -// eslint-disable-next-line prettier/prettier +// ==UserScript== +// @name WPlace Auto-Farm Enhanced +// @namespace http://tampermonkey.net/ +// @version 2025-01-10 +// @description Enhanced Auto-Farm with cooldown system and canvas interaction +// @author Wbot +// @match https://wplace.live/* +// @grant none +// @icon 🌾 +// ==/UserScript== + ; (async () => { + // Prevent multiple instances + if (window.WPLACE_AUTO_FARM_ENHANCED_LOADED) { + console.warn('⚠️ Auto-Farm Enhanced already loaded'); + return; + } + window.WPLACE_AUTO_FARM_ENHANCED_LOADED = true; + + console.log('%c🌾 WPlace Auto-Farm Enhanced Starting...', 'color: #00ff41; font-weight: bold; font-size: 16px;'); + + // CONFIGURATION const CONFIG = { - START_X: 742, - START_Y: 1148, - PIXELS_PER_LINE: 100, - DELAY: 1000, + COOLDOWN_DEFAULT: 31000, + COOLDOWN_CHARGE_THRESHOLD: 10, // Wait until charges reach this amount + DELAY_BETWEEN_PIXELS: 50, // ms delay between placing pixels + DELAY_AFTER_BATCH: 2000, // ms delay after confirming batch (reopen delay) + CANVAS_SIZE: 1000, // Canvas tile size THEME: { primary: '#000000', secondary: '#111111', @@ -13,23 +34,27 @@ highlight: '#775ce3', success: '#00ff00', error: '#ff0000', + warning: '#ffaa00', }, }; + // STATE const state = { running: false, paintedCount: 0, + sessionPixels: 0, charges: { count: 0, max: 80, cooldownMs: 30000 }, userInfo: null, - lastPixel: null, minimized: false, - menuOpen: false, language: 'en', - autoRefresh: true, - pausedForManual: false, + cooldownChargeThreshold: CONFIG.COOLDOWN_CHARGE_THRESHOLD, + lastBatchTime: 0, + totalBatches: 0, }; + // UTILITIES 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) { @@ -40,30 +65,17 @@ return null; }; - const originalFetch = window.fetch; - 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 (payload.t) { - console.log('✅ CAPTCHA Token Captured:', payload.t); - capturedCaptchaToken = payload.t; - if (state.pausedForManual) { - state.pausedForManual = false; - state.running = true; - updateUI( - state.language === 'pt' ? '🚀 Pintura reiniciada!' : '🚀 Farm resumed!', - 'success' - ); - paintLoop(); - } - } - } catch (e) {} - } - return originalFetch(url, options); + const msToTimeText = (ms) => { + const totalSec = Math.ceil(ms / 1000); + const h = Math.floor(totalSec / 3600); + const m = Math.floor((totalSec % 3600) / 60); + const s = totalSec % 60; + if (h > 0) return `${h}h ${m}m ${s}s`; + if (m > 0) return `${m}m ${s}s`; + return `${s}s`; }; + // API FUNCTIONS const fetchAPI = async (url, options = {}) => { try { const res = await fetch(url, { @@ -72,44 +84,12 @@ }); return await res.json(); } catch (e) { + console.error('API fetch error:', e); return null; } }; - 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; - } - }; - - const getCharge = async () => { + const getCharges = async () => { const data = await fetchAPI('https://backend.wplace.live/me'); if (data) { state.userInfo = data; @@ -125,14 +105,12 @@ return state.charges; }; - const detectUserLocation = async () => { + const detectLanguage = 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'; } @@ -141,151 +119,277 @@ } }; - const paintLoop = async () => { + // CANVAS INTERACTION FUNCTIONS + const getRandomCanvasPosition = () => { + // Generate random position within canvas bounds + const canvas = document.querySelector('canvas'); + if (!canvas) return { x: 500, y: 500 }; + + const rect = canvas.getBoundingClientRect(); + return { + x: Math.floor(Math.random() * rect.width), + y: Math.floor(Math.random() * rect.height), + }; + }; + + const clickColorPaletteButton = async () => { + updateUI( + state.language === 'pt' ? '🎨 Abrindo paleta de cores...' : '🎨 Opening color palette...', + 'status' + ); + + // Find the "Paint" Button + const allButtons = Array.from(document.querySelectorAll('button')); + const paletteBtn = allButtons.find(b => /(Pintar|Paint)/i.test(b.textContent.trim())); + + if (paletteBtn) { + console.log('✅ Found palette button:', paletteBtn.textContent); + paletteBtn.click(); + await sleep(500); + return true; + } + + console.warn('⚠️ Paint button not found'); + return false; + }; + + const selectTransparentColor = async () => { + updateUI( + state.language === 'pt' ? '🔍 Selecionando cor transparente...' : '🔍 Selecting transparent color...', + 'status' + ); + + // Wait for color palette to appear + await sleep(300); + + // Try to find transparent color button (ID 0) + const transBtn = await waitForSelector('button#color-0', 100, 2000); + if (transBtn) { + console.log('✅ Found transparent color button'); + transBtn.click(); + await sleep(300); + return true; + } + + console.warn('⚠️ Transparent color button not found'); + return false; + }; + + const placePixelOnCanvas = async (x, y) => { + const canvas = await waitForSelector('canvas', 100, 2000); + if (!canvas) { + console.warn('⚠️ Canvas not found'); + return false; + } + + // Get canvas position + const rect = canvas.getBoundingClientRect(); + const clientX = Math.round(rect.left + x); + const clientY = Math.round(rect.top + y); + + // Simulate mouse/pointer events + const commonPointer = { + clientX: clientX, + clientY: clientY, + bubbles: true, + cancelable: true, + pointerId: 1, + isPrimary: true, + button: 0, + buttons: 1, + }; + + const commonMouse = { + clientX: clientX, + clientY: clientY, + bubbles: true, + cancelable: true, + button: 0, + buttons: 1, + }; + + + canvas.dispatchEvent(new PointerEvent('pointerdown', commonPointer)); + canvas.dispatchEvent(new MouseEvent('mousedown', commonMouse)); + canvas.dispatchEvent(new PointerEvent('pointerup', commonPointer)); + canvas.dispatchEvent(new MouseEvent('mouseup', commonMouse)); + canvas.dispatchEvent(new MouseEvent('click', commonMouse)); + + return true; + }; + + const confirmPaintBatch = async () => { + updateUI( + state.language === 'pt' ? '✅ Confirmando lote...' : '✅ Confirming batch...', + 'status' + ); + + await sleep(500); + + // Find the "Paint" or "Pintar" button + const allButtons = Array.from(document.querySelectorAll('button')); + const confirmBtn = allButtons.find(b => /(Pintar|Paint)/i.test(b.textContent.trim())); + + if (confirmBtn) { + console.log('✅ Found confirm button:', confirmBtn.textContent); + confirmBtn.click(); + await sleep(CONFIG.DELAY_AFTER_BATCH); // Wait for reopen + return true; + } + + console.warn('⚠️ Confirm button not found'); + return false; + }; + + // MAIN FARMING LOOP + const farmingLoop = async () => { while (state.running) { - const { count, cooldownMs } = state.charges; + try { + await getCharges(); + updateStats(); - if (count < 1) { - 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 { count, cooldownMs } = state.charges; - 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; - } + // Check if we have enough charges + if (count < state.cooldownChargeThreshold) { + const waitTime = cooldownMs || CONFIG.COOLDOWN_DEFAULT; updateUI( state.language === 'pt' - ? '❌ Token expirado. Aguardando elemento Paint...' - : '❌ CAPTCHA token expired. Waiting for Paint button...', + ? `⌛ Aguardando cargas (${count}/${state.cooldownChargeThreshold}). Próximo em ${msToTimeText(waitTime)}` + : `⌛ Waiting for charges (${count}/${state.cooldownChargeThreshold}). Next in ${msToTimeText(waitTime)}`, + 'status' + ); + + // Wait and recheck periodically + await sleep(Math.min(waitTime, 10000)); + continue; + } + + // We have enough charges, start farming + updateUI( + state.language === 'pt' + ? `🚀 Iniciando farming com ${count} cargas disponíveis` + : `🚀 Starting farming with ${count} charges available`, + 'success' + ); + + // Step 1: Click color palette button + const paletteOpened = await clickColorPaletteButton(); + if (!paletteOpened) { + updateUI( + state.language === 'pt' + ? '❌ Falha ao abrir paleta. Tentando novamente...' + : '❌ Failed to open palette. Retrying...', '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(2000); + continue; } - await sleep(1000); - continue; + + // Step 2: Select transparent color + const colorSelected = await selectTransparentColor(); + if (!colorSelected) { + updateUI( + state.language === 'pt' + ? '❌ Falha ao selecionar cor. Tentando novamente...' + : '❌ Failed to select color. Retrying...', + 'error' + ); + await sleep(2000); + continue; + } + + // Step 3: Place ALL available pixels randomly on canvas FIRST + const pixelsToPlace = count; // Use ALL available charges + updateUI( + state.language === 'pt' + ? `🎨 Colocando ${pixelsToPlace} pixels...` + : `🎨 Placing ${pixelsToPlace} pixels...`, + 'status' + ); + + let placedCount = 0; + for (let i = 0; i < pixelsToPlace; i++) { + if (!state.running) break; + + const pos = getRandomCanvasPosition(); + const placed = await placePixelOnCanvas(pos.x, pos.y); + + if (placed) { + placedCount++; + + // Update UI every 5 pixels + if (placedCount % 5 === 0) { + updateUI( + state.language === 'pt' + ? `🎨 Colocado ${placedCount}/${pixelsToPlace} pixels...` + : `🎨 Placed ${placedCount}/${pixelsToPlace} pixels...`, + 'status' + ); + } + } + + await sleep(CONFIG.DELAY_BETWEEN_PIXELS); + } + + // Step 4: NOW confirm the ENTIRE batch at once (clicks Paint button to submit ALL pixels) + updateUI( + state.language === 'pt' + ? `✅ Confirmando ${placedCount} pixels...` + : `✅ Confirming ${placedCount} pixels...`, + 'status' + ); + + const confirmed = await confirmPaintBatch(); + if (confirmed) { + state.totalBatches++; + state.paintedCount += placedCount; + state.sessionPixels += placedCount; + state.lastBatchTime = Date.now(); + + updateUI( + state.language === 'pt' + ? `✅ Lote confirmado! ${placedCount} pixels pintados` + : `✅ Batch confirmed! ${placedCount} pixels painted`, + 'success' + ); + + // Visual feedback + const effect = document.getElementById('paintEffect'); + if (effect) { + effect.style.animation = 'pulse 0.5s'; + setTimeout(() => { + effect.style.animation = ''; + }, 500); + } + } else { + updateUI( + state.language === 'pt' + ? '❌ Falha ao confirmar lote' + : '❌ Failed to confirm batch', + 'error' + ); + } + + // Update charges after batch + await getCharges(); + updateStats(); + + } catch (error) { + console.error('❌ Error in farming loop:', error); + updateUI( + state.language === 'pt' + ? `❌ Erro: ${error.message}` + : `❌ Error: ${error.message}`, + 'error' + ); + await sleep(5000); } - - 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(); } }; + // UI FUNCTIONS const createUI = () => { - if (state.menuOpen) return; - state.menuOpen = true; - 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'; @@ -294,7 +398,7 @@ const style = document.createElement('style'); style.textContent = ` @keyframes pulse { - 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); }n + 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); } } @@ -302,24 +406,24 @@ from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } - .wplace-bot-panel { + .wplace-farm-panel { position: fixed; top: 20px; right: 20px; - width: 250px; + width: 280px; background: ${CONFIG.THEME.primary}; border: 1px solid ${CONFIG.THEME.accent}; - border-radius: 8px; + border-radius: 12px; padding: 0; - box-shadow: 0 5px 15px rgba(0,0,0,0.5); + box-shadow: 0 8px 32px rgba(0,0,0,0.6); z-index: 9999; font-family: 'Segoe UI', Roboto, sans-serif; color: ${CONFIG.THEME.text}; animation: slide-in 0.4s ease-out; overflow: hidden; } - .wplace-header { - padding: 12px 15px; + .wplace-farm-header { + padding: 15px; background: ${CONFIG.THEME.secondary}; color: ${CONFIG.THEME.highlight}; font-size: 16px; @@ -330,12 +434,12 @@ cursor: move; user-select: none; } - .wplace-header-title { + .wplace-farm-title { display: flex; align-items: center; gap: 8px; } - .wplace-header-controls { + .wplace-farm-controls { display: flex; gap: 10px; } @@ -346,24 +450,20 @@ cursor: pointer; opacity: 0.7; transition: opacity 0.2s; + font-size: 14px; } .wplace-header-btn:hover { opacity: 1; } - .wplace-content { + .wplace-farm-content { padding: 15px; display: ${state.minimized ? 'none' : 'block'}; } - .wplace-controls { - display: flex; - gap: 10px; - margin-bottom: 15px; - } - .wplace-btn { - flex: 1; - padding: 10px; + .wplace-farm-btn { + width: 100%; + padding: 12px; border: none; - border-radius: 6px; + border-radius: 8px; font-weight: 600; cursor: pointer; display: flex; @@ -371,23 +471,61 @@ justify-content: center; gap: 8px; transition: all 0.2s; + margin-bottom: 12px; } - .wplace-btn:hover { + .wplace-farm-btn:hover { transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0,0,0,0.3); } - .wplace-btn-primary { - background: ${CONFIG.THEME.accent}; + .wplace-farm-btn-start { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); color: white; } - .wplace-btn-stop { + .wplace-farm-btn-stop { background: ${CONFIG.THEME.error}; color: white; } - .wplace-stats { + .wplace-farm-setting { background: ${CONFIG.THEME.secondary}; padding: 12px; - border-radius: 6px; - margin-bottom: 15px; + border-radius: 8px; + margin-bottom: 12px; + } + .wplace-farm-setting-label { + font-size: 13px; + opacity: 0.8; + margin-bottom: 8px; + display: flex; + align-items: center; + gap: 6px; + } + .wplace-farm-slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: ${CONFIG.THEME.accent}; + outline: none; + -webkit-appearance: none; + } + .wplace-farm-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: ${CONFIG.THEME.highlight}; + cursor: pointer; + } + .wplace-farm-slider-value { + display: flex; + justify-content: space-between; + font-size: 12px; + margin-top: 4px; + } + .wplace-farm-stats { + background: ${CONFIG.THEME.secondary}; + padding: 12px; + border-radius: 8px; + margin-bottom: 12px; } .wplace-stat-item { display: flex; @@ -401,11 +539,15 @@ gap: 6px; opacity: 0.8; } - .wplace-status { - padding: 8px; - border-radius: 4px; + .wplace-stat-value { + font-weight: 600; + } + .wplace-farm-status { + padding: 10px; + border-radius: 6px; text-align: center; font-size: 13px; + line-height: 1.4; } .status-default { background: rgba(255,255,255,0.1); @@ -418,6 +560,10 @@ background: rgba(255, 0, 0, 0.1); color: ${CONFIG.THEME.error}; } + .status-status { + background: rgba(119, 92, 227, 0.1); + color: ${CONFIG.THEME.highlight}; + } #paintEffect { position: absolute; top: 0; @@ -425,72 +571,85 @@ width: 100%; height: 100%; pointer-events: none; - border-radius: 8px; + border-radius: 12px; } `; document.head.appendChild(style); const translations = { pt: { - title: 'WPlace Auto-Farm', - start: 'Iniciar', - stop: 'Parar', - ready: 'Pronto para começar', + title: 'WPlace Auto-Farm 🌾', + start: 'Iniciar Farm', + stop: 'Parar Farm', + ready: 'Pronto para iniciar', user: 'Usuário', - pixels: 'Pixels', + sessionPixels: 'Pixels (Sessão)', + totalPixels: 'Total Pintado', charges: 'Cargas', - level: 'Level', + batches: 'Lotes', + threshold: 'Limite de Cargas', + thresholdDesc: 'Aguardar até ter esta quantidade de cargas', }, en: { - title: 'WPlace Auto-Farm', - start: 'Start', - stop: 'Stop', + title: 'WPlace Auto-Farm 🌾', + start: 'Start Farm', + stop: 'Stop Farm', ready: 'Ready to start', user: 'User', - pixels: 'Pixels', + sessionPixels: 'Pixels (Session)', + totalPixels: 'Total Painted', charges: 'Charges', - level: 'Level', + batches: 'Batches', + threshold: 'Charge Threshold', + thresholdDesc: 'Wait until charges reach this amount', }, }; const t = translations[state.language] || translations.en; const panel = document.createElement('div'); - panel.className = 'wplace-bot-panel'; + panel.className = 'wplace-farm-panel'; panel.innerHTML = `
-