feat(slider): 滑块 (2401/2402) 端到端打通 —— errorCode=0

做了什么:
- solvers/slider.py: 新增 SliderChallenge(把课题 JSON 解析成解题参数)+ piece_from_sprite
  (按 sprite_pos/size_2d 裁拼图块)+ track_limit 解析 / clamp_x / answer(按 data_type 组答案)
- telemetry/values.py: 新增 drag_trajectory_points(拖动轨迹:按下->横移->松手,
  首点绝对、其余增量、尾点固定 [1,1,12]);_sample_dt 抽成模块级供两条轨迹复用
- telemetry/{values,crypto}.py: generate_collect / generate_telemetry 接受 trajectory=
- protocol/client.py: 新增滑块分支(拉底图 + 精灵图 -> 裁块 -> ZNCC -> 组答案 -> 拖动轨迹),
  accept_kinds 默认扩为 文字点选 + 图标点选 + 滑块
- scripts/verify/send_test.py: 加 --kind text|slider|icon|any

关键结论(账本 #60~#63):
- 滑块答案是 DynAnswerType_POS,但坐标用**背景图原始像素**(不是页面显示像素):
  所以解题用 scale=1.0 直接提交 target_x_image;elem_id / data_type 均取自 move_cfg
- 拼图块可从课题配置直接裁,与旧 DOM 换算结果**逐像素相同**(测试已加交叉断言)
- **ddddocr 的滑块距离识别不可用**:fixture 偏 59px、实时偏 60px。判定手段是把拼图块
  alpha 轮廓叠到背景上目视 —— 本项目 ZNCC 与缺口描边严丝合缝,ddddocr 整体右偏
- 低置信度(margin=0.024 < 0.08)那次被正确拒答并换题,换题后立刻成功,安全网有效

怎么验证:
- .venv/bin/python -m tcaptcha.tests.test_slider_solver   全绿(新增 [6]/[7] 两组断言)
- .venv/bin/python -m tcaptcha.selftest                   全绿
- .venv/bin/python scripts/verify/send_test.py --kind slider   3/3 errorCode=0
- 文字点选回归不受影响:--kind text 仍为 errorCode=0
This commit is contained in:
yml2213
2026-10-12 12:46:35 +08:00
parent 344eea2194
commit 6a8b3b3ce4
7 changed files with 294 additions and 33 deletions
+13 -2
View File
@@ -25,17 +25,27 @@ sys.path.insert(0, str(ROOT))
sys.path.insert(0, str(ROOT / "tools"))
import tcaptcha.protocol.client as client_mod # noqa: E402
from tcaptcha.protocol.client import KIND_TEXT_CLICK, TencentCaptchaClient # noqa: E402
from tcaptcha.protocol.client import (KIND_ICON_CLICK, KIND_SLIDER, # noqa: E402
KIND_TEXT_CLICK, TencentCaptchaClient)
AID = "195672344"
ENTRY = "https://1ms.run/console/personal/benefits/check-in"
REFERER = "https://1ms.run/"
KIND_MAP = {
"text": (KIND_TEXT_CLICK,),
"slider": (KIND_SLIDER,),
"icon": (KIND_ICON_CLICK,),
"any": (KIND_TEXT_CLICK, KIND_ICON_CLICK, KIND_SLIDER),
}
def main() -> int:
ap = argparse.ArgumentParser(description="1ms 签到页文字点选 · 真实发送测试")
ap.add_argument("--mode", default="python_values",
choices=["python_values", "python_keyed", "python"])
ap.add_argument("--kind", default="text", choices=list(KIND_MAP),
help="只解哪种题(默认 text=文字点选)")
ap.add_argument("--max-refreshes", type=int, default=12)
ap.add_argument("--max-retries", type=int, default=2)
args = ap.parse_args()
@@ -73,7 +83,7 @@ def main() -> int:
c = TencentCaptchaClient(
aid=AID, entry_url=ENTRY, referer=REFERER, wb="1",
accept_kinds=(KIND_TEXT_CLICK,),
accept_kinds=KIND_MAP[args.kind],
telemetry_mode=args.mode,
proxy=None,
)
@@ -86,6 +96,7 @@ def main() -> int:
print()
print("=" * 70)
print("模式 : %s" % args.mode)
print("题型 : %s" % args.kind)
print("耗时 : %.1fs" % (time.time() - t0))
print("success : %s" % res.get("success"))
print("kind : %s 指令=%r" % (res.get("kind"), res.get("instruction")))