90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
"""修复 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()
|