This commit is contained in:
Your Name
2026-08-18 17:25:22 +08:00
parent 1048b9ba29
commit f8c78739e7
261 changed files with 16253 additions and 7399 deletions
+98 -98
View File
@@ -1,98 +1,98 @@
"""为什么每次 [待回复恢复] 之后都跟着 [页面校验] 点击前会话列表已变化?
定位器 _pending_rows_on_page 找到了行,click_session 用同一个 y 复核却不过。
两边的判据不一样,这里把 click_session 的三个子条件拆开单独打印,看是哪一个
把点击否掉的。纯读,不点击、不发送。
"""
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import wechat_bot as bot_module # noqa: E402
from wechat_bot import ( # noqa: E402
_AVATAR_FP_BYTES,
_SESSION_FP_BYTES,
_LEGACY_SESSION_FP_BYTES,
)
def hamming(a: bytes, b: bytes) -> int:
if len(a) != len(b) or not a:
return -1
return (int.from_bytes(a, "big") ^ int.from_bytes(b, "big")).bit_count()
def main() -> None:
bot = bot_module.WeChatBot()
bot.safe_window_mode = True
bot.auto_activate_window = False
if not bot.connect(activate=False, wait_if_missing=False):
print("[-] 未挂载到企业微信窗口")
return
pending = dict(getattr(bot, "_pending_reply_sessions", {}))
print(f"待回复任务 {len(pending)}\n")
for key, state in pending.items():
target_fp = bytes.fromhex(key)
print("=" * 72)
print(f"任务 {key[:16]}… send_state={state.get('send_state')!r}")
found = bot._find_pending_session(target_fp)
if found is None:
incomplete = getattr(bot, "_pending_scan_incomplete", False)
print(
f" _find_pending_session: 没找到"
f"(扫描{'被时间预算截断,结论不可信' if incomplete else '已走完全部分页,确实不存在'}"
)
continue
_page, row_center = found
print(f" _find_pending_session: 命中 row_center={row_center}")
latest = bot.capture_session_list()
if latest is None:
print(" 重新截取列表失败")
continue
legacy = len(target_fp) == _LEGACY_SESSION_FP_BYTES
if legacy:
actual = bot._legacy_session_fingerprint(latest, row_center, row_center=True)
fp_ok = actual == target_fp
else:
actual = bot._session_fingerprint(latest, row_center, row_center=True)
fp_ok = bot._session_fp_matches(actual, target_fp)
print(f" 条件1 指纹匹配 = {fp_ok}")
if actual and len(actual) == _SESSION_FP_BYTES and not legacy:
name_ok, name_d = bot._name_fp_matches(
actual[_AVATAR_FP_BYTES:], target_fp[_AVATAR_FP_BYTES:]
)
print(
f" 头像距离={hamming(actual[:_AVATAR_FP_BYTES], target_fp[:_AVATAR_FP_BYTES])} "
f"(容差 {bot._FP_HAMMING_TOL_NAMED}/{bot._FP_HAMMING_TOL}) "
f"名称匹配={name_ok} 距离={name_d} "
f"名称有效={bot._name_fp_is_substantive(actual[_AVATAR_FP_BYTES:])}"
)
pair = tuple(sorted((actual.hex(), target_fp.hex())))
print(f" 别名已登记={pair in getattr(bot, '_live_session_fp_aliases', set())}")
pending_state = bot._pending_reply_state(target_fp)
allow_flat = bool(
(pending_state or {}).get("requires_visual_proof", False)
or bot._flat_session_is_known(target_fp)
)
allow_unrounded = bool(
len(target_fp) == _SESSION_FP_BYTES
and (pending_state or {}).get("requires_visual_proof", False)
and not bot._flat_session_rejected(target_fp)
)
shape_ok = bot._is_real_conversation(
latest, int(row_center), quiet=True, row_center=True, allow_flat=allow_flat
)
print(f" 条件2 会话形态 = {shape_ok} (allow_flat={allow_flat})")
print(f" 条件3 兜底放行 = {allow_unrounded}")
verdict = fp_ok and (shape_ok or allow_unrounded)
print(f" → click_session 会{'放行' if verdict else '取消这次点击'}")
if __name__ == "__main__":
main()
"""为什么每次 [待回复恢复] 之后都跟着 [页面校验] 点击前会话列表已变化?
定位器 _pending_rows_on_page 找到了行,click_session 用同一个 y 复核却不过。
两边的判据不一样,这里把 click_session 的三个子条件拆开单独打印,看是哪一个
把点击否掉的。纯读,不点击、不发送。
"""
import json
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import wechat_bot as bot_module # noqa: E402
from wechat_bot import ( # noqa: E402
_AVATAR_FP_BYTES,
_SESSION_FP_BYTES,
_LEGACY_SESSION_FP_BYTES,
)
def hamming(a: bytes, b: bytes) -> int:
if len(a) != len(b) or not a:
return -1
return (int.from_bytes(a, "big") ^ int.from_bytes(b, "big")).bit_count()
def main() -> None:
bot = bot_module.WeChatBot()
bot.safe_window_mode = True
bot.auto_activate_window = False
if not bot.connect(activate=False, wait_if_missing=False):
print("[-] 未挂载到企业微信窗口")
return
pending = dict(getattr(bot, "_pending_reply_sessions", {}))
print(f"待回复任务 {len(pending)}\n")
for key, state in pending.items():
target_fp = bytes.fromhex(key)
print("=" * 72)
print(f"任务 {key[:16]}… send_state={state.get('send_state')!r}")
found = bot._find_pending_session(target_fp)
if found is None:
incomplete = getattr(bot, "_pending_scan_incomplete", False)
print(
f" _find_pending_session: 没找到"
f"(扫描{'被时间预算截断,结论不可信' if incomplete else '已走完全部分页,确实不存在'}"
)
continue
_page, row_center = found
print(f" _find_pending_session: 命中 row_center={row_center}")
latest = bot.capture_session_list()
if latest is None:
print(" 重新截取列表失败")
continue
legacy = len(target_fp) == _LEGACY_SESSION_FP_BYTES
if legacy:
actual = bot._legacy_session_fingerprint(latest, row_center, row_center=True)
fp_ok = actual == target_fp
else:
actual = bot._session_fingerprint(latest, row_center, row_center=True)
fp_ok = bot._session_fp_matches(actual, target_fp)
print(f" 条件1 指纹匹配 = {fp_ok}")
if actual and len(actual) == _SESSION_FP_BYTES and not legacy:
name_ok, name_d = bot._name_fp_matches(
actual[_AVATAR_FP_BYTES:], target_fp[_AVATAR_FP_BYTES:]
)
print(
f" 头像距离={hamming(actual[:_AVATAR_FP_BYTES], target_fp[:_AVATAR_FP_BYTES])} "
f"(容差 {bot._FP_HAMMING_TOL_NAMED}/{bot._FP_HAMMING_TOL}) "
f"名称匹配={name_ok} 距离={name_d} "
f"名称有效={bot._name_fp_is_substantive(actual[_AVATAR_FP_BYTES:])}"
)
pair = tuple(sorted((actual.hex(), target_fp.hex())))
print(f" 别名已登记={pair in getattr(bot, '_live_session_fp_aliases', set())}")
pending_state = bot._pending_reply_state(target_fp)
allow_flat = bool(
(pending_state or {}).get("requires_visual_proof", False)
or bot._flat_session_is_known(target_fp)
)
allow_unrounded = bool(
len(target_fp) == _SESSION_FP_BYTES
and (pending_state or {}).get("requires_visual_proof", False)
and not bot._flat_session_rejected(target_fp)
)
shape_ok = bot._is_real_conversation(
latest, int(row_center), quiet=True, row_center=True, allow_flat=allow_flat
)
print(f" 条件2 会话形态 = {shape_ok} (allow_flat={allow_flat})")
print(f" 条件3 兜底放行 = {allow_unrounded}")
verdict = fp_ok and (shape_ok or allow_unrounded)
print(f" → click_session 会{'放行' if verdict else '取消这次点击'}")
if __name__ == "__main__":
main()