Files
zWorkFlow/setup.py
2026-05-14 15:45:00 +08:00

246 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
WeChat Decrypt — 交互式配置向导
检测微信数据目录、选择转录 backend、生成 config.json。
用法:
python3 setup.py # 交互式配置
python3 setup.py --check # 仅检查环境,不修改文件
"""
import argparse
import glob
import json
import os
import platform
import shutil
import subprocess
import sys
def detect_wechat_dir():
"""自动检测微信数据目录"""
system = platform.system().lower()
if system == "darwin":
containers = (
os.path.expanduser(
"~/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files"
)
)
if os.path.exists(containers):
dirs = [
os.path.join(containers, d, "db_storage")
for d in os.listdir(containers)
if os.path.isdir(os.path.join(containers, d))
]
dirs = [d for d in dirs if os.path.exists(d)]
# 按修改时间排序,最近活跃的排最前
dirs.sort(key=lambda d: os.path.getmtime(d), reverse=True)
return dirs if dirs else None
elif system == "linux":
home = os.path.expanduser("~")
candidates = [
os.path.join(home, "Documents", "xwechat_files"),
os.path.join(home, ".xwechat", "files"),
]
for c in candidates:
if os.path.exists(c):
dirs = [
os.path.join(c, d, "db_storage")
for d in os.listdir(c)
if os.path.isdir(os.path.join(c, d))
]
dirs = [d for d in dirs if os.path.exists(d)]
if dirs:
return dirs
elif system == "windows":
localappdata = os.environ.get("LOCALAPPDATA", "")
candidates = [
os.path.join(localappdata, "xwechat_files"),
os.path.join(os.environ.get("USERPROFILE", ""), "Documents", "xwechat_files"),
]
for c in candidates:
if os.path.exists(c):
dirs = [
os.path.join(c, d, "db_storage")
for d in os.listdir(c)
if os.path.isdir(os.path.join(c, d))
]
dirs = [d for d in dirs if os.path.exists(d)]
if dirs:
return dirs
return None
def detect_transcription_backends():
"""检测可用的转录 backend"""
backends = {"local": True} # local 总是可用(如果安装了依赖)
# whisper-cpp
if shutil.which("whisper-cpp") or shutil.which("whisper-cli"):
backends["whisper_cpp"] = True
model_dirs = [
os.path.expanduser("~/Library/Application Support/whisper-cpp"),
os.path.expanduser("~/whisper-models"),
"/opt/homebrew/share/whisper-cpp/models",
]
for md in model_dirs:
if os.path.exists(md):
models = [f for f in os.listdir(md) if f.startswith("ggml-") and f.endswith(".bin")]
if models:
backends["whisper_cpp_model"] = models[0]
break
else:
backends["whisper_cpp"] = False
# openai
try:
import openai # noqa
backends["openai"] = True
except ImportError:
backends["openai"] = False
return backends
def check_environment():
"""检查环境,返回状态信息"""
print("=== 环境检查 ===")
py_ver = sys.version.split()[0]
print(f"[Python] {py_ver}")
# venv
in_venv = sys.prefix != sys.base_prefix
print(f"[venv] {'' if in_venv else ''}")
if not in_venv:
print(" 建议使用: python3 -m venv .venv && source .venv/bin/activate")
# whisper-cpp
whisper_bin = shutil.which("whisper-cpp") or shutil.which("whisper-cli")
print(f"[whisper-cpp] {'' + whisper_bin if whisper_bin else '✗ 未安装 (brew install whisper-cpp)'}")
# config
if os.path.exists("config.json"):
with open("config.json") as f:
cfg = json.load(f)
db_dir = cfg.get("db_dir", "?")
backend = cfg.get("transcription_backend", "未设置")
print(f"[config.json] ✓ (db_dir = {db_dir}, backend = {backend})")
else:
print("[config.json] ✗ 未找到")
# 微信目录
dirs = detect_wechat_dir()
if dirs:
print(f"[微信目录] 找到 {len(dirs)} 个:")
for d in dirs:
age_days = (os.path.getmtime(__file__ if '__file__' in dir() else 0) - os.path.getmtime(d)) / 86400 if os.path.exists(d) else 0
print(f" {d}")
else:
print("[微信目录] 未找到自动检测路径")
return dirs
def interactive_setup():
"""交互式配置向导"""
print("\n=== 微信解密工具 — 配置向导 ===\n")
# 加载或创建配置
config = {}
if os.path.exists("config.json"):
with open("config.json") as f:
config = json.load(f)
print(f"现有 config.json 已加载 ({len(config)} 个字段)")
print()
# 微信数据目录
detected = detect_wechat_dir()
if detected:
if len(detected) == 1:
chosen = detected[0]
print(f"[1/3] 微信数据目录: 自动检测到")
print(f" {chosen}")
else:
print(f"[1/3] 检测到 {len(detected)} 个微信数据目录:")
for i, d in enumerate(detected, 1):
print(f" [{i}] {d}")
try:
sel = int(input("\n请选择 (1-{}): ".format(len(detected))) or "1")
chosen = detected[sel - 1]
except (ValueError, IndexError):
chosen = detected[0]
config["db_dir"] = chosen
else:
print("[1/3] 微信数据目录: 未能自动检测")
default_path = os.path.expanduser("~/Documents/xwechat_files/your_wxid/db_storage")
chosen = input(f" 请手动输入路径 [{default_path}]: ") or default_path
config["db_dir"] = chosen
# 转录 backend
backends = detect_transcription_backends()
print(f"\n[2/3] 语音转录 backend:")
print(f" [1] local — 本地 CPU 转录(默认,隐私最佳,速度较慢)")
status_w = "" if backends.get("whisper_cpp") else "✗ (brew install whisper-cpp)"
print(f" [2] whisper_cpp — GPU 加速 ({status_w})")
status_o = "" if backends.get("openai") else "✗ (pip install openai)"
print(f" [3] openai — API 转录 ({status_o})")
try:
sel = int(input("\n 请选择 (1-3) [1]: ") or "1")
if sel == 2:
config["transcription_backend"] = "whisper_cpp"
if "whisper_cpp_model" in backends:
config["whisper_cpp_model"] = backends["whisper_cpp_model"]
elif sel == 3:
config["transcription_backend"] = "openai"
key = input(" 输入 OpenAI API Key: ").strip()
if key:
config["openai_api_key"] = key
else:
config["transcription_backend"] = "local"
except (ValueError, IndexError):
config["transcription_backend"] = "local"
# 确认
print(f"\n[3/3] 即将写入 config.json:")
print(json.dumps(config, indent=4))
ans = input("\n 确认?(Y/n): ").strip().lower()
if ans in ("", "y", "yes"):
with open("config.json", "w", encoding="utf-8") as f:
json.dump(config, f, ensure_ascii=False, indent=4)
print(" config.json 已写入")
else:
print(" 已取消config.json 未修改")
print("\n配置完成!下一步:")
print(" python main.py status — 查看状态")
print(" python main.py decrypt — 解密数据库")
print(" python main.py export — 解密 + 导出聊天记录")
def main():
parser = argparse.ArgumentParser(
description="WeChat Decrypt — 配置向导",
)
parser.add_argument(
"--check",
action="store_true",
help="仅检查环境,不修改文件",
)
args = parser.parse_args()
if args.check:
check_environment()
else:
interactive_setup()
if __name__ == "__main__":
main()