ksjsb/tools/phone_solve_v7.py
2026-07-30 20:25:56 +08:00

163 lines
6.1 KiB
Python

"""v7: 迭代收敛落点。每次 swipe 测 piece 最大位置 => 反馈调整 target => 找 close(PASS)。
R=1.0 (1:1), home_true=183。每次:
target = handle_home + (notch_center - 183) + offset
慢 swipe, 轨迹测 max piece_right (落点右缘, 比 cx 稳定)
期望 piece_right = notch_center + 84 (拼图块居中缺口时的右缘)
err = (notch_center+84) - max_piece_right ; offset += err
观察:
close => result=1 (PASS! 真机指纹过, 号码未标记 => 路径=真机/模拟器)
refresh 且 |err|<=7 => 正确落点仍刷新 => 350014 (号码被标记/指纹被卡, 真机也过不去)
refresh 且 |err|>7 => 350002 (错位, 继续收敛)
最多 7 次。
"""
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
R = 1.0
TRACK_Y = 950
PIECE_HALF = 84 # cutPic 122 * 1.379 / 2
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, 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, None
center = PHOTO_LEFT + int(round(np.average(xs + segL)))
return center, PHOTO_LEFT + int(segR) # center, right_edge
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())
return lo + int(round(np.average(seg, weights=col[seg])))
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])
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 attempt(home, notch_c, offset):
hh = detect_handle(home)
if hh is None or notch_c is None:
return None
target = int(round(hh + (notch_c - HOME_TRUE) / R + offset))
desired_right = notch_c + PIECE_HALF
p = subprocess.Popen(["adb", "shell", "input", "swipe",
str(hh), str(TRACK_Y), str(target), str(TRACK_Y), "2600"])
max_right = 0; max_cx = 0
for _ in range(7):
time.sleep(0.30)
c = cap()
cx, bl, br = piece_blob(home, c)
if br and br > max_right:
max_right = br; max_cx = cx
p.wait()
time.sleep(3.5)
aft = cap()
after = top_activity()
closed = "KwaiWebViewActivity" not in after
changed = not np.array_equal(photo(home), photo(aft))
err = desired_right - max_right
print(f" target={target} max_piece_right={max_right} (want {desired_right}) "
f"err={err:+d} => closed={closed} changed={changed}", flush=True)
return dict(closed=closed, changed=changed, err=err, max_cx=max_cx, aft=aft)
def main():
offset = 0.0
converged_refresh = False
for k in range(7):
home = cap()
notch_c, notch_r = detect_notch(home)
print(f"[try {k}] notch_center={notch_c} notch_right={notch_r} offset={offset:+.0f}", flush=True)
if notch_c is None:
print(" [!] 无缺口, 等待"); time.sleep(2); continue
r = attempt(home, notch_c, offset)
if r is None:
continue
if r["closed"]:
print(f"\n>>> VERDICT: PASS (result=1) on try {k}! 真机 WebView 指纹过验证。", flush=True)
print(">>> 号码未标记 + 真机指纹OK => 可行路径 = 真机/模拟器 WebView。", flush=True)
return 0
if not r["changed"]:
# 静默回弹 = 落点太远(错位大), offset 调整幅度加大
print(" (静默回弹: 落点偏离过大, 增大调整)", flush=True)
offset += 25 if offset >= 0 else -25
continue
# 刷新 => verify 触发
if abs(r["err"]) <= 7:
converged_refresh = True
print(f" *** 正确落点(err={r['err']:+d}≤7) 却刷新 => 350014 候选 ***", flush=True)
# 再确认一次: 微调看是否仍刷新
offset += r["err"]
continue
offset += r["err"] # 收敛
print("\n>>> 7 次未 PASS。", flush=True)
if converged_refresh:
print(">>> VERDICT: 正确落点(err≤7)仍刷新 => 350014 (号码被永久标记或真机指纹仍被卡)。", flush=True)
print(">>> 真机也过不去 => 纯算 + 真机 WebView 都不行, 只能换号(新号不触发验证码)。", flush=True)
else:
print(">>> VERDICT: 未收敛到正确落点(每次都错位>7)。需更多迭代或重新标定。", flush=True)
return 0
if __name__ == "__main__":
raise SystemExit(main())