From 2b03a81a8f5675d9105783d923a67e06fdc7c1a7 Mon Sep 17 00:00:00 2001 From: ylytdeng Date: Sat, 7 Mar 2026 00:53:48 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=88=86=E9=9A=94=E7=AC=A6=E4=B8=BA=E6=AD=A3=E6=96=9C=E6=9D=A0?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=20macOS/Linux=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit all_keys.json 中的 key 统一使用 `/` 作为路径分隔符, 消除 Windows 反斜杠硬编码,确保跨平台兼容。 涉及文件: find_all_keys.py, decrypt_db.py, monitor.py, monitor_web.py, mcp_server.py, decode_image.py, latency_test.py Fixes #17 Co-Authored-By: Claude Opus 4.6 --- decode_image.py | 2 +- decrypt_db.py | 6 +++--- find_all_keys.py | 2 +- latency_test.py | 2 +- mcp_server.py | 12 ++++++------ monitor.py | 2 +- monitor_web.py | 22 +++++++++++----------- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/decode_image.py b/decode_image.py index 0eb510c..773446f 100644 --- a/decode_image.py +++ b/decode_image.py @@ -313,7 +313,7 @@ class ImageResolver: def get_image_md5(self, local_id): """通过 local_id 查 message_resource.db 获取图片文件 MD5""" - path = self.cache.get("message\\message_resource.db") + path = self.cache.get("message/message_resource.db") if not path: return None diff --git a/decrypt_db.py b/decrypt_db.py index d1be29a..430fffd 100644 --- a/decrypt_db.py +++ b/decrypt_db.py @@ -129,7 +129,7 @@ def main(): for f in files: if f.endswith('.db') and not f.endswith('-wal') and not f.endswith('-shm'): path = os.path.join(root, f) - rel = os.path.relpath(path, DB_DIR) + rel = os.path.relpath(path, DB_DIR).replace('\\', '/') sz = os.path.getsize(path) db_files.append((rel, path, sz)) @@ -142,8 +142,8 @@ def main(): total_bytes = 0 for rel, path, sz in db_files: - # 用反斜杠格式查找key (json中的key是Windows路径) - rel_key = rel.replace('/', '\\') + # 统一用正斜杠查找key + rel_key = rel.replace('\\', '/') if rel_key not in keys: print(f"SKIP: {rel} (无密钥)") failed += 1 diff --git a/find_all_keys.py b/find_all_keys.py index 9933337..ff7f70a 100644 --- a/find_all_keys.py +++ b/find_all_keys.py @@ -100,7 +100,7 @@ def main(): for f in files: if f.endswith('.db') and not f.endswith('-wal') and not f.endswith('-shm'): path = os.path.join(root, f) - rel = os.path.relpath(path, DB_DIR) + rel = os.path.relpath(path, DB_DIR).replace('\\', '/') sz = os.path.getsize(path) if sz < PAGE_SZ: continue diff --git a/latency_test.py b/latency_test.py index b1d4603..e5f2fae 100644 --- a/latency_test.py +++ b/latency_test.py @@ -17,7 +17,7 @@ DECRYPTED = os.path.join(_cfg["decrypted_dir"], "session", "session.db") with open(KEYS_FILE) as f: keys = json.load(f) -enc_key = bytes.fromhex(keys["session\\session.db"]["enc_key"]) +enc_key = bytes.fromhex(keys["session/session.db"]["enc_key"]) session_db = os.path.join(DB_DIR, "session", "session.db") wal_path = session_db + "-wal" diff --git a/mcp_server.py b/mcp_server.py index f4261aa..f8f2fa8 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -149,7 +149,7 @@ class DBCache: tmp_path = info["path"] if not os.path.exists(tmp_path): continue - rel_path = rel_key.replace('\\', os.sep) + rel_path = rel_key.replace('/', os.sep) db_path = os.path.join(DB_DIR, rel_path) wal_path = db_path + "-wal" try: @@ -177,7 +177,7 @@ class DBCache: def get(self, rel_key): if rel_key not in ALL_KEYS: return None - rel_path = rel_key.replace('\\', os.sep) + rel_path = rel_key.replace('/', os.sep) db_path = os.path.join(DB_DIR, rel_path) wal_path = db_path + "-wal" if not os.path.exists(db_path): @@ -248,7 +248,7 @@ def get_contact_names(): pass # 实时解密 - path = _cache.get("contact\\contact.db") + path = _cache.get("contact/contact.db") if path: try: _contact_names, _contact_full = _load_contacts_from(path) @@ -332,7 +332,7 @@ def _parse_message_content(content, local_type, is_group): # 消息 DB 的 rel_keys(排除 fts/resource/media/biz) MSG_DB_KEYS = sorted([ k for k in ALL_KEYS - if k.startswith("message\\message_") and k.endswith(".db") + if k.startswith("message/message_") and k.endswith(".db") and "fts" not in k and "resource" not in k ]) @@ -379,7 +379,7 @@ def get_recent_sessions(limit: int = 20) -> str: Args: limit: 返回的会话数量,默认20 """ - path = _cache.get("session\\session.db") + path = _cache.get("session/session.db") if not path: return "错误: 无法解密 session.db" @@ -635,7 +635,7 @@ def get_new_messages() -> str: """获取自上次调用以来的新消息。首次调用返回最近的会话状态。""" global _last_check_state - path = _cache.get("session\\session.db") + path = _cache.get("session/session.db") if not path: return "错误: 无法解密 session.db" diff --git a/monitor.py b/monitor.py index 83b6265..3f503a4 100644 --- a/monitor.py +++ b/monitor.py @@ -151,7 +151,7 @@ def main(): with open(KEYS_FILE) as f: keys = json.load(f) - session_key_info = keys.get("session\\session.db") + session_key_info = keys.get("session/session.db") if not session_key_info: print("[ERROR] 找不到session.db的密钥") sys.exit(1) diff --git a/monitor_web.py b/monitor_web.py index d1de7b8..2c0c81b 100644 --- a/monitor_web.py +++ b/monitor_web.py @@ -62,7 +62,7 @@ def _build_emoji_lookup(keys_dict): """从 emoticon.db 构建 emoji md5 → URL 映射(直接解密,不走 cache)""" global _emoji_lookup, _emoji_keys_dict, _emoji_last_refresh _emoji_keys_dict = keys_dict - key_info = keys_dict.get("emoticon\\emoticon.db") + key_info = keys_dict.get("emoticon/emoticon.db") if not key_info: print("[emoji] 无 emoticon.db key,跳过", flush=True) return @@ -260,7 +260,7 @@ class MonitorDBCache: lock = self._get_lock(rel_key) with lock: enc_key = bytes.fromhex(self.keys[rel_key]["enc_key"]) - rel_path = rel_key.replace('\\', os.sep) + rel_path = rel_key.replace('/', os.sep) db_path = os.path.join(DB_DIR, rel_path) wal_path = db_path + "-wal" @@ -273,7 +273,7 @@ class MonitorDBCache: except OSError: return None - out_name = rel_key.replace('\\', '_') + out_name = rel_key.replace('/', '_') out_path = os.path.join(self.tmp_dir, out_name) prev = self._state.get(rel_key) @@ -313,7 +313,7 @@ def build_username_db_map(): # 先获取每个 DB 的 mtime 用于排序 db_mtimes = {} for i in range(5): - rel_key = f"message\\message_{i}.db" + rel_key = f"message/message_{i}.db" db_path = os.path.join(DB_DIR, "message", f"message_{i}.db") try: db_mtimes[rel_key] = os.path.getmtime(db_path) @@ -326,7 +326,7 @@ def build_username_db_map(): db_path = os.path.join(decrypted_msg_dir, f"message_{i}.db") if not os.path.exists(db_path): continue - rel_key = f"message\\message_{i}.db" + rel_key = f"message/message_{i}.db" try: conn = sqlite3.connect(f"file:{db_path}?mode=ro", uri=True) for row in conn.execute("SELECT user_name FROM Name2Id").fetchall(): @@ -595,7 +595,7 @@ class SessionMonitor: # local_id 不全局唯一,需要同时匹配 create_time file_md5 = None for _try in range(2): - res_path = self.db_cache.get("message\\message_resource.db") + res_path = self.db_cache.get("message/message_resource.db") if not res_path: return None try: @@ -620,7 +620,7 @@ class SessionMonitor: except Exception as e: if 'malformed' in str(e) and _try == 0: print(f" [img] resource DB malformed, 强制刷新...", flush=True) - self.db_cache.invalidate("message\\message_resource.db") + self.db_cache.invalidate("message/message_resource.db") continue print(f" [img] 查询 message_resource 失败: {e}", flush=True) return None @@ -756,7 +756,7 @@ class SessionMonitor: if db_key not in self.db_cache.keys: return [] enc_key = bytes.fromhex(self.db_cache.keys[db_key]["enc_key"]) - rel_path = db_key.replace('\\', os.sep) + rel_path = db_key.replace('/', os.sep) db_path = os.path.join(DB_DIR, rel_path) wal_path = db_path + "-wal" if not os.path.exists(db_path): @@ -1877,7 +1877,7 @@ def main(): with open(KEYS_FILE) as f: keys = json.load(f) - enc_key = bytes.fromhex(keys["session\\session.db"]["enc_key"]) + enc_key = bytes.fromhex(keys["session/session.db"]["enc_key"]) session_db = os.path.join(DB_DIR, "session", "session.db") print("加载联系人...", flush=True) @@ -1910,9 +1910,9 @@ def main(): def _warmup(): try: t0 = time.perf_counter() - warmup_keys = ["message\\message_resource.db"] + warmup_keys = ["message/message_resource.db"] for i in range(5): - k = f"message\\message_{i}.db" + k = f"message/message_{i}.db" if k in keys: warmup_keys.append(k) for k in warmup_keys: