#!/usr/bin/env python3 """WeChatDecrypt.exe 双模式入口。 无参数时启动 Web UI;带参数时分发到现有 CLI 脚本。该入口同时兼容 PyInstaller onefile 里 Web UI 子进程使用的 ``sys.executable script.py`` 调用形态。 """ import os import runpy import sys MAIN_COMMANDS = {"decrypt", "export", "all", "status", "-s", "decode-images"} HELP_COMMANDS = {"help", "-h", "--help"} SCRIPT_ENTRYPOINTS = { "main.py", "monitor_web.py", "export_all_chats.py", "find_image_key.py", "decrypt_sns.py", "export_sns.py", "find_wxwork_keys.py", "decrypt_wxwork_db.py", "export_wxwork_messages.py", "voice_to_mp3.py", "batch_decrypt_images.py", "decrypt_db.py", } def _app_base_dir(): if getattr(sys, "frozen", False): return os.path.dirname(sys.executable) return os.path.dirname(os.path.abspath(__file__)) def _script_dir(): return getattr(sys, "_MEIPASS", _app_base_dir()) def _prepare_runtime(): base_dir = _app_base_dir() os.environ["WECHAT_DECRYPT_APP_DIR"] = base_dir os.chdir(base_dir) def _configure_stdio(): for stream in (sys.stdout, sys.stderr): if hasattr(stream, "reconfigure"): stream.reconfigure(encoding="utf-8", errors="replace") def print_usage(stream=None): stream = stream or sys.stdout stream.write( "WeChatDecrypt.exe 用法:\n" "\n" " WeChatDecrypt.exe 启动 Web UI\n" " WeChatDecrypt.exe web 启动 Web UI\n" " WeChatDecrypt.exe status 查看状态\n" " WeChatDecrypt.exe decrypt 提取密钥并解密数据库\n" " WeChatDecrypt.exe export [args...] 解密并批量导出聊天\n" " WeChatDecrypt.exe all 密钥 -> 解密 -> 导出\n" " WeChatDecrypt.exe decode-images [args...] 批量解密 .dat 图片\n" " WeChatDecrypt.exe export-all [args...] 直接调用 export_all_chats.py\n" "\n" "兼容脚本式调用:\n" " WeChatDecrypt.exe main.py decrypt\n" " WeChatDecrypt.exe export_all_chats.py --write-plan-csv export_plan.csv\n" ) def _resolve_script(argv): if not argv: return "monitor_web.py", [] cmd = argv[0] if cmd == "web": return "monitor_web.py", argv[1:] if cmd in MAIN_COMMANDS: return "main.py", argv if cmd == "export-all": return "export_all_chats.py", argv[1:] if cmd in SCRIPT_ENTRYPOINTS: return cmd, argv[1:] return None, None def run_script(script, script_args): _prepare_runtime() script_path = os.path.join(_script_dir(), script) if not os.path.exists(script_path): script_path = os.path.join(_app_base_dir(), script) if not os.path.exists(script_path): raise FileNotFoundError(f"脚本不存在: {script_path}") old_argv = sys.argv[:] try: sys.argv = [script] + list(script_args) try: runpy.run_path(script_path, run_name="__main__") except SystemExit as exc: if exc.code is None: return 0 if isinstance(exc.code, int): return exc.code print(exc.code, file=sys.stderr) return 1 return 0 finally: sys.argv = old_argv def dispatch(argv=None, script_runner=None, stdout=None, stderr=None): argv = list(sys.argv[1:] if argv is None else argv) stdout = stdout or sys.stdout stderr = stderr or sys.stderr runner = script_runner or run_script if argv and argv[0] in HELP_COMMANDS: print_usage(stdout) return 0 script, script_args = _resolve_script(argv) if script is None: print_usage(stdout) print(f"未知命令: {argv[0]}", file=stderr) return 2 return runner(script, script_args) def main(): _configure_stdio() sys.exit(dispatch()) if __name__ == "__main__": main()