167 lines
6.2 KiB
Python
167 lines
6.2 KiB
Python
"""v6: 修正 R=1.0 (1:1 联动, 同浏览器) + 轨迹确认拼图块到达缺口 + 观察释放。
|
|
|
|
v4/v5 用 R=1.27 (受低 handle 处有偏 piece_x 污染) => target 714 => 拼图块实际到 760,
|
|
错过缺口 916 共 156px => 静默回弹(有效区假设: 错位回弹, 不算尝试)。
|
|
轨迹标定证实 R≈0.99 (handle/piece 同步 ~88/步), home_true=183 自洽。
|
|
|
|
target = handle_home + (notch - 183) / 1.0 = 137 + 733 = 870。
|
|
慢速 swipe 到 870, 轨迹跟踪 piece 确认到达 ~916, 释放后观察:
|
|
close => result=1 (真机指纹过!); refresh+350014 => 号码标记; snapback => 仍错位或bot。
|
|
handle 预测跟踪(左移窗, 跟住左侧按钮, 不抓 x727 元素)。
|
|
"""
|
|
from __future__ import annotations
|
|
import subprocess
|
|
import time
|
|
from io import BytesIO
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
PHOTO_LEFT, PHOTO_RIGHT = 66, 1013
|
|
PHOTO_TOP, PHOTO_BOT = 267, 819
|
|
HOME_TRUE = 66 + (24 + 61) * (947 / 686.0) # 183.2
|
|
R = 1.0
|
|
TRACK_Y = 950
|
|
|
|
|
|
def adb_text(*a):
|
|
r = subprocess.run(["adb", *a], capture_output=True, text=True,
|
|
encoding="utf-8", errors="replace", timeout=60)
|
|
return r.stdout
|
|
|
|
|
|
def cap():
|
|
d = subprocess.run(["adb", "exec-out", "screencap", "-p"],
|
|
capture_output=True, timeout=30).stdout
|
|
return np.asarray(Image.open(BytesIO(d)).convert("L"))
|
|
|
|
|
|
def photo(img):
|
|
return img[PHOTO_TOP:PHOTO_BOT, PHOTO_LEFT:PHOTO_RIGHT]
|
|
|
|
|
|
def detect_notch(img, x_min_local=230, col_thr=25):
|
|
reg = photo(img)
|
|
col = (reg < 90).sum(axis=0)
|
|
col_r = col[x_min_local:]
|
|
hot = np.where(col_r > col_thr)[0]
|
|
if len(hot) == 0:
|
|
return None
|
|
br = np.where(np.diff(hot) > 12)[0]
|
|
segs = [s for s in np.split(hot, br + 1) if len(s) > 10]
|
|
seg = max(segs, key=lambda s: col_r[s].sum())
|
|
segL = x_min_local + int(seg[0]); segR = x_min_local + int(seg[-1])
|
|
ys, xs = np.where((reg[:, segL:segR + 1] < 95))
|
|
if len(xs) == 0:
|
|
return None
|
|
return PHOTO_LEFT + int(round(np.average(xs + segL)))
|
|
|
|
|
|
def detect_handle(img, lo=80, hi=340):
|
|
band = img[932:968, lo:hi]
|
|
col = (band < 200).sum(axis=0).astype(float)
|
|
hot = np.where(col > 12)[0]
|
|
if len(hot) == 0:
|
|
return None
|
|
br = np.where(np.diff(hot) > 6)[0]
|
|
cands = [s for s in np.split(hot, br + 1) if 10 <= len(s) <= 80]
|
|
if not cands:
|
|
return None
|
|
seg = max(cands, key=lambda s: col[s].max())
|
|
w = col[seg]
|
|
return lo + int(round(np.average(seg, weights=w)))
|
|
|
|
|
|
def track_handle(img, center):
|
|
"""预测跟踪: 在 center±70 内找最密紧簇(左移窗跟随左侧按钮)。"""
|
|
lo = max(40, center - 70); hi = min(940, center + 70)
|
|
return detect_handle(img, lo=lo, hi=hi) or center
|
|
|
|
|
|
def piece_blob(home, cur):
|
|
d = np.abs(photo(cur).astype(np.int16) - photo(home).astype(np.int16))
|
|
mask = d > 45; col = mask.sum(axis=0); hot = np.where(col > 6)[0]
|
|
if len(hot) == 0:
|
|
return None, None, None
|
|
w = np.where(col > 6, col, 0); br = np.where(np.diff(hot) > 12)[0]
|
|
c = [s for s in np.split(hot, br + 1) if len(s) > 12]
|
|
if not c:
|
|
return None, None, None
|
|
seg = max(c, key=lambda s: s[-1]) # rightmost = piece current
|
|
cx = PHOTO_LEFT + int(round(np.average(seg, weights=w[seg])))
|
|
return cx, PHOTO_LEFT + int(seg[0]), PHOTO_LEFT + int(seg[-1])
|
|
|
|
|
|
def top_activity():
|
|
out = adb_text("shell", "dumpsys", "activity", "activities")
|
|
return next((l.strip() for l in out.splitlines() if "topResumedActivity" in l), "(none)")
|
|
|
|
|
|
def main():
|
|
home = cap()
|
|
notch = detect_notch(home)
|
|
hh = detect_handle(home)
|
|
print(f"[init] notch={notch} handle_home={hh} HOME_TRUE={HOME_TRUE:.0f} R={R}", flush=True)
|
|
if notch is None or hh is None:
|
|
print("[!] 检测失败"); return 2
|
|
|
|
target = int(round(hh + (notch - HOME_TRUE) / R))
|
|
print(f"[plan] target = {hh} + ({notch}-{HOME_TRUE:.0f})/{R} = {target}", flush=True)
|
|
|
|
adb_text("logcat", "-c")
|
|
reached = None
|
|
p = subprocess.Popen(["adb", "shell", "input", "swipe",
|
|
str(hh), str(TRACK_Y), str(target), str(TRACK_Y), "3000"])
|
|
hc = hh; t0 = time.monotonic()
|
|
traj = []
|
|
for _ in range(9):
|
|
time.sleep(0.32)
|
|
c = cap()
|
|
hc = track_handle(c, hc)
|
|
cx, bl, br = piece_blob(home, c)
|
|
traj.append((round(time.monotonic() - t0, 1), hc, cx, br))
|
|
if cx is not None:
|
|
reached = cx
|
|
p.wait()
|
|
print("[traj] (t, handle, piece_cx, piece_right):", flush=True)
|
|
for t in traj:
|
|
print(f" t={t[0]} h={t[1]} pcx={t[2]} pr={t[3]}", flush=True)
|
|
print(f"[traj] piece max_cx reached ~ {reached} (notch={notch}, diff={reached-notch if reached else '?'})",
|
|
flush=True)
|
|
|
|
time.sleep(4.0)
|
|
aft = cap()
|
|
after = top_activity()
|
|
closed = "KwaiWebViewActivity" not in after
|
|
changed = not np.array_equal(photo(home), photo(aft))
|
|
print(f"[post] closed={closed} photo_changed={changed} top={after[:60]}", flush=True)
|
|
|
|
code = None
|
|
hits = []
|
|
for ln in adb_text("logcat", "-d").splitlines():
|
|
low = ln.lower()
|
|
if any(k in low for k in ("350014", "350002", "350005", "\"result\"", "ksecretapi", "captcha/sliding", "verify")):
|
|
hits.append(ln[:170])
|
|
for tok in ("350014", "350002", "350005"):
|
|
if tok in ln:
|
|
code = tok
|
|
print("----- logcat hits -----", flush=True)
|
|
for h in hits[:25]:
|
|
print(h, flush=True)
|
|
|
|
if closed:
|
|
print("\n>>> VERDICT: PASS (result=1) — 真机 WebView 指纹过验证! 路径=真机/模拟器。", flush=True)
|
|
elif code == "350014":
|
|
print("\n>>> VERDICT: 350014 — 号码被永久标记, 真机正确落点也过不去 => 只能换号。", flush=True)
|
|
elif code == "350002":
|
|
print("\n>>> VERDICT: 350002 — 触发 verify 但缺口错位, 微调 target。", flush=True)
|
|
elif changed:
|
|
print("\n>>> VERDICT: 刷新(无码) — 触发了 verify, 落点接近, 待 logcat 细节。", flush=True)
|
|
else:
|
|
print(f"\n>>> VERDICT: 静默回弹(nochange) — 落点 max={reached} 仍错位缺口{notch}, "
|
|
f"或 bot 检测。检查轨迹 max 是否到达缺口。", flush=True)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|