"""监视人工手指滑动: 你滑, 我抓结果。 检测: 活动离开 KwaiWebViewActivity => PASS(result=1); 图片 md5 变化 => 刷新(350014/350002); 不变 => 未滑或静默回弹。 """ from __future__ import annotations import subprocess import time import hashlib from io import BytesIO import numpy as np from PIL import Image PHOTO_LEFT, PHOTO_RIGHT = 66, 1013 PHOTO_TOP, PHOTO_BOT = 267, 819 def adb_text(*a): r = subprocess.run(["adb", *a], capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=30) return r.stdout def state(): d = subprocess.run(["adb", "exec-out", "screencap", "-p"], capture_output=True, timeout=30).stdout img = np.asarray(Image.open(BytesIO(d)).convert("L")) photo = img[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT] md5 = hashlib.md5(photo.tobytes()).hexdigest()[:10] top = adb_text("shell", "dumpsys", "activity", "activities") act = next((l.strip() for l in top.splitlines() if "topResumedActivity" in l), "(none)") on_captcha = "KwaiWebViewActivity" in act return md5, on_captcha, act, img def moved(a, b): d = np.abs(b[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT].astype(np.int16) - a[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT].astype(np.int16)) return int((d > 45).sum()) def main(): print("[watch] 开始监视 (~45s)。请现在用手指把拼图块滑进缺口。", flush=True) base_md5, base_captcha, base_act, base_img = state() print(f"[watch] baseline md5={base_md5} on_captcha={base_captcha}", flush=True) t0 = time.monotonic(); last_print = 0; motion_seen = False while time.monotonic() - t0 < 45: time.sleep(0.6) md5, on_captcha, act, img = state() mv = moved(base_img, img) if mv > 2000 and not motion_seen: motion_seen = True print(f"[watch] t={time.monotonic()-t0:.0f}s 检测到手指拖动(diff={mv})。等释放结果...", flush=True) if not on_captcha: print(f"\n>>> [PASS] t={time.monotonic()-t0:.0f}s 活动离开验证码页(人工滑动通过)。", flush=True) print(f">>> top={act[:75]}", flush=True) print(">>> 结论: 是自动化被检测(非号码标记)。路径=人工/root+Frida。", flush=True) return 0 if md5 != base_md5 and motion_seen: print(f"\n>>> [REFRESH] t={time.monotonic()-t0:.0f}s 图片变化(md5 {base_md5}->{md5})但仍在验证码页。", flush=True) print(">>> 即人工正确滑动仍被拒 => 350014(号码被永久标记)。只能换号(新号不触发验证码)。", flush=True) return 0 if time.monotonic() - last_print > 4: print(f"[watch] t={time.monotonic()-t0:.0f}s 等待中... md5={'同' if md5==base_md5 else '变'} motion={'是' if motion_seen else '否'}", flush=True) last_print = time.monotonic() print("\n[watch] 超时未检测到明确结果。可能没滑/滑错回弹。再跑一次或告诉我结果。", flush=True) return 0 if __name__ == "__main__": raise SystemExit(main())