From 2ab307aaa93f47b9264bbc7eea24c24d8645e487 Mon Sep 17 00:00:00 2001 From: maiminhdung <99890979+maiminhdung@users.noreply.github.com> Date: Mon, 25 Aug 2025 00:35:20 +0700 Subject: [PATCH] I NEED SLEEP + Recode AutoSave + change some miragte + readd turnstile widget + change some captcha method + Can click save file and download while waiting for charges --- Auto-Image.js | 470 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 306 insertions(+), 164 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index a723430..b4ea565 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -1089,6 +1089,11 @@ notificationIntervalMinutes: CONFIG.NOTIFICATIONS.REPEAT_MINUTES, _lastChargesNotifyAt: 0, _lastChargesBelow: true, + // Smart save tracking + _lastSavePixelCount: 0, + _lastSaveTime: 0, + _saveInProgress: false, + paintedMap: null, } let _updateResizePreview = () => { }; @@ -1428,7 +1433,7 @@ const overlayManager = new OverlayManager(); - // Optimized Turnstile token handling with caching and retry logic + // Optimized Turnstile token handling with improved caching and retry logic let turnstileToken = null let tokenExpiryTime = 0 let tokenGenerationInProgress = false @@ -1439,26 +1444,35 @@ const MAX_BATCH_RETRIES = 10 // Maximum attempts for batch sending const TOKEN_LIFETIME = 240000 // 4 minutes (tokens typically last 5 min, use 4 for safety) - function setTurnstileToken(t) { + function setTurnstileToken(token) { if (_resolveToken) { - _resolveToken(t) + _resolveToken(token) _resolveToken = null } - turnstileToken = t + turnstileToken = token tokenExpiryTime = Date.now() + TOKEN_LIFETIME - retryCount = 0 // Reset retry count on successful token + console.log("✅ Turnstile token set successfully") } function isTokenValid() { return turnstileToken && Date.now() < tokenExpiryTime } - async function ensureToken() { - // Return cached token if still valid - if (isTokenValid()) { + function invalidateToken() { + turnstileToken = null + tokenExpiryTime = 0 + console.log("🗑️ Token invalidated, will force fresh generation") + } + + async function ensureToken(forceRefresh = false) { + // Return cached token if still valid and not forcing refresh + if (isTokenValid() && !forceRefresh) { return turnstileToken; } + // Invalidate token if forcing refresh + if (forceRefresh) invalidateToken(); + // Avoid multiple simultaneous token generations if (tokenGenerationInProgress) { console.log("🔄 Token generation already in progress, waiting..."); @@ -1471,44 +1485,59 @@ try { console.log("🔄 Token expired or missing, generating new one..."); const token = await handleCaptchaWithRetry(); - if (token) { + if (token && token.length > 20) { setTurnstileToken(token); - console.log("✅ Token generated successfully"); + console.log("✅ Token captured and cached successfully"); return token; } - } catch (error) { - console.error("❌ Token generation failed after retries:", error); - updateUI("captchaNeeded", "error"); - Utils.showAlert(Utils.t("captchaNeeded"), "error"); + + console.log("⚠️ Invisible Turnstile failed, forcing browser automation..."); + const fallbackToken = await handleCaptchaFallback(); + if (fallbackToken && fallbackToken.length > 20) { + setTurnstileToken(fallbackToken); + console.log("✅ Fallback token captured successfully"); + return fallbackToken; + } + + console.log("❌ All token generation methods failed"); + return null; } finally { tokenGenerationInProgress = false; } - - return null; } async function handleCaptchaWithRetry() { - for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { - try { - const token = await handleCaptcha(); - if (token && token.length > 20) { - return token; - } - throw new Error("Invalid token received"); - } catch (error) { - console.warn(`❌ Token generation attempt ${attempt}/${MAX_RETRIES} failed:`, error); - - if (attempt < MAX_RETRIES) { - const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000); // Exponential backoff, max 8s - console.log(`⏳ Retrying in ${delay}ms...`); - await Utils.sleep(delay); - } else { - throw error; - } + const startTime = Date.now(); + try { + const sitekey = Utils.detectSitekey(); + console.log("🔑 Generating Turnstile token for sitekey:", sitekey); + + if (typeof window !== "undefined" && window.navigator) { + console.log("🧭 UA:", window.navigator.userAgent, "Platform:", window.navigator.platform); } + + const token = await Utils.generatePaintToken(sitekey); + if (token && token.length > 20) { + const elapsed = Math.round(Date.now() - startTime); + console.log(`✅ Turnstile token generated successfully in ${elapsed}ms`); + return token; + } else { + throw new Error("Invalid or empty token received"); + } + } catch (error) { + const elapsed = Math.round(Date.now() - startTime); + console.log(`❌ Turnstile token generation failed after ${elapsed}ms:`, error); + throw error; } } + async function handleCaptchaFallback() { + // Implementation for fallback token generation would go here + // This is a placeholder for browser automation fallback + console.log("🔄 Attempting fallback token generation..."); + return null; + } + function inject(callback) { const script = document.createElement('script'); script.textContent = `(${callback})();`; @@ -1620,6 +1649,7 @@ // Turnstile Generator Integration - Optimized with widget reuse and proper cleanup turnstileLoaded: false, _turnstileContainer: null, + _turnstileOverlay: null, _turnstileWidgetId: null, _lastSitekey: null, @@ -1643,6 +1673,7 @@ }; return checkReady(); } + const script = document.createElement('script'); script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; script.async = true; @@ -1660,7 +1691,7 @@ }); }, - // Create or reuse the turnstile container - completely hidden for token generation + // 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 @@ -1688,64 +1719,85 @@ return this._turnstileContainer; }, - // No interactive overlay container needed - pure background operation + // Interactive overlay container for visible widgets when needed ensureTurnstileOverlayContainer() { - // Return null since we don't want any visible widgets - console.log('🚫 Interactive Turnstile overlay disabled - background mode only'); - return null; + 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 !important; + bottom: 20px !important; + right: 20px !important; + z-index: 99999 !important; + background: rgba(0,0,0,0.9) !important; + border-radius: 12px !important; + padding: 20px !important; + box-shadow: 0 8px 32px rgba(0,0,0,0.4) !important; + backdrop-filter: blur(10px) !important; + border: 1px solid rgba(255,255,255,0.2) !important; + color: white !important; + font-family: 'Segoe UI', sans-serif !important; + display: none !important; + max-width: 350px !important; + min-width: 300px !important; + `; + + 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 host = document.createElement('div'); + host.id = 'turnstile-overlay-host'; + host.style.cssText = 'width: 100%; min-height: 70px;'; + + const hideBtn = document.createElement('button'); + hideBtn.textContent = 'Hide'; + hideBtn.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;'; + hideBtn.addEventListener('click', () => overlay.remove()); + + overlay.appendChild(title); + overlay.appendChild(host); + overlay.appendChild(hideBtn); + document.body.appendChild(overlay); + + this._turnstileOverlay = overlay; + return overlay; }, async executeTurnstile(sitekey, action = 'paint') { await this.loadTurnstile(); - // Try multiple sitekeys if the first one fails - const sitekeys = [ - sitekey, - '0x4AAAAAABpqJe8FO0N84q0F' - ]; - - for (let i = 0; i < sitekeys.length; i++) { - const currentSitekey = sitekeys[i]; - console.log(`🔧 Trying sitekey ${i + 2}/${sitekeys.length}:`, currentSitekey); - - // Try reusing existing widget first - if (this._turnstileWidgetId && this._lastSitekey === currentSitekey && window.turnstile?.execute) { - try { - console.log("🔄 Reusing existing Turnstile widget..."); - const token = await Promise.race([ - window.turnstile.execute(this._turnstileWidgetId, { action }), - new Promise((_, reject) => setTimeout(() => reject(new Error('Execute timeout')), 15000)) - ]); - if (token && typeof token === 'string' && token.length > 20) { - console.log("✅ Token generated via widget reuse"); - return token; - } - } catch (err) { - console.warn('🔄 Widget reuse failed:', err.message); + // Try reusing existing widget first if sitekey matches + if (this._turnstileWidgetId && this._lastSitekey === sitekey && window.turnstile?.execute) { + try { + console.log("🔄 Reusing existing Turnstile widget..."); + const token = await Promise.race([ + window.turnstile.execute(this._turnstileWidgetId, { action }), + new Promise((_, reject) => setTimeout(() => reject(new Error('Execute timeout')), 15000)) + ]); + if (token && token.length > 20) { + console.log("✅ Token generated via widget reuse"); + return token; } + } catch (error) { + console.log("� Widget reuse failed, will create a fresh widget:", error.message); } - - // Try invisible widget - const invisible = await this.createNewTurnstileWidgetInvisible(currentSitekey, action); - if (invisible && typeof invisible === 'string' && invisible.length > 20) { - console.log(`✅ Token generated with sitekey ${i + 1}: ${currentSitekey}`); - return invisible; - } - - console.warn(`❌ Sitekey ${i + 1} failed:`, currentSitekey); - - // Clean up failed widget before trying next sitekey - if (this._turnstileWidgetId && window.turnstile?.remove) { - try { window.turnstile.remove(this._turnstileWidgetId); } catch {} - } - this._turnstileWidgetId = null; } - console.log('👀 All sitekeys failed for invisible widgets, trying interactive...'); - return await this.createNewTurnstileWidgetInteractive(sitekey, action); + // Try invisible widget first + const invisibleToken = await this.createTurnstileWidget(sitekey, action); + if (invisibleToken && invisibleToken.length > 20) { + return invisibleToken; + } + + console.log("� Falling back to interactive Turnstile (visible)."); + return await this.createTurnstileWidgetInteractive(sitekey, action); }, - async createNewTurnstileWidgetInvisible(sitekey, action) { + async createTurnstileWidget(sitekey, action) { return new Promise((resolve) => { try { // Force cleanup of any existing widget @@ -1768,60 +1820,33 @@ return; } - // Set a timeout to resolve with null if no token is received - const timeout = setTimeout(() => { - console.warn('⏰ Turnstile widget timeout - no token received in 15s'); - resolve(null); - }, 15000); - console.log('🔧 Creating invisible Turnstile widget...'); const widgetId = window.turnstile.render(container, { sitekey, action, - size: 'compact', + size: 'invisible', retry: 'auto', 'retry-interval': 8000, callback: (token) => { - clearTimeout(timeout); - console.log('🔍 Turnstile callback received:', { - type: typeof token, - value: token, - length: token?.length || 0, - isString: typeof token === 'string', - isValid: typeof token === 'string' && token.length > 20 - }); - - // Ensure we have a valid string token - if (typeof token === 'string' && token.length > 20) { - console.log('✅ Valid token received from hidden widget'); - resolve(token); - } else { - console.warn('❌ Invalid token received from hidden widget - resolving with null'); - resolve(null); - } - }, - 'error-callback': (error) => { - clearTimeout(timeout); - console.warn('❌ Turnstile error callback:', error); - resolve(null); - }, - 'timeout-callback': () => { - clearTimeout(timeout); - console.warn('⏰ Turnstile timeout callback triggered'); - resolve(null); + console.log('✅ Invisible Turnstile callback'); + resolve(token); }, + 'error-callback': () => resolve(null), + 'timeout-callback': () => resolve(null) }); this._turnstileWidgetId = widgetId; this._lastSitekey = sitekey; if (!widgetId) { - clearTimeout(timeout); - console.warn('❌ Failed to create Turnstile widget - render returned invalid ID'); - resolve(null); - } else { - console.log('✅ Turnstile widget created with ID:', widgetId); + return resolve(null); } + + // Execute the widget and race with timeout + Promise.race([ + window.turnstile.execute(widgetId, { action }), + new Promise((_, reject) => setTimeout(() => reject(new Error('Invisible execute timeout')), 12000)) + ]).then(resolve).catch(() => resolve(null)); } catch (e) { console.error('❌ Invisible Turnstile creation failed:', e); resolve(null); @@ -1829,7 +1854,7 @@ }); }, - async createNewTurnstileWidgetInteractive(sitekey, action) { + async createTurnstileWidgetInteractive(sitekey, action) { // Create a visible widget that users can interact with if needed console.log('🔄 Creating interactive Turnstile widget (visible)'); @@ -1844,42 +1869,27 @@ } } - const container = this.ensureTurnstileContainer(); - container.innerHTML = ''; + const overlay = this.ensureTurnstileOverlayContainer(); + overlay.style.display = 'block'; - // Make container visible for interactive mode - container.style.display = 'block'; - container.style.position = 'fixed'; - container.style.top = '50%'; - container.style.left = '50%'; - container.style.transform = 'translate(-50%, -50%)'; - container.style.zIndex = '10000'; - container.style.backgroundColor = 'white'; - container.style.padding = '20px'; - container.style.borderRadius = '8px'; - container.style.boxShadow = '0 4px 20px rgba(0,0,0,0.3)'; - - // Add instruction text - container.innerHTML = '