# -*- coding: utf-8 -*- """ 腾讯天御点选验证码纯协议 HTTP 微服务 (Tencent Turing Captcha HTTP Microservice) 用于在独立机器/宿主机/GPU服务器上运行,供青龙容器或其他轻量级客户端通过 HTTP API 跨机调用。 基于 Python 原生 ThreadingHTTPServer 实现,零额外依赖 (无需额外安装 FastAPI/Flask)。 使用方式: uv run python -m tcaptcha.server --port 8000 --host 0.0.0.0 调用接口: POST http://:8000/solve 请求体 (可选): {"aid": "2039519451"} 返回 JSON: { "code": 200, "success": true, "ticket": "tr03...", "randstr": "@...", "elapsed_ms": 820 } """ import argparse import json import logging import sys from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parent.parent if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from tcaptcha.protocol.client import TencentCaptchaClient from tcaptcha.solvers.icon_click import CaptchaSolver logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S", ) logger = logging.getLogger("tcaptcha_server") GLOBAL_SOLVER = None def get_solver() -> CaptchaSolver: global GLOBAL_SOLVER if GLOBAL_SOLVER is None: logger.info("正在初始化并预加载 AI 求解推理引擎 (YOLOv8 + ResNet18)...") GLOBAL_SOLVER = CaptchaSolver() logger.info("✓ AI 推理引擎预加载完成!") return GLOBAL_SOLVER class CaptchaHttpHandler(BaseHTTPRequestHandler): def _send_json(self, code: int, data: dict): body = json.dumps(data, ensure_ascii=False).encode("utf-8") self.send_response(code) self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Headers", "*") self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS") self.end_headers() self.wfile.write(body) def do_OPTIONS(self): self._send_json(200, {"status": "ok"}) def do_GET(self): if self.path in ["/health", "/"]: self._send_json(200, { "status": "online", "service": "Tencent Captcha Solver Microservice", "endpoints": { "solve": "POST /solve (body: {'aid': '2039519451'})", "health": "GET /health" } }) elif self.path.startswith("/solve"): # 允许通过 GET 快速调试 self._handle_solve({"aid": "2039519451"}) else: self._send_json(404, {"error": "Not Found"}) def do_POST(self): if self.path.startswith("/solve"): content_length = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(content_length) if content_length > 0 else b"{}" try: params = json.loads(body.decode("utf-8")) if body else {} except Exception: params = {} self._handle_solve(params) else: self._send_json(404, {"error": "Not Found"}) def _handle_solve(self, params: dict): aid = params.get("aid") or "2039519451" logger.info(f"收到求解请求 (AppID: {aid})...") solver = get_solver() client = TencentCaptchaClient(aid=aid, solver=solver) result = client.solve() if result.get("success"): self._send_json(200, { "code": 200, "success": True, "ticket": result.get("ticket"), "randstr": result.get("randstr"), "elapsed_ms": result.get("elapsed_ms"), "points": result.get("points"), }) else: self._send_json(500, { "code": 500, "success": False, "error": result.get("error"), "elapsed_ms": result.get("elapsed_ms"), }) def log_message(self, format, *args): logger.info("%s - %s", self.address_string(), format % args) def main(): parser = argparse.ArgumentParser(description="Tencent Captcha Microservice") parser.add_argument("--host", type=str, default="0.0.0.0", help="Listen host (default: 0.0.0.0)") parser.add_argument("--port", type=int, default=8000, help="Listen port (default: 8000)") args = parser.parse_args() # 预加载推理引擎 get_solver() server_address = (args.host, args.port) httpd = ThreadingHTTPServer(server_address, CaptchaHttpHandler) logger.info(f"🚀 验证码微服务已启动: http://{args.host}:{args.port}") logger.info("可用于青龙面板配置环境变量: TCAPTCHA_API_URL=http://:%d/solve", args.port) try: httpd.serve_forever() except KeyboardInterrupt: logger.info("服务已停止") finally: httpd.server_close() if __name__ == "__main__": main()