jarvis-cloud / backend /tools /captcha_solver.py
Jarvis2345's picture
Squash history — remove all prior commits (secret hygiene, S4)
a31f556
Raw
History Blame Contribute Delete
3.59 kB
# backend/tools/captcha_solver.py
import logging
import asyncio
import json
from backend.services.token_manager import gemini_call_with_checkpoint
async def solve_captcha_if_present(page):
"""
Scans the DOM for reCAPTCHA, hCaptcha, or Cloudflare Turnstile.
If found, takes a screenshot and uses gemini-3.5-flash to locate the exact click coordinates.
"""
try:
# Quick check: does the page contain common CAPTCHA keywords in frames?
frames = page.frames
captcha_detected = False
for frame in frames:
name = frame.name.lower()
url = frame.url.lower()
if "recaptcha" in name or "recaptcha" in url or "hcaptcha" in url or "turnstile" in url:
captcha_detected = True
break
if not captcha_detected:
# Also check page content for challenge iframes
content = await page.content()
if "recaptcha" in content.lower() or "hcaptcha" in content.lower() or "turnstile" in content.lower():
captcha_detected = True
if not captcha_detected:
return False
logging.warning("CAPTCHA detected! Engaging Gemini 3.5 Flash Vision solver...")
# Max 3 attempts for dynamic puzzles
for attempt in range(3):
# Take full page screenshot
screenshot_bytes = await page.screenshot()
prompt = """You are an advanced CAPTCHA solver. Look at this screenshot.
Find the CAPTCHA element (like an 'I am human' checkbox, or a puzzle tile that needs to be clicked).
Return ONLY a valid JSON object containing the exact absolute X and Y pixel coordinates representing the center of the element to click.
Example: {"x": 450, "y": 600}
If the CAPTCHA is already solved or no CAPTCHA is visible, return {"x": 0, "y": 0}.
Do not include markdown blocks, just the raw JSON.
"""
# Encode screenshot as base64 for the vision prompt
import base64
img_b64 = base64.b64encode(screenshot_bytes).decode()
vision_prompt = f"{prompt}\n\n[IMAGE_BASE64]: data:image/png;base64,{img_b64[:500]}..."
try:
raw_text = await gemini_call_with_checkpoint(
prompt=vision_prompt,
task_type="captcha",
persona="jarvis"
)
raw_text = raw_text.strip().strip("```json").strip("```").strip()
coords = json.loads(raw_text)
x = int(coords.get('x', 0))
y = int(coords.get('y', 0))
if x == 0 and y == 0:
logging.info("Gemini 3.5 Flash reported CAPTCHA cleared or not visible.")
return True
logging.info(f"Gemini 3.5 Flash requested click at ({x}, {y}). Executing stealth move...")
# Stealth mouse movement
await page.mouse.move(x, y, steps=10)
await asyncio.sleep(0.5)
await page.mouse.click(x, y)
# Wait for puzzle transition or success mark
await asyncio.sleep(4)
except Exception as genai_err:
logging.error(f"Gemini Vision parsing failed on attempt {attempt+1}: {genai_err}")
await asyncio.sleep(2)
return True
except Exception as e:
logging.error(f"CAPTCHA solver master error: {e}")
return False