diff --git a/Auto-Image.js b/Auto-Image.js index 3b12d07..fde2f4f 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -6,12 +6,13 @@ WHITE_THRESHOLD: 250, LOG_INTERVAL: 10, PAINTING_SPEED: { - MIN: 1, // Minimum 1 pixel per second - MAX: 1000, // Maximum 1000 pixels per second - DEFAULT: 5, // Default 5 pixels per second + MIN: 1, // Minimum 1 pixel batch size + MAX: 1000, // Maximum 1000 pixels batch size + DEFAULT: 5, // Default 5 pixels batch size }, - PAINTING_SPEED_ENABLED: false, + PAINTING_SPEED_ENABLED: false, // Off by default AUTO_CAPTCHA_ENABLED: true, // Turnstile generator enabled by default + TOKEN_SOURCE: "generator", // "generator", "manual", or "hybrid" - default to generator COOLDOWN_CHARGE_THRESHOLD: 1, // Default wait threshold OVERLAY: { OPACITY_DEFAULT: 0.6, @@ -1050,8 +1051,9 @@ lastPosition: { x: 0, y: 0 }, estimatedTime: 0, language: "en", - paintingSpeed: CONFIG.PAINTING_SPEED.DEFAULT, // pixels per second + paintingSpeed: CONFIG.PAINTING_SPEED.DEFAULT, // pixels batch size cooldownChargeThreshold: CONFIG.COOLDOWN_CHARGE_THRESHOLD, + tokenSource: CONFIG.TOKEN_SOURCE, // "generator" or "manual" overlayOpacity: CONFIG.OVERLAY.OPACITY_DEFAULT, blueMarbleEnabled: CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT, ditheringEnabled: true, @@ -1635,7 +1637,7 @@ }); }, - // Create or reuse the turnstile container + // Create or reuse the turnstile container - completely hidden for token generation ensureTurnstileContainer() { if (!this._turnstileContainer || !document.body.contains(this._turnstileContainer)) { // Clean up old container if it exists @@ -1646,13 +1648,15 @@ this._turnstileContainer = document.createElement('div'); this._turnstileContainer.style.cssText = ` position: fixed !important; - left: -9999px !important; /* keep off-screen for invisible mode */ - top: -9999px !important; - width: 300px !important; - height: 65px !important; + left: -99999px !important; + top: -99999px !important; + width: 1px !important; + height: 1px !important; pointer-events: none !important; - opacity: 0 !important; /* do not use visibility:hidden to avoid engine quirks */ - z-index: -1 !important; + opacity: 0 !important; + visibility: hidden !important; + z-index: -99999 !important; + overflow: hidden !important; `; this._turnstileContainer.setAttribute('aria-hidden', 'true'); this._turnstileContainer.id = 'turnstile-widget-container'; @@ -1661,43 +1665,11 @@ return this._turnstileContainer; }, + // No interactive overlay container needed - pure background operation ensureTurnstileOverlayContainer() { - if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { - return this._turnstileOverlay; - } - const overlay = document.createElement('div'); - overlay.id = 'turnstile-overlay-container'; - overlay.style.cssText = ` - position: fixed; - right: 16px; - bottom: 16px; - width: 320px; - min-height: 80px; - background: rgba(0,0,0,0.7); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 10px; - padding: 12px; - z-index: 100000; - backdrop-filter: blur(6px); - color: #fff; - box-shadow: 0 8px 24px rgba(0,0,0,0.4); - `; - const title = document.createElement('div'); - title.textContent = 'Cloudflare Turnstile — please complete the check if shown'; - title.style.cssText = 'font: 600 12px/1.3 \"Segoe UI\",sans-serif; margin-bottom: 8px; opacity: 0.9;'; - const widgetHost = document.createElement('div'); - widgetHost.id = 'turnstile-overlay-host'; - widgetHost.style.cssText = 'width: 100%; min-height: 70px;'; - const closeBtn = document.createElement('button'); - closeBtn.textContent = 'Hide'; - closeBtn.style.cssText = 'position:absolute; top:6px; right:6px; font-size:11px; background:transparent; color:#fff; border:1px solid rgba(255,255,255,0.2); border-radius:6px; padding:2px 6px; cursor:pointer;'; - closeBtn.addEventListener('click', () => overlay.remove()); - overlay.appendChild(title); - overlay.appendChild(widgetHost); - overlay.appendChild(closeBtn); - document.body.appendChild(overlay); - this._turnstileOverlay = overlay; - return overlay; + // Return null since we don't want any visible widgets + console.log('🚫 Interactive Turnstile overlay disabled - background mode only'); + return null; }, async executeTurnstile(sitekey, action = 'paint') { @@ -1734,85 +1706,65 @@ } const container = this.ensureTurnstileContainer(); container.innerHTML = ''; + + // Set a timeout to resolve with null if no token is received + const timeout = setTimeout(() => { + console.warn('⏰ Turnstile widget timeout - no token received'); + resolve(null); + }, 15000); + const widgetId = window.turnstile.render(container, { sitekey, action, - size: 'invisible', + size: 'compact', retry: 'auto', 'retry-interval': 8000, callback: (token) => { - console.log('✅ Invisible Turnstile callback'); + clearTimeout(timeout); + console.log('✅ Turnstile token received from hidden widget:', token ? 'Valid token' : 'Invalid token'); resolve(token); }, - 'error-callback': () => resolve(null), - 'timeout-callback': () => resolve(null), + 'error-callback': (error) => { + clearTimeout(timeout); + console.warn('❌ Turnstile error callback:', error); + resolve(null); + }, + 'timeout-callback': () => { + clearTimeout(timeout); + console.warn('⏰ Turnstile timeout callback'); + resolve(null); + }, }); + this._turnstileWidgetId = widgetId; this._lastSitekey = sitekey; - if (!widgetId) return resolve(null); - Promise.race([ - window.turnstile.execute(widgetId, { action }), - new Promise((_, reject) => setTimeout(() => reject(new Error('Invisible execute timeout')), 12000)) - ]).then(resolve).catch(() => resolve(null)); + + if (!widgetId) { + clearTimeout(timeout); + console.warn('❌ Failed to create Turnstile widget'); + resolve(null); + } } catch (e) { - console.warn('Invisible Turnstile failed:', e); + console.warn('❌ Invisible Turnstile failed:', e); resolve(null); } }); }, async createNewTurnstileWidgetInteractive(sitekey, action) { - return new Promise((resolve, reject) => { - try { - if (this._turnstileWidgetId && window.turnstile?.remove) { - try { window.turnstile.remove(this._turnstileWidgetId); } catch {} - } - - const overlay = this.ensureTurnstileOverlayContainer(); - const host = overlay.querySelector('#turnstile-overlay-host'); - host.innerHTML = ''; - - const timeoutId = setTimeout(() => { - console.warn('⏰ Interactive Turnstile timed out'); - resolve(null); - }, 120000); // give users up to 2 minutes - - const widgetId = window.turnstile.render(host, { - sitekey, - action, - size: 'normal', - retry: 'auto', - 'retry-interval': 8000, - callback: (token) => { - clearTimeout(timeoutId); - // Hide overlay after success - try { overlay.remove(); } catch {} - console.log('✅ Interactive Turnstile solved'); - resolve(token); - }, - 'error-callback': (error) => { - console.warn('🚨 Interactive Turnstile error:', error); - }, - 'timeout-callback': () => { - console.warn('⏰ Turnstile timeout callback (interactive)'); - }, - 'expired-callback': () => { - console.warn('⚠️ Interactive Turnstile token expired'); - } - }); - - this._turnstileWidgetId = widgetId; - this._lastSitekey = sitekey; - if (!widgetId) { - clearTimeout(timeoutId); - resolve(null); - return; - } - } catch (error) { - console.error('❌ Error creating interactive Turnstile widget:', error); - reject(error); - } - }); + // Since we want purely background token generation, attempt invisible again + // or fallback to manual pixel placement automation + console.log('🔄 Interactive widget requested - falling back to invisible or manual automation'); + + // Try invisible one more time + const invisibleToken = await this.createNewTurnstileWidgetInvisible(sitekey, action); + if (invisibleToken && invisibleToken.length > 20) { + return invisibleToken; + } + + // If invisible fails, return null to trigger manual automation fallback + console.log('⚠️ All Turnstile methods exhausted, manual fallback will be used'); + return null; }, async generatePaintToken(sitekey) { @@ -1832,13 +1784,10 @@ if (this._turnstileContainer && document.body.contains(this._turnstileContainer)) { this._turnstileContainer.remove(); } - if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) { - this._turnstileOverlay.remove(); - } this._turnstileWidgetId = null; this._turnstileContainer = null; - this._turnstileOverlay = null; + this._turnstileOverlay = null; // Keep for compatibility but won't exist this._lastSitekey = null; }, @@ -2808,29 +2757,44 @@ } async function handleCaptcha() { const startTime = performance.now(); + + // Check user's token source preference + if (state.tokenSource === "manual") { + console.log("🎯 Manual token source selected - using pixel placement automation"); + return await handleCaptchaFallback(); + } + + // Generator mode (pure) or Hybrid mode - try generator first try { // Use optimized token generation with automatic sitekey detection const sitekey = Utils.detectSitekey(); console.log("🔑 Generating Turnstile token for sitekey:", sitekey); - console.log('🧭 UA:', navigator.userAgent, 'Platform:', navigator.platform); + console.log('🧭 UA:', navigator.userAgent, 'Platform:', navigator.platform); const token = await Utils.generatePaintToken(sitekey); + console.log(`🔍 Token received - Type: ${typeof token}, Value: ${token ? (token.length > 50 ? token.substring(0, 50) + '...' : token) : 'null/undefined'}, Length: ${token?.length || 0}`); + if (token && token.length > 20) { const duration = Math.round(performance.now() - startTime); console.log(`✅ Turnstile token generated successfully in ${duration}ms`); return token; } else { - throw new Error("Invalid or empty token received"); + throw new Error(`Invalid or empty token received - Type: ${typeof token}, Length: ${token?.length || 0}`); } } catch (error) { const duration = Math.round(performance.now() - startTime); console.error(`❌ Turnstile token generation failed after ${duration}ms:`, error); - // Fallback to original browser automation if Turnstile fails - console.log("🔄 Falling back to browser automation..."); - const fbToken = await handleCaptchaFallback(); - return fbToken; + // Fallback to manual pixel placement for hybrid mode + if (state.tokenSource === "hybrid") { + console.log("🔄 Hybrid mode: Generator failed, automatically switching to manual pixel placement..."); + const fbToken = await handleCaptchaFallback(); + return fbToken; + } else { + // Pure generator mode - don't fallback, just fail + throw error; + } } } @@ -4289,14 +4253,44 @@
+ +
+ +
+ +

+ Generator mode creates tokens automatically. Hybrid mode falls back to manual when generator fails. Manual mode only uses pixel placement. +

+
+
+
- - +
@@ -4344,7 +4338,7 @@ cursor: pointer; ">
${CONFIG.PAINTING_SPEED.DEFAULT} px/s
+ ">${CONFIG.PAINTING_SPEED.DEFAULT} (batch size)
${CONFIG.PAINTING_SPEED.MIN} @@ -4363,7 +4357,7 @@
@@ -4910,6 +4904,21 @@ makeDraggable(settingsContainer) + const tokenSourceSelect = settingsContainer.querySelector("#tokenSourceSelect") + if (tokenSourceSelect) { + tokenSourceSelect.addEventListener("change", (e) => { + state.tokenSource = e.target.value + saveBotSettings() + console.log(`🔑 Token source changed to: ${state.tokenSource}`) + const sourceNames = { + 'generator': 'Automatic Generator', + 'hybrid': 'Generator + Auto Fallback', + 'manual': 'Manual Pixel Placement' + } + Utils.showAlert(`Token source set to: ${sourceNames[state.tokenSource]}`, "success") + }) + } + const languageSelect = settingsContainer.querySelector("#languageSelect") if (languageSelect) { languageSelect.addEventListener("change", (e) => { @@ -4944,6 +4953,18 @@ }); } + // Speed slider event listener + const speedSlider = settingsContainer.querySelector("#speedSlider"); + const speedValue = settingsContainer.querySelector("#speedValue"); + if (speedSlider && speedValue) { + speedSlider.addEventListener('input', (e) => { + const speed = parseInt(e.target.value, 10); + state.paintingSpeed = speed; + speedValue.textContent = `${speed} (batch size)`; + saveBotSettings(); + }); + } + if (enableBlueMarbleToggle) { enableBlueMarbleToggle.addEventListener('click', async () => { state.blueMarbleEnabled = enableBlueMarbleToggle.checked; @@ -6386,6 +6407,7 @@ pixelBatch.regionY !== regionY + adderY) { if (pixelBatch && pixelBatch.pixels.length > 0) { + console.log(`🌍 Sending region-change batch with ${pixelBatch.pixels.length} pixels (switching to region ${regionX + adderX},${regionY + adderY})`); const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); if (success) { @@ -6401,8 +6423,9 @@ } if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && pixelBatch.pixels.length > 0) { - const delayPerPixel = 1000 / state.paintingSpeed // ms per pixel - const totalDelay = Math.max(100, delayPerPixel * pixelBatch.pixels.length) // minimum 100ms + // paintingSpeed now represents batch size, so add a small delay based on batch size + const batchDelayFactor = Math.max(1, 100 / state.paintingSpeed); // Larger batches = less delay + const totalDelay = Math.max(100, batchDelayFactor * pixelBatch.pixels.length); await Utils.sleep(totalDelay) } updateStats(); @@ -6449,7 +6472,10 @@ localY: y, }); - if (pixelBatch.pixels.length >= Math.floor(state.currentCharges)) { + // Use paintingSpeed as batch size, but limit by available charges + const maxBatchSize = Math.min(state.paintingSpeed, Math.floor(state.currentCharges)); + if (pixelBatch.pixels.length >= maxBatchSize) { + console.log(`📦 Sending batch with ${pixelBatch.pixels.length} pixels (batch size setting: ${state.paintingSpeed}, max allowed: ${maxBatchSize})`); const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); if (success) { @@ -6507,6 +6533,7 @@ } if (pixelBatch && pixelBatch.pixels.length > 0 && !state.stopFlag) { + console.log(`🏁 Sending final batch with ${pixelBatch.pixels.length} pixels`); const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); if (success) { pixelBatch.pixels.forEach((pixel) => { @@ -6685,6 +6712,7 @@ paintingSpeed: state.paintingSpeed, paintingSpeedEnabled: document.getElementById('enableSpeedToggle')?.checked, cooldownChargeThreshold: state.cooldownChargeThreshold, + tokenSource: state.tokenSource, // "generator", "hybrid", or "manual" minimized: state.minimized, overlayOpacity: state.overlayOpacity, blueMarbleEnabled: document.getElementById('enableBlueMarbleToggle')?.checked, @@ -6719,6 +6747,7 @@ state.paintingSpeed = settings.paintingSpeed || CONFIG.PAINTING_SPEED.DEFAULT; state.cooldownChargeThreshold = settings.cooldownChargeThreshold || CONFIG.COOLDOWN_CHARGE_THRESHOLD; + state.tokenSource = settings.tokenSource || CONFIG.TOKEN_SOURCE; // Default to "generator" state.minimized = settings.minimized ?? false; CONFIG.PAINTING_SPEED_ENABLED = settings.paintingSpeedEnabled ?? false; CONFIG.AUTO_CAPTCHA_ENABLED = settings.autoCaptchaEnabled ?? false; @@ -6748,7 +6777,7 @@ const speedSlider = document.getElementById('speedSlider'); if (speedSlider) speedSlider.value = state.paintingSpeed; const speedValue = document.getElementById('speedValue'); - if (speedValue) speedValue.textContent = `${state.paintingSpeed} px/s`; + if (speedValue) speedValue.textContent = `${state.paintingSpeed} (batch size)`; const enableSpeedToggle = document.getElementById('enableSpeedToggle'); if (enableSpeedToggle) enableSpeedToggle.checked = CONFIG.PAINTING_SPEED_ENABLED; @@ -6766,6 +6795,10 @@ if (overlayOpacityValue) overlayOpacityValue.textContent = `${Math.round(state.overlayOpacity * 100)}%`; const enableBlueMarbleToggle = document.getElementById('enableBlueMarbleToggle'); if (enableBlueMarbleToggle) enableBlueMarbleToggle.checked = state.blueMarbleEnabled; + + const tokenSourceSelect = document.getElementById('tokenSourceSelect'); + if (tokenSourceSelect) tokenSourceSelect.value = state.tokenSource; + const colorAlgorithmSelect = document.getElementById('colorAlgorithmSelect'); if (colorAlgorithmSelect) colorAlgorithmSelect.value = state.colorMatchingAlgorithm; const enableChromaPenaltyToggle = document.getElementById('enableChromaPenaltyToggle'); @@ -6785,9 +6818,10 @@ } // Initialize Turnstile generator integration - console.log("🚀 WPlace Auto-Image with Turnstile Generator loaded"); - console.log("🔑 Turnstile generator: ALWAYS ENABLED"); - console.log("🎯 Manual pixel captcha solving: DISABLED - fully automated!"); + console.log("🚀 WPlace Auto-Image with Turnstile Token Generator loaded"); + console.log("🔑 Turnstile token generator: ALWAYS ENABLED (Background mode)"); + console.log("🎯 Manual pixel captcha solving: Available as fallback/alternative"); + console.log("📱 Turnstile widgets: DISABLED - pure background token generation only!"); // Optimized token initialization with better timing and error handling async function initializeTokenGenerator() { @@ -6853,4 +6887,4 @@ Utils.cleanupTurnstile(); }); }) -})() \ No newline at end of file +})()