| import os |
| import json |
| import re |
| import threading |
| import logging |
| from http.server import HTTPServer, BaseHTTPRequestHandler |
| from PIL import Image |
| from google import genai |
| from telegram import Update |
| from telegram.ext import ApplicationBuilder, ContextTypes, MessageHandler, filters |
|
|
| |
| logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO) |
|
|
| |
| class HealthCheckHandler(BaseHTTPRequestHandler): |
| def do_GET(self): |
| self.send_response(200) |
| self.end_headers() |
| self.wfile.write(b"Universal Captcha Solver is Online!") |
|
|
| def run_health_check(): |
| httpd = HTTPServer(('0.0.0.0', 7860), HealthCheckHandler) |
| logging.info("Health check server started on port 7860.") |
| httpd.serve_forever() |
|
|
| |
| GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") |
| TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN") |
| MODEL_ID = "gemini-3.1-flash-lite-preview" |
|
|
| client = genai.Client(api_key=GEMINI_API_KEY) |
|
|
| |
| |
| UNIVERSAL_PROMPT = """ |
| Analyze this hCaptcha challenge image. Perform the specific task requested in the image instruction. |
| |
| Task Types and Requirements: |
| 1. "Drag the blocks/letter": Identify the source block and the exact target slot. |
| Return: {"type": "drag", "moves": [{"source": [x,y], "destination": [x,y]}]} |
| |
| 2. "Click/Pick/Select objects" (Ingredients, Wall outlets, Bicycles, etc.): Identify all matching items. |
| Return: {"type": "click", "points": [[x,y], [x,y], ...]} |
| |
| 3. "Two shapes that are identical": Find the two matching shapes. |
| Return: {"type": "click", "points": [[x,y], [x,y]]} |
| |
| 4. "Click the asterisk where animals meet": Identify the meeting point. |
| Return: {"type": "click", "points": [[x,y]]} |
| |
| Output Rules: |
| - Return ONLY a strict JSON object. |
| - Use normalized coordinates [0-1000] for both X and Y. |
| - Ensure points are the exact centers of the objects. |
| """ |
|
|
| |
| def scale_coord(p, target_w=500, target_h=470): |
| try: |
| |
| raw_x = p[0][0] if isinstance(p[0], list) else p[0] |
| raw_y = p[1][0] if isinstance(p[1], list) else p[1] |
| return { |
| "x": int((float(raw_x) / 1000) * target_w), |
| "y": int((float(raw_y) / 1000) * target_h) |
| } |
| except: |
| return None |
|
|
| async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE): |
| photo_file = await update.message.photo[-1].get_file() |
| photo_path = f"task_{update.message.message_id}.png" |
| await photo_file.download_to_drive(photo_path) |
| |
| await update.message.reply_text("🧠 Analyzing challenge type and calculating coordinates...") |
|
|
| try: |
| raw_img = Image.open(photo_path) |
| response = client.models.generate_content( |
| model=MODEL_ID, |
| contents=[UNIVERSAL_PROMPT, raw_img] |
| ) |
| |
| |
| raw_text = response.text |
| match = re.search(r"\{.*\}", raw_text, re.DOTALL) |
| data = json.loads(match.group(0)) if match else {} |
| |
| result = {"model": MODEL_ID, "analysis_type": data.get("type")} |
| |
| |
| if data.get("type") == "drag": |
| moves = [] |
| for m in data.get("moves", []): |
| moves.append({ |
| "source": scale_coord(m["source"]), |
| "destination": scale_coord(m["destination"]) |
| }) |
| result["required_moves"] = moves |
| else: |
| points = [scale_coord(p) for p in data.get("points", []) if scale_coord(p)] |
| result["points_to_click"] = points |
|
|
| await update.message.reply_text(f"✅ Solution:\n\n```json\n{json.dumps(result, indent=2)}\n```", parse_mode='Markdown') |
| |
| except Exception as e: |
| logging.error(f"Error: {e}") |
| await update.message.reply_text(f"❌ Error: {str(e)}") |
| finally: |
| if os.path.exists(photo_path): os.remove(photo_path) |
|
|
| if __name__ == '__main__': |
| threading.Thread(target=run_health_check, daemon=True).start() |
| app = ApplicationBuilder().token(TELEGRAM_TOKEN).build() |
| app.add_handler(MessageHandler(filters.PHOTO, handle_photo)) |
| logging.info("Bot started. Send any hCaptcha image!") |
| app.run_polling() |
| |