Files
hello_js_reverse_skill/tcaptcha/server.py
T
yml2213 344eea2194 refactor(结构): 分层重组 + 清除全部废弃路线
做了什么:
- 包改为分层: tcaptcha/{protocol,telemetry,solvers,ocr,adapters,models,tests}
  * protocol/  client.py  transport.py  proxy.py
  * telemetry/ crypto.py  values.py  key.py  opcodes.py
               disasm.py  program.py  layout.py  modules.py
  * solvers/   text_click.py  icon_click.py  icon_click_rotate.py  slider.py
  * ocr/       engines.py  slicer.py
  * adapters/  one_ms.py
- tools/ 里原本是**包运行时依赖**的 tdc_{disasm,program,layout,modules}.py 并入 telemetry/
  (放在 tools/ 属于分层倒置),tools/ 只留 CLI: tdc_key_pure.py / tdc_profile.py / cdp_*.js
- crypto.py 529 -> 340 行: 切除「成功样本模板回填」与「Node 插桩兜底」两条老路;
  generate_telemetry 只剩 python_values 一种模式,传别的会直接报错并说明原因
- 求解器改名分清: solver.py -> solvers/icon_click.py(client 实际在用的 YOLO+ResNet18 ONNX),
  icon_click_solver.py -> solvers/icon_click_rotate.py(仅测试在用,留作对照)
- pyproject: 去掉 ultralytics/onnx(训练栈已删),补上真正在用的 onnxruntime

删除了什么:
- 雨云整条线 + dist/rainyun_qinglong(104M 旧快照)
- 模板回填: models/success_collect_template.b64、tdc_worker.js、analyze_collect_layout.py
- 「补环境」时代浏览器 JS: chrome_fetch/manual_captcha/probe_captcha/solve_in_browser/tdc_browser
- 旧 VM 探索工具 10 个(tdc_collectgen/tdc_slotmap/tdc_lift/tdc_vm_run/tdc_query/tdc_probe 等)
  + 8 个基于它们的旧 verify 脚本
- 训练/标注/评估: train_classifier/train_detector/prepare_yolo/web_labeler/collector/
  siamese/evaluate/benchmark_verify
- tcaptcha/core_js/(60 个分析产物)、TOOLCHAIN_REPORT.md(整篇在描述已删工具)
- 3 个已无任何引用的 PyTorch 权重:icon_classifier.pt 43M / yolo_detector.pt 5.9M /
  siamese_matcher.pt 1.6M —— 线上只跑 onnx,tcaptcha/ 117M -> 66M

为什么:
- 项目后面要专门承载「滑块 / 文字点选 / 图标点选」三种求解器;扁平包 + 运行时依赖混在
  tools/ + 两条已废弃的 collect 生成路线,会让后续扩展持续踩坑

怎么验证:
- .venv/bin/python -m tcaptcha.selftest                    全绿
- scripts/verify/verify_offline.py                         逐槽全等 + 分段几何一致
- scripts/verify/verify_values.py                          4 份真机明文 152/152 逐槽全等
- scripts/verify/send_test.py --mode python_values         1ms 签到页文字点选 errorCode=0
- 回滚点: tag backup/pre-refactor-20261012
2026-10-12 12:34:57 +08:00

150 lines
5.0 KiB
Python

# -*- 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://<IP>: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://<IP>:%d/solve", args.port)
try:
httpd.serve_forever()
except KeyboardInterrupt:
logger.info("服务已停止")
finally:
httpd.server_close()
if __name__ == "__main__":
main()