Commit Graph

64 Commits

Author SHA1 Message Date
Belugary
989badd14f feat: 给 transcribe_voice 工具加持久化缓存 (#58)
Whisper 本地推理在 CPU 下每条语音数秒到数十秒,且同一段 voice_data
产出相同 text,非常适合缓存。新增 voice_transcriptions.json 持久化
存储,命中时跳过 DB 查询、SILK 解码和 Whisper 推理全链路。

关键技术选择:
- 缓存 key 用 json.dumps([username, local_id]),即使 username 含
  分隔符也不冲突
- 写入走 tmp + os.replace 原子替换,进程中断不会损坏主文件
- 条目记录 model_size,Whisper 默认模型升级后旧条目自动失效
- 空转录也缓存(配合 model_size 失效),避免静音片段每次重跑
- threading.Lock 防御并发 load/save 竞态
- 首次 OSError 写 stderr 警告一次,后续静默避免刷屏

小的行为改进:resolve_username 移到 whisper/pysilk 导入探测之前,
bad chat_name 情况下不再需要 whisper 已安装也能给出"找不到聊天对象"
的错误提示。

15 个新测试:持久化 roundtrip、UTF-8 保留、corrupt JSON 容错、原子
写、写前失败不污染主文件、并发 load/save、缓存命中跳过重活、model
不匹配视为 miss、key 对含分隔符 username 的防御。全部通过。
2026-04-25 00:19:08 +08:00
btc-z
edf2c0940a feat: 新增聊天导出与语音转录 CLI 脚本 (#57)
* feat: 新增聊天导出与语音转录 CLI 脚本

新增两个独立 CLI 脚本,用于将单个聊天导出为结构化 JSON、并批量
填充语音消息的 Whisper 转录。区别于 MCP 工具:这些脚本面向离线
导出/归档,适合一次性拉取大量消息,或在会话外喂给其他 LLM/索引
管线使用。

- export_chat.py:跨分片合并某个聊天的全部消息,按时间排序后输出
  紧凑 JSON(type 为 text 时省略,is_group 仅群聊保留等)。复用
  mcp_server 中的消息解析/发送者解析辅助函数。
- transcribe_chat.py:读入 export_chat.py 产出的 JSON,对所有尚
  未转录的 voice 消息调用 Whisper,原地写回 transcription 字段。
  幂等(已有 transcription 的消息跳过)、崩溃安全(每条写回一次
  输出文件)。
- .gitignore:新增 *.json 通配,避免本地导出文件被误提交。
  config.example.json 已被跟踪,不受影响。

修复:transcribe_chat.py 原先调用 _silk_to_wav 时缺少 local_id
参数(commit c149389 将 local_id 加入签名用于文件名唯一化),
本 PR 中已补齐。

* docs: 新增聊天导出 JSON 数据格式文档

新增 docs/chat_export_format.md,描述 export_chat.py 与
transcribe_chat.py 产出的 JSON schema:顶层字段、消息对象的必填/
可选字段、默认值省略规则,以及加载与过滤的 Python 示例。

与现有 docs/macos-*.md 指南风格一致,避免在脚本 docstring 中堆叠
大段表格。export_chat.py 的 docstring 加一行指针指向本文档。

* docs: 聊天导出格式文档翻译为中文

与 docs/macos-*.md 既有指南保持一致的语言风格,将
docs/chat_export_format.md 翻译为中文。JSON 字段名、Python
代码示例等技术标识保持英文不变。

* fix: 回应 PR #57 review — 崩溃处理、幂等性、schema 补全

根据 review (#57) 的反馈:

- export_chat.py: _resolve_chat_context 返回 None 时的崩溃改为友好
  退出,并在 resolve 成功后打印 display_name (username),便于用户
  核对 resolve_username 的模糊匹配结果。
- export_chat.py: _query_messages 的 limit=999999 改为 None,避免
  超长历史被悄悄截断(_query_messages 对 None 会省略 LIMIT 子句)。
- export_chat.py: 输出 JSON 顶层新增 username 字段,让
  transcribe_chat.py 可以跳过二次模糊匹配,避免同名联系人漂移。
- transcribe_chat.py: 优先读取 JSON 顶层的 username,旧导出文件
  (无 username)回退到按 chat 名解析,保持向后兼容。
- transcribe_chat.py: 删除未使用的 import io / import wave,将循环
  内的 import datetime 提至模块顶部。
- export_chat.py: _decode_sticker_desc 的 varint 单字节简化给出
  注释说明局限,以及对 create_time 排序加 "or 0" 防御。
- export_chat.py / transcribe_chat.py: 模块 docstring 翻译为中文,
  与 docs/macos-*.md 保持一致。
- docs/chat_export_format.md: 同步补充 username 字段说明。
- .gitignore: 将 *.json 收窄为 *_export*.json / *_transcribed*.json,
  避免误屏蔽未来的 config/fixtures,同时匹配导出工具实际产出的
  文件名。
2026-04-25 00:16:37 +08:00
btc-z
02bc9c1840 feat: 新增语音 MCP 工具 + macOS 密钥提取修复 (#53)
* feat: 新增语音 MCP 工具 + macOS 密钥提取修复

- 新增 get_voice_messages / decode_voice / transcribe_voice MCP 工具
  - 语音数据存储在 media_0.db VoiceInfo 表(SILK v3 格式)
  - decode_voice 解码为 WAV 文件(saved to decoded_voices/)
  - transcribe_voice 通过 Whisper 自动识别语言转录
- 新增 get_chat_history oldest_first 参数,支持从最早消息开始分页
- 修复 macOS 下 check_wechat_running / ensure_keys 逻辑
  - 改用 pgrep 检测微信进程,绕过不支持 macOS 的 Python 扫描器
  - 无 all_keys.json 时打印清晰引导,提示运行 C 版扫描器
- 新增 Makefile(build / keys / decrypt / web 快捷命令)
- .gitignore 补充 find_all_keys_macos 二进制和 decoded_voices/

* fix: 语音查询支持多分片 media DB + 文件名唯一化

解决 PR #53 review 的阻塞项 #1,顺手修 #3、#6。

#1 `_get_media_db_path()` 硬编码 `media_0.db`
  - 新增模块级 `MEDIA_DB_KEYS`,镜像 `MSG_DB_KEYS` 的分片发现逻辑
  - `_fetch_voice_row` 遍历所有分片,按 `(chat_name_id, local_id)`
    首个命中即返回;单条语音在 media DB 家族内唯一,命中即可停
  - `get_voice_messages` 从每个分片各取 `LIMIT limit`,合并排序后
    截断到 `limit`。选择"每分片取 limit 条再合并"而非"按
    max(create_time) 排序后逐个取到 limit 即停止":后者假设分片
    间时间不重叠,一旦 WeChat 改分片策略就会静默丢消息;前者工作
    量 O(N 分片 × limit),在任何分片布局下都正确

#3 输出文件名冲突
  - `_silk_to_wav` 增加 `local_id` 参数,输出 `{user}_{time}_{lid}.wav`,
    同一秒内两条语音不会互相覆盖;两个调用方都已在作用域内持有
    `local_id`

#6 `_fetch_voice_row` 的 `local_id=None` 死分支
  - 随 #1 的重写一并删除,`local_id` 改为必填位置参数

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor: macOS 密钥提取分层下沉到 find_all_keys.py

解决 PR #53 review 的阻塞项 #2。

review 里提到"跟 PR #51 冲突"实测不存在 —— PR #51 当前 0 文件改动
(fork 分支已与上游同步),但架构建议本身是对的:macOS 处理应集中
在 `find_all_keys.py`,而不是在 `main.py` 提前 return 截胡。

- `main.py:ensure_keys()` 移除 darwin 专属提前返回分支,macOS 走
  和其他平台相同的 `extract_keys()` 路径
- `find_all_keys.py:_load_impl()` 在 darwin 分支抛出带
  `sudo ./find_all_keys_macos` 操作指引的 RuntimeError;非 macOS
  的平台兜底分支保留
- `main.py` 里已有 `except RuntimeError` 会打印并 `sys.exit(1)`,
  用户可见行为不变

未来若有 PR 在 `find_all_keys.py` 加 macOS 自动编译 / dispatch,
直接替换这段 RuntimeError 即可,不再需要改 `main.py`。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: Makefile 支持 PYTHON 变量覆盖

解决 PR #53 review 的非阻塞项 #7。

原 Makefile 硬编码 `.venv/bin/python3`,没有 venv 的用户跑 `make
decrypt` 直接报错。引入 `PYTHON ?= .venv/bin/python3`:默认行为
不变(仍走 venv),想用系统 Python 的用户 `PYTHON=python3 make
decrypt` 即可。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: 回应 PR #53 review #4 — 澄清 silk-python 与 pysilk 包名关系

验证:本项目 import 的 `pysilk` 实际由 `pip install silk-python`
(synodriver/pysilk) 提供;pypi 上另有同名 `pysilk==0.0.1` 是无内容
的占位包,不可用。错误消息里 `pip install silk-python` 已经是对的,
但 reader 看到 `import pysilk` 仍会困惑,所以:

- `_silk_to_wav` 的 import 处加一行注释,点名所用的是
  synodriver 版本,并提醒 pypi 上还有 pilk / pysilk 两个同类包
- `decode_voice` / `transcribe_voice` 的 docstring 加 "依赖:" 行,
  明确 "pip install silk-python (import 名为 pysilk)",MCP 客户端
  读 tool 描述就能看到正确的安装命令

未新增 requirements.txt 条目:voice 支持是可选功能(tool 内
try/except ImportError 懒加载),保持非必需依赖的语义。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 14:10:22 +08:00
ylytdeng
e86e00df87 fix: 新联系人/新群名称不刷新(issue #46)
之前的修复 load_contact_names() 读的是 decrypted/contact/contact.db
静态快照,新加联系人不在里面,所以"自动刷新"实际不生效。

现改为通过 db_cache 实时解密源 contact.db 再加载,确保新增联系人
即时可见。db_cache 内部靠 mtime 检测变化,微信写入后下次查询会触发
重新解密。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 13:55:51 +08:00
ylytdeng
a8cf64c0a6 docs: 补充 README macOS 操作说明
- 环境要求和快速开始章节新增 macOS 小节
- 添加 macOS 版 config.json 示例
- 明确 codesign、编译、扫描、解密四步流程

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 20:56:31 +08:00
ylytdeng
69a2f44240 feat: /api/history 支持按群过滤和增量拉取,更新 README API 文档
- /api/history 新增 chat、since、limit 参数
- README 新增 HTTP API 端点说明和联系人标签工具文档

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:43:41 +08:00
ylytdeng
7eb29b03e8 feat: 新增联系人标签查询功能
解析 contact.db 的 contact_label 表和 extra_buffer protobuf Field #30,
支持查询标签列表及指定标签下的成员。

- mcp_server.py: 新增 get_contact_tags / get_tag_members MCP 工具
- monitor_web.py: 新增 /api/tags JSON 端点,支持 ?name= 过滤

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 09:54:21 +08:00
ylytdeng
b80e7d1c14 fix: 新群/新联系人自动刷新联系人缓存
检测到消息的用户名不在联系人缓存中时,自动重新加载
contact.db,解决新建群聊一直显示 chatroom ID 的问题。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:43:34 +08:00
ylytdeng
396d4b24e2 fix: CLI 入口支持 V2(AES) 格式图片解密
decode_image.py 的 CLI 入口之前只走 XOR 解密路径,
V2 格式图片会直接报错退出。改为使用 decrypt_dat_file
智能入口,自动判断 V1/V2/XOR 格式。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:40:16 +08:00
joshua-deng
0821dc0e4e Update README.md
加了一个tg群,防失联
2026-03-23 17:25:19 +08:00
ylytdeng
944546beb1 fix: 统一所有 JSON 文件读写为 UTF-8 编码
Windows 中文环境默认编码为 GBK,未指定 encoding 会导致
config.json/all_keys.json 解析失败。修复 9 个文件共 17 处。

Closes #32

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 14:32:37 +08:00
joshua-deng
67244597f2 Merge pull request #28 from dsjzazs/feat/auto-install-deps
fix: 改为通过 requirements 安装依赖
2026-03-14 22:22:54 +08:00
joshua-deng
3e79c8e093 Merge pull request #30 from dsjzazs/main
MCP增强消息查询,支持时间范围和分页
2026-03-14 17:38:37 +08:00
dsjzazs
7c42ff5d38 Investigate get_chat_history limit 2026-03-14 16:59:17 +08:00
dsjzazs
2cd180c63a Merge pull request #2 from dsjzazs/codex/searchmessages
Add unit tests for MCP search and fix pagination
2026-03-14 16:39:12 +08:00
dsjzazs
9ae558a31e Fix global search pagination 2026-03-14 16:36:55 +08:00
dsjzazs
2e03247fb9 Add MCP dependency and pin versions (#1) 2026-03-14 15:13:28 +08:00
dsjzazs
b623711410 Add MCP search unit tests 2026-03-14 14:07:51 +08:00
dsjzazs
4bda20f7aa feat: 更新 README 2026-03-14 10:24:23 +08:00
dsjzazs
7e7f7a2516 feat: 增强消息查询功能,支持时间范围和分页 2026-03-14 10:21:21 +08:00
dsjzazs
8e8edc649c fix: 改为通过 requirements 安装依赖
README 改为统一使用 requirements.txt 安装依赖,并补充 zstandard 依赖,避免手动漏装。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:27:09 +08:00
ylytdeng
7020409543 fix: full_decrypt 写入前自动创建输出目录
full_decrypt 打开 out_path 写入时未创建父目录,
首次运行 monitor_web 且 decrypted/ 不存在时会报
FileNotFoundError。

Fixes #22

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 17:21:11 +08:00
ylytdeng
030680eb85 fix: 修复短时间大量消息丢失问题
旧逻辑用 `if ts == prev_ts: continue` 粗暴跳过上轮时间戳的所有消息,
但同一秒内可能有多条不同消息(如连续转发公众号文章),导致只显示
最后一条,其余丢失。

改为用 (username, timestamp, msg_type) 精确去重:
- 主消息和 hidden 消息显示后都记录到 _shown_keys
- 过滤时精确匹配已显示的消息,不再按时间戳整体跳过
- _shown_keys 每轮清理过期条目(保留 5 分钟),防止内存泄漏

Fixes #20

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:52:46 +08:00
joshua-deng
64b2c9fdef Merge pull request #19 from BiboyQG/feat/chat-history-formatting
功能改进实用,问题不阻塞合并。
2026-03-09 19:48:06 +08:00
Banghao Chi
fd67536ef7 Refine chat history message parsing 2026-03-08 20:52:33 -05:00
Banghao Chi
fa273b810d Improve chat history formatting 2026-03-08 15:30:10 -05:00
ylytdeng
a5a347f69e Merge PR #18: feat: Linux 数据库解密支持
- 新增 find_all_keys_linux.py (通过 /proc/pid/mem 扫描密钥)
- 新增 key_utils.py (跨平台路径兼容)
- 新增 key_scan_common.py (公共扫描逻辑)
- 拆分 find_all_keys.py 为平台分发入口
- 所有下游模块统一使用 get_key_info() 查找密钥

Fixes #12 (部分: Linux 支持)
Co-authored-by: PeanutSplash <b1300658700@outlook.com>
2026-03-07 21:35:37 +08:00
PeanutSplash
30112b9a10 fix(linux): address code review feedback
- SUDO_USER: skip fallback entirely when user is invalid (KeyError)
- load_config: move default merge after db_dir check to avoid dead code
- _is_wechat_process: prefer exact comm match, use exe substring as fallback
2026-03-07 21:35:24 +08:00
PeanutSplash
3d58b6508c fix(linux): validate SUDO_USER and use prefix matching for interpreters
- Validate SUDO_USER via pwd.getpwnam() to prevent path injection
- Use prefix matching for interpreter detection to cover python3.10+ etc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 21:35:24 +08:00
PeanutSplash
bf77cc97d8 refactor(linux): improve wechat detection and sudo db path fallback 2026-03-07 21:35:24 +08:00
PeanutSplash
bc80a1578d refactor(find_all_keys_windows): drop unused constants imports 2026-03-07 21:35:24 +08:00
PeanutSplash
6d9b2c0fe4 refactor(find_all_keys): extract shared key scan logic 2026-03-07 21:35:24 +08:00
PeanutSplash
872e3f58dc fix: handle exited PIDs and narrow message DB keys 2026-03-07 21:35:24 +08:00
PeanutSplash
f9c338b48d feat: add Linux support with cross-platform memory scanning
- Add Linux memory scanner (`find_all_keys_linux.py`) using `/proc/<pid>/mem`,
  same approach as Windows/macOS — no GDB, no function offsets, no restart needed
- Extract Windows-specific code to `find_all_keys_windows.py`
- Make `find_all_keys.py` a platform dispatcher (Windows / Linux)
- Add `key_utils.py` for cross-platform path matching (`/` vs `\` in all_keys.json)
- Update `config.py` with Linux auto-detection of db_storage paths
- Update all consumers (decrypt_db, monitor, monitor_web, mcp_server) to use
  `get_key_info()` for platform-agnostic key lookup

Tested on remote Linux container: 15/15 DBs scanned, decrypted, and verified.
2026-03-07 21:35:24 +08:00
ylytdeng
5879b58239 Merge PR #15: feat: macOS 图片密钥扫描器 + 批量解密器 (C)
新增 find_image_key.c 和 decrypt_images.c,
通过 Mach VM API + CommonCrypto 实现 macOS 图片解密。

Co-authored-by: bbingz
2026-03-07 21:35:08 +08:00
bbingz
e84f1d5130 fix: fallback key in multi-key mode + bound printf context
- decrypt_images.c: try image_keys.json lookup first, fall back to
  config.json single key when CT pattern not mapped (previously returned
  -5 immediately in multi-key mode)
- find_image_key.c: cap ASCII context printf to remaining buffer length,
  preventing out-of-bounds read near region end
2026-03-07 21:35:00 +08:00
bbingz
96c1a5ac2e fix: add file size validation and clarify Method 2 intent
- decrypt_images.c: validate aes_ct_size + xor_size fits within file
  before reading, preventing out-of-bounds reads on corrupt files
- decrypt_images.c: remove unused bytes2hex function
- find_image_key.c: add comment explaining Method 2 design intent —
  hex ASCII bytes used directly as AES key (not hex-decoded)
2026-03-07 21:35:00 +08:00
bbingz
03582dd82c fix: narrow Method 2 scan to hex charset [0-9a-f]
Previous range [a-z0-9] was too broad, matching non-hex characters
g-z which wastes CPU on false candidates. WeChat image keys are
lowercase hex strings.
2026-03-07 21:35:00 +08:00
bbingz
0576151b67 feat: add macOS image key scanner and batch decryptor (C)
- find_image_key.c: scans WeChat process memory for V2 image AES keys
  using Mach VM API + CommonCrypto batch decryption
- decrypt_images.c: batch decrypts V2 .dat image files using keys
  from image_keys.json, handles AES-ECB + XOR + raw_data segments

Build: cc -O3 -o find_image_key find_image_key.c -framework Security
       cc -O3 -o decrypt_images decrypt_images.c -framework Security
2026-03-07 21:35:00 +08:00
ylytdeng
2b03a81a8f fix: 统一路径分隔符为正斜杠,修复 macOS/Linux 兼容性
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 <noreply@anthropic.com>
2026-03-07 00:53:48 +08:00
joshua-deng
1294953681 Merge pull request #14 from bbingz/pr/macos-c-scanner
核心功能已验证,新增独立文件不影响现有功能。
2026-03-06 09:29:42 +08:00
joshua-deng
fc2ae833dc Merge pull request #13 from bbingz/pr/macos-docs
文档质量高,实测数据详实。剩余小问题不阻塞合并。
2026-03-06 09:29:35 +08:00
bbingz
992c3f5ee4 fix: replace nonexistent scan_keys.js with actual tools in quickstart
Quickstart step 4 referenced scan_keys.js which doesn't exist in the
repo. Replace with find_all_keys_macos.c (Method A) and note Frida as
Method B requiring user's own script. Also add config.json note for
step 5.
2026-03-05 23:26:55 +08:00
bbingz
18ffb2e7fa fix: use forward slashes in JSON output and add size==0 guard
- Remove forward-to-backslash conversion in JSON keys — forward slashes
  are native macOS paths and don't need JSON escaping (backslash paths
  like \b would be misinterpreted as escape sequences by JSON parsers)
- Add size==0 guard after mach_vm_region to prevent infinite loop
2026-03-05 23:19:22 +08:00
bbingz
76dd2b6d95 fix: clear header reserved-space field and add per-page HMAC note
- Zero out SQLite header offset 20 (reserved-space) after decryption,
  otherwise SQLite miscalculates usable page size
- Add comment noting production code should verify HMAC on every page,
  not just page 1
2026-03-05 23:18:03 +08:00
bbingz
d38d7ebf9c fix: replace glob() with nftw() and add chunk overlap
- glob() does not support ** recursive matching on macOS (POSIX).
  Replace with nftw() + opendir to recursively walk db_storage/.
- Add overlap between memory chunks to catch x'...' patterns
  spanning chunk boundaries.
2026-03-05 22:02:49 +08:00
bbingz
d4314c4857 fix: address review feedback on docs
- decrypt_page: zero-fill reserve for all pages (consistency)
- Move reserve into configs tuple for clarity
- Remove unused import os
- Trim duplicated permission content, reference permission guide
- Replace empty scan_keys.js shell with find_all_keys_macos reference
2026-03-05 21:55:10 +08:00
bbingz
1f9ca3792a feat: add macOS C memory key scanner
Scans WeChat process memory for SQLCipher encryption keys using
Mach VM API. Outputs all_keys.json compatible with decrypt_db.py.

Build: cc -O2 -o find_all_keys_macos find_all_keys_macos.c -framework Foundation
Usage: sudo ./find_all_keys_macos [pid]
2026-03-05 21:49:00 +08:00
bbingz
98933d5987 docs: add macOS permission guide and 3.x vs 4.x decryption comparison
- macOS permission guide: SIP, task_for_pid, codesign requirements
- 3.x vs 4.x decryption guide: SQLCipher parameter differences,
  multi-config DB handling, complete Python decryption examples
2026-03-05 21:48:35 +08:00
ylytdeng
3b4b811cc3 fix: 清理调试代码 + 提升服务稳定性
- 移除 _debug_log、signal handler、atexit 等调试代码
- 添加 allow_reuse_address 防止重启端口占用
- warmup 线程加外层异常捕获防止静默崩溃
- 恢复启动自动打开浏览器

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:41:36 +08:00