"""WeChat Viewer — 微信消息管理 Web 服务""" import subprocess import json import os from pathlib import Path BASE_DIR = Path(__file__).parent # ── 账号自动发现 ────────────────────────────────────── DEFAULT_KEY = "e03ce81" def _su_exists(path): try: r = subprocess.run(["su","-c",f"test -f {path} && echo yes || echo no"], capture_output=True, text=True, timeout=5) return "yes" in r.stdout except: return False def _get_key_for_account(account_id): """读取 .wechat_key_ 文件,不存在用默认密钥""" key_file = BASE_DIR / f".wechat_key_{account_id}" try: with open(key_file) as f: k = f.read().strip() if k: return k except: pass return DEFAULT_KEY def _get_nickname(db_path, key): """从账号自己的 DB 读取昵称""" try: inner = ( f"{SQLCIPHER_BIN} '{db_path}' " f"-cmd \"PRAGMA key='{key}';\" " f"-cmd \"PRAGMA cipher_compatibility=1;\" " f"-cmd \"PRAGMA cipher_page_size=1024;\" " f"-cmd \"PRAGMA query_only=true;\" " f"-cmd \".mode json\" " f"\"SELECT value FROM userinfo WHERE id=4 LIMIT 1;\"" ) r = subprocess.run(["su","-c",inner], capture_output=True, text=True, timeout=10) # Parse JSON output for line in r.stdout.strip().split("\n"): line = line.strip() if line.startswith("[{") and '"value"' in line: import json as _json rows = _json.loads(line) if rows and isinstance(rows, list) and rows[0].get("value"): return rows[0]["value"] except: pass return None def _get_custom_name(account_id): """读取 .wechat_name_ 文件(用户自定义昵称)""" name_file = BASE_DIR / f".wechat_name_{account_id}" try: with open(name_file) as f: n = f.read().strip() if n: return n except: pass return None def discover_accounts(): accounts = [] base = "/data/data/com.tencent.mm/MicroMsg" try: result = subprocess.run( ["su","-c",f"ls -d {base}/*/ 2>/dev/null"], capture_output=True, text=True, timeout=5 ) for line in result.stdout.strip().split("\n"): line = line.strip() if not line: continue db_path = os.path.join(line, "EnMicroMsg.db") if _su_exists(db_path): folder = os.path.basename(line.rstrip("/")) key = _get_key_for_account(folder) # 名字优先级:自定义名 > 从DB读 > hash前缀 name = _get_custom_name(folder) or _get_nickname(db_path, key) or folder[:8] accounts.append({ "id": folder, "name": name, "db_path": db_path, "data_root": line.rstrip("/"), "key": key, }) except Exception: pass if not accounts: accounts = [{"id": "default", "name": "默认", "db_path": "", "data_root": "", "key": DEFAULT_KEY}] return accounts SQLCIPHER_BIN = "/data/data/com.termux/files/usr/bin/sqlcipher" ACCOUNTS = discover_accounts() # ── 当前账号状态 ───────────────────────────────────── ACCOUNT_FILE = str(BASE_DIR / ".current_account") def get_current_account_id(): try: with open(ACCOUNT_FILE) as f: aid = f.read().strip() if any(a["id"] == aid for a in ACCOUNTS): return aid except: pass return ACCOUNTS[0]["id"] if ACCOUNTS else "default" def set_current_account_id(aid): with open(ACCOUNT_FILE, "w") as f: f.write(aid) def get_current_account(): aid = get_current_account_id() for a in ACCOUNTS: if a["id"] == aid: return a return ACCOUNTS[0] if ACCOUNTS else {"id": "default", "name": "默认", "db_path": "", "data_root": "", "key": DEFAULT_KEY} # ── 通用配置 ────────────────────────────────────────── WECHAT_KEY = DEFAULT_KEY # 向后兼容,实际用 account["key"] LOCAL_DB = str(BASE_DIR / "circle.db") HOST = "0.0.0.0" PORT = 5000 DEBUG = False