refactor(find_all_keys): extract shared key scan logic
This commit is contained in:
112
monitor_web.py
112
monitor_web.py
@@ -13,11 +13,11 @@ from datetime import datetime
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
from socketserver import ThreadingMixIn
|
||||
from Crypto.Cipher import AES
|
||||
import urllib.parse
|
||||
import glob as glob_mod
|
||||
import zstandard as zstd
|
||||
from decode_image import extract_md5_from_packed_info, decrypt_dat_file, is_v2_format
|
||||
from key_utils import get_key_info, strip_key_metadata
|
||||
import urllib.parse
|
||||
import glob as glob_mod
|
||||
import zstandard as zstd
|
||||
from decode_image import extract_md5_from_packed_info, decrypt_dat_file, is_v2_format
|
||||
from key_utils import get_key_info, strip_key_metadata
|
||||
|
||||
_zstd_dctx = zstd.ZstdDecompressor()
|
||||
|
||||
@@ -59,14 +59,14 @@ _emoji_lookup_lock = threading.Lock()
|
||||
_emoji_keys_dict = None # 保存 keys 引用供刷新用
|
||||
_emoji_last_refresh = 0
|
||||
|
||||
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 = get_key_info(keys_dict, os.path.join("emoticon", "emoticon.db"))
|
||||
if not key_info:
|
||||
print("[emoji] 无 emoticon.db key,跳过", flush=True)
|
||||
return
|
||||
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 = get_key_info(keys_dict, os.path.join("emoticon", "emoticon.db"))
|
||||
if not key_info:
|
||||
print("[emoji] 无 emoticon.db key,跳过", flush=True)
|
||||
return
|
||||
|
||||
src = os.path.join(DB_DIR, "emoticon", "emoticon.db")
|
||||
if not os.path.exists(src):
|
||||
@@ -253,18 +253,18 @@ class MonitorDBCache:
|
||||
with lock:
|
||||
self._state.pop(rel_key, None)
|
||||
|
||||
def get(self, rel_key):
|
||||
"""返回解密后的临时文件路径,mtime 变化时自动重新解密"""
|
||||
key_info = get_key_info(self.keys, rel_key)
|
||||
if not key_info:
|
||||
return None
|
||||
|
||||
lock = self._get_lock(rel_key)
|
||||
with lock:
|
||||
enc_key = bytes.fromhex(key_info["enc_key"])
|
||||
rel_path = rel_key.replace('\\', '/').replace('/', os.sep)
|
||||
db_path = os.path.join(DB_DIR, rel_path)
|
||||
wal_path = db_path + "-wal"
|
||||
def get(self, rel_key):
|
||||
"""返回解密后的临时文件路径,mtime 变化时自动重新解密"""
|
||||
key_info = get_key_info(self.keys, rel_key)
|
||||
if not key_info:
|
||||
return None
|
||||
|
||||
lock = self._get_lock(rel_key)
|
||||
with lock:
|
||||
enc_key = bytes.fromhex(key_info["enc_key"])
|
||||
rel_path = rel_key.replace('\\', '/').replace('/', os.sep)
|
||||
db_path = os.path.join(DB_DIR, rel_path)
|
||||
wal_path = db_path + "-wal"
|
||||
|
||||
if not os.path.exists(db_path):
|
||||
return None
|
||||
@@ -275,8 +275,8 @@ class MonitorDBCache:
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
out_name = rel_key.replace('\\', '_').replace('/', '_')
|
||||
out_path = os.path.join(self.tmp_dir, out_name)
|
||||
out_name = rel_key.replace('\\', '_').replace('/', '_')
|
||||
out_path = os.path.join(self.tmp_dir, out_name)
|
||||
|
||||
prev = self._state.get(rel_key)
|
||||
|
||||
@@ -315,7 +315,7 @@ def build_username_db_map():
|
||||
# 先获取每个 DB 的 mtime 用于排序
|
||||
db_mtimes = {}
|
||||
for i in range(5):
|
||||
rel_key = f"message\\message_{i}.db"
|
||||
rel_key = os.path.join("message", f"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)
|
||||
@@ -328,7 +328,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 = os.path.join("message", f"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():
|
||||
@@ -597,7 +597,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(os.path.join("message", "message_resource.db"))
|
||||
if not res_path:
|
||||
return None
|
||||
try:
|
||||
@@ -622,7 +622,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(os.path.join("message", "message_resource.db"))
|
||||
continue
|
||||
print(f" [img] 查询 message_resource 失败: {e}", flush=True)
|
||||
return None
|
||||
@@ -753,14 +753,14 @@ class SessionMonitor:
|
||||
if attempt < 2:
|
||||
time.sleep(delays[attempt])
|
||||
|
||||
def _fresh_decrypt_query(self, db_key, table_name, prev_ts, curr_ts):
|
||||
"""独立解密 message DB 到临时文件并查询,避免共享缓存竞态"""
|
||||
key_info = get_key_info(self.db_cache.keys, db_key)
|
||||
if not key_info:
|
||||
return []
|
||||
enc_key = bytes.fromhex(key_info["enc_key"])
|
||||
rel_path = db_key.replace('\\', '/').replace('/', os.sep)
|
||||
db_path = os.path.join(DB_DIR, rel_path)
|
||||
def _fresh_decrypt_query(self, db_key, table_name, prev_ts, curr_ts):
|
||||
"""独立解密 message DB 到临时文件并查询,避免共享缓存竞态"""
|
||||
key_info = get_key_info(self.db_cache.keys, db_key)
|
||||
if not key_info:
|
||||
return []
|
||||
enc_key = bytes.fromhex(key_info["enc_key"])
|
||||
rel_path = db_key.replace('\\', '/').replace('/', os.sep)
|
||||
db_path = os.path.join(DB_DIR, rel_path)
|
||||
wal_path = db_path + "-wal"
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
@@ -1875,17 +1875,17 @@ class ThreadedServer(ThreadingMixIn, HTTPServer):
|
||||
def main():
|
||||
print("=" * 60, flush=True)
|
||||
print(" 微信实时监听 (WAL增量 + SSE推送)", flush=True)
|
||||
print("=" * 60, flush=True)
|
||||
|
||||
with open(KEYS_FILE) as f:
|
||||
keys = strip_key_metadata(json.load(f))
|
||||
|
||||
session_key_info = get_key_info(keys, os.path.join("session", "session.db"))
|
||||
if not session_key_info:
|
||||
print("[ERROR] 找不到 session.db 的密钥", flush=True)
|
||||
sys.exit(1)
|
||||
enc_key = bytes.fromhex(session_key_info["enc_key"])
|
||||
session_db = os.path.join(DB_DIR, "session", "session.db")
|
||||
print("=" * 60, flush=True)
|
||||
|
||||
with open(KEYS_FILE) as f:
|
||||
keys = strip_key_metadata(json.load(f))
|
||||
|
||||
session_key_info = get_key_info(keys, os.path.join("session", "session.db"))
|
||||
if not session_key_info:
|
||||
print("[ERROR] 找不到 session.db 的密钥", flush=True)
|
||||
sys.exit(1)
|
||||
enc_key = bytes.fromhex(session_key_info["enc_key"])
|
||||
session_db = os.path.join(DB_DIR, "session", "session.db")
|
||||
|
||||
print("加载联系人...", flush=True)
|
||||
contact_names = load_contact_names()
|
||||
@@ -1916,12 +1916,12 @@ def main():
|
||||
# 后台预热所有 message DB(图片/emoji 解密必需)
|
||||
def _warmup():
|
||||
try:
|
||||
t0 = time.perf_counter()
|
||||
warmup_keys = ["message\\message_resource.db"]
|
||||
for i in range(5):
|
||||
k = f"message\\message_{i}.db"
|
||||
if get_key_info(keys, k):
|
||||
warmup_keys.append(k)
|
||||
t0 = time.perf_counter()
|
||||
warmup_keys = [os.path.join("message", "message_resource.db")]
|
||||
for i in range(5):
|
||||
k = os.path.join("message", f"message_{i}.db")
|
||||
if get_key_info(keys, k):
|
||||
warmup_keys.append(k)
|
||||
for k in warmup_keys:
|
||||
t1 = time.perf_counter()
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user