"""修复 wechat_bot.py 的字节级损坏。 损坏形态:个别字节被就地替换成 0x3f('?'),字节长度不变,行结构完好。 因此可以用 git HEAD 版本做参照:把损坏行里的 0x3f 当通配符,在 HEAD 里找 长度相同、其余字节全部一致的唯一候选行来还原。 默认只报告不写入;加 --write 才真正落盘。 """ import subprocess import sys def load_head() -> bytes: return subprocess.run( ["git", "show", "HEAD:wechat_rpa/wechat_bot.py"], capture_output=True, check=True, cwd="..", ).stdout def is_valid(line: bytes) -> bool: try: line.decode("utf-8") return True except UnicodeDecodeError: return False def candidates(broken: bytes, pool: dict) -> list: """在同长度的候选里找出「除 0x3f 位置外完全一致」的行。""" found = [] for other in pool.get(len(broken), ()): # 长度相同才可能是同一行 if all( b == o or b == 0x3F for b, o in zip(broken, other) ): found.append(other) return found def main(): write = "--write" in sys.argv current = open("wechat_bot.py", "rb").read() head = load_head() cur_lines = current.split(b"\n") head_lines = head.split(b"\n") pool = {} for line in head_lines: pool.setdefault(len(line), []).append(line) broken_idx = [i for i, line in enumerate(cur_lines) if not is_valid(line)] print(f"总行数 {len(cur_lines)},损坏行 {len(broken_idx)}") repaired = list(cur_lines) fixed = unresolved = ambiguous = 0 unresolved_lines = [] for i in broken_idx: found = set(candidates(cur_lines[i], pool)) if len(found) == 1: repaired[i] = found.pop() fixed += 1 elif len(found) > 1: ambiguous += 1 unresolved_lines.append((i, cur_lines[i], len(found))) else: unresolved += 1 unresolved_lines.append((i, cur_lines[i], 0)) print(f"可唯一还原 {fixed},歧义 {ambiguous},HEAD 里找不到 {unresolved}") if unresolved_lines: print("\n需要人工确认的行(最多列 40 条):") for i, line, n in unresolved_lines[:40]: print(f" 第 {i + 1} 行 候选={n}: {line.decode('utf-8', 'replace')!r}") if not write: print("\n(只报告,未写入。加 --write 才落盘)") return out = b"\n".join(repaired) open("wechat_bot.py", "wb").write(out) print(f"\n已写回 {len(out)} 字节") if __name__ == "__main__": main()