"""实拍会话列表,逐行报告名称字形哈希的置位数,并导出取样带截图。 用途:定位为什么某些联系人的 256 位名称哈希退化成全零。 """ import os import sys import numpy as np sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import cv2 # noqa: E402 from wechat_bot import WeChatBot, _NAME_FP_BYTES # noqa: E402 OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "namehash") def main() -> None: os.makedirs(OUT, exist_ok=True) bot = WeChatBot() if not bot.connect(activate=False): raise SystemExit("挂载企业微信失败") img = bot.capture_session_list() if img is None: raise SystemExit("截取会话列表失败") cv2.imwrite(os.path.join(OUT, "list.png"), img) scale = max(0.75, float(bot.scale or 1.0)) print(f"列表尺寸 {img.shape[1]}x{img.shape[0]} scale={scale}") centers = bot._avatar_row_centers(img) print(f"检出 {len(centers)} 行头像,中心={centers}") # 名称哈希实际取样的两条带(与 _session_name_fingerprint 保持一致) bands = { "prefix": (60 * scale, 84 * scale, -8 * scale, -2 * scale), "main": (80 * scale, img.shape[1] - 60 * scale, -21 * scale, -2 * scale), } for idx, raw_center in enumerate(centers): y_c = bot._avatar_anchor(img, raw_center) name_fp = bot._session_name_fingerprint(img, y_c, row_center=True) avatar_fp = bot._session_fingerprint(img, y_c, row_center=True) bits = int.from_bytes(name_fp or b"", "big").bit_count() head = (avatar_fp or b"")[:8].hex() print( f"行{idx}: 锚点 {raw_center}->{y_c} 名称置位={bits:3d}/256 " f"长度={len(name_fp or b'')} 头像={head}" ) for tag, (x1, x2, dy1, dy2) in bands.items(): xa, xb = int(max(0, x1)), int(min(img.shape[1], x2)) ya, yb = int(max(0, y_c + dy1)), int(min(img.shape[0], y_c + dy2)) crop = img[ya:yb, xa:xb] if crop.size: gray = crop[:, :, :3].astype(np.float32).mean(axis=2) background = float(np.median(gray)) ink = float((np.abs(gray - background) >= 16.0).mean()) print( f" {tag:6s} x[{xa},{xb}) y[{ya},{yb}) " f"底色={background:.0f} 墨迹占比={ink:.3f}" ) cv2.imwrite(os.path.join(OUT, f"row{idx}_{tag}.png"), crop) else: print(f" {tag:6s} 取样区为空 x[{xa},{xb}) y[{ya},{yb})") print(f"\n截图已写入 {OUT}") print(f"_NAME_FP_BYTES={_NAME_FP_BYTES}") # 不同联系人之间名称哈希的距离下界,决定容差能开到多大 print("\n当前列表内不同行的名称哈希距离:") fps = [] for idx, raw_center in enumerate(centers): y_c = bot._avatar_anchor(img, raw_center) fps.append((idx, bot._session_name_fingerprint(img, y_c, row_center=True))) worst = 999 for i in range(len(fps)): for j in range(i + 1, len(fps)): a, b = fps[i][1], fps[j][1] if len(a) != _NAME_FP_BYTES or len(b) != _NAME_FP_BYTES: continue dist = (int.from_bytes(a, "big") ^ int.from_bytes(b, "big")).bit_count() worst = min(worst, dist) print(f" 行{fps[i][0]} vs 行{fps[j][0]}: {dist}") print(f"不同联系人名称距离下界 = {worst}") if __name__ == "__main__": main()