Files
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

74 lines
2.5 KiB
Python

# -*- coding: utf-8 -*-
"""
图标/图文点选求解器 回归测试 (AGENTS.md §5.9)
固化的性质:
1. 提示条(目标原图) -> 背景图 的**形状模板匹配**在真机样本上给出正确、有序的点击点;
2. 能处理**旋转**目标 (样本里"图表"图标被倾斜 ~-30°);
3. 能排除干扰图标 (样本里的笑脸/银行).
样本: tcaptcha/tests/fixtures/1ms_icon_click_1_{prompt,bg}.jpg
取自 1ms.run (aid=195672344) 真机页面上弹出的验证码 (2026-10-11),
题面 = "0" + 图表图标 + "7", 期望点击顺序与坐标见 EXPECTED。
运行:
.venv/bin/python -m tcaptcha.tests.test_icon_click_solver
"""
from __future__ import annotations
import sys
from pathlib import Path
import cv2
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from tcaptcha.solvers.icon_click_rotate import IconClickSolver # noqa: E402
FIX = ROOT / "tcaptcha" / "tests" / "fixtures"
# 期望点击顺序: ("0" 右下) -> (倾斜的图表图标 左上) -> ("7" 中上),坐标是 672x480 原图口径
EXPECTED = [(520, 440), (200, 40), (360, 120)]
TOL = 12 # 允许的像素误差 (连通域中心 vs 目视中心)
_fails: list = []
def check(name: str, cond: bool, detail: str = "") -> None:
print(f" [{'PASS' if cond else 'FAIL'}] {name}" + (f" {detail}" if detail else ""))
if not cond:
_fails.append(name)
def main() -> int:
pr = cv2.imread(str(FIX / "1ms_icon_click_1_prompt.jpg"))
bg = cv2.imread(str(FIX / "1ms_icon_click_1_bg.jpg"))
check("fixture 可读", pr is not None and bg is not None)
print("1) 求解")
res = IconClickSolver().solve(bg, pr)
check("目标数=3", res.target_count == 3, str(res.target_count))
check("求解成功", res.ok, res.describe())
print("2) 点击顺序与坐标")
for i, (exp, got) in enumerate(zip(EXPECTED, res.points)):
dx, dy = abs(exp[0] - got[0]), abs(exp[1] - got[1])
check(f"第{i+1}个点 ~{exp}", dx <= TOL and dy <= TOL,
f"got={got} (Δ={dx},{dy}) score={res.scores[i]:.3f}")
print("3) 旋转与干扰项")
check("命中倾斜图标 (角度非 0)", any(abs(a) >= 10 for a in res.angles), str(res.angles))
check("所有匹配分数 > 0.45", res.min_score > 0.45, f"min={res.min_score:.3f}")
print()
if _fails:
print(f"❌ test_icon_click_solver 失败 {len(_fails)} 项: {_fails}")
return 1
print("✅ test_icon_click_solver 全部通过")
return 0
if __name__ == "__main__":
raise SystemExit(main())