15 KiB
15 KiB
2026-08-20
MySQL SQL 导出升级(crm_import 链路):
- 用户确定表用 MySQL,建表 SQL 已提供(create_tables_mysql.sql),需求:导出时直接生成 SQL 数据文件方便导入
- 修改
wxwork_export_db.py:新增sql_escape/sql_num(MySQL 字面量转义)、MYSQL_MSG_COLS/MYSQL_USER_COLS/MYSQL_CONV_COLS、chunked_insert(每 500 行一条 INSERT)、build_mysql_data_sql(建表+INSERT 一体,含 SET NAMES utf8mb4、FOREIGN_KEY_CHECKS=0、文件名按范围crm_import_mysql_YYYY-MM-DD.sql/_all.sql) - 注意:MySQL 版 chat_conversations 无 account 列(SQLite 版有),SQL 生成时该列写 NULL
- 验证:144 条(7 天)与 2794 条(全量)SQL 行数与 SQLite 完全一致;users 392 vs 390、convs 199 vs 151 差异为多账号重复主键,INSERT IGNORE 自动去重属预期
wxwork_gui.py集成:新增「同时生成 MySQL 导入 SQL」复选框(configwith_db,默认开),导出流程改 4 步(解密/CSV/Excel/MySQL SQL),do_export 加 with_db 参数- 已重新打包 dist/企业微信导出助手.exe(含 wxwork_export_db 模块)
- 导入方式:
mysql -u root -p --default-character-set=utf8mb4 < crm_import_mysql_xxx.sql
表名对齐 zyt_ 前缀:
- 用户提供截图显示现有 MySQL 表名为
zyt_wx_chat_messages/zyt_wx_chat_users/zyt_wx_chat_conversations - 已将
wxwork_export_db.py中 SQLite/MySQL 表名、索引名、INSERT 语句全部替换为zyt_wx_*前缀 - 重新生成最近 7 天与全量 SQL,验证 INSERT 表名正确
- 已重新打包
dist/企业微信导出助手.exe
一键打包脚本:
- 新增
build.py+build.bat:一键打包(PyInstaller onefile/windowed/icon/add-data,打包后自动同步 wxwork_keys.json 到 dist) - 手动命令:
C:\Users\pc\.workbuddy\binaries\python\envs\gui311\Scripts\python.exe -m PyInstaller --onefile --windowed --name "企业微信导出助手" --icon app_icon.png --add-data "app_icon.png;." wxwork_gui.py - 注意:沙箱拦截 shutil.rmtree,build.py 不做 build 缓存清理(PyInstaller 自动覆盖)
图片/语音媒体导出可行性调查:
- 媒体文件存储位置:
Documents/WXWork/{account}/Cache/{Image,Voice,Video,File}/按月/ - 文件全部明文不加密:图片=PNG/JPG(89 50 4E 47 / FF D8 FF)、语音=SILK_V3(#!SILK_V3)、视频=MP4(ftyp)、文件=原始格式
- 消息 content 中含 protobuf 编码的文件 UUID + CDN URL(wework.qpic.cn/wwpic3az/...)
- UUID 匹配率极低(3/2758=0.1%):消息 content UUID 与 Cache 文件名不是同一套体系
- Cache/File 用原始文件名存储(如
【全栈工程师...】.pdf),可与消息 content 文件名匹配 - Cache 覆盖范围有限:仅存 2026-03~08(约 644M,图片553+语音21),更早的已清理
- message_table 列名:content_type(非 msg_type),实际 content_type 值与 MSG_TYPE_MAP 不同(14=图片,16=语音,23=视频,565=文件,31=链接,123=未知)
- forever_store.db 为空,无文件映射表
- 结论:可导出本地 Cache 媒体文件(无需解密),但无法保证全部历史媒体;图片/语音精确关联到消息需额外开发 protobuf 深度解析
媒体导出功能开发完成:
- 新增
wxwork_export_media.py模块:extract_media_refs(UUID+CDN URL+文件名提取)、build_cache_index(Cache 扫描索引)、match_media(UUID/文件名/模糊匹配)、export_media(复制到输出目录) - 集成到
wxwork_export_db.py:export_to_db 自动调用 export_media,消息表增加media_file_path+media_url两列(SQLite/MySQL/CSV/SQL 全部同步) - GUI 集成:步骤 4/4 描述改为"MySQL SQL + 导出媒体文件",无需额外选项(with_db=True 时自动导出媒体)
- 验证:全量 2794 条消息中 292 条复制了本地文件(图片256+语音22+视频10+文件4,202MB),655 条有 CDN URL
- 关联方式:文件名匹配 254 条(主力)、UUID 3 条、模糊 35 条
- 已重新打包
dist/企业微信导出助手.exe(含 wxwork_export_media hidden-import) - build.py 也加了
--hidden-import wxwork_export_media
修复今天消息/图片"丢失"问题:
- 根因 1:
decrypt_with_keys缓存只比较主库message.dbmtime, 未考虑-wal文件。企业微信运行时使用 WAL 模式, 新消息先写message.db-wal, 退出后才 checkpoint 到主库。旧缓存crm_import/decrypted/.../message.db(09:38) 比源库 (11:02) 旧, 但用户导出时 WAL 尚未 checkpoint, 导致今天消息缺失。 - 修复 1:
wxwork_export_final.py中decrypt_with_keys缓存判断改为src_mtime = max(db_mtime, wal_mtime); 若 WAL 比解密副本新则强制重新解密。 - 根因 2:
session.db的conversation_table对 S: 开头单聊未存会话名, 代码 fallback 为"群聊 S:...", 导致用户以为消息不在该会话。 - 修复 2:
wxwork_export_final.py与wxwork_export_db.py中对S:对方UID_本账号UID格式的 conversation_id, 从user_cache查对方 UID 的名字, 正确显示为"甄养堂(霍医生)"。 - 根因 3:
MSG_TYPE_MAP把 content_type=14 标为"语音/加密", 但实际为截图/图片; content_type=123 标为"文本", 实际也带截图文件。 - 修复 3:
MSG_TYPE_MAP[14]改为"图片";wxwork_export_db.py中若media_file_path指向图片/视频/语音/文件, 按扩展名覆盖msg_type_name。 - 新增:
wxwork_gui.py导出前检测message.db-wal, 若存在则提示"企业微信仍在运行, 最新消息可能未写入本地, 建议退出后重试"。 - 验证: 重新全量导出后
crm_wecom.db今天 14 条消息、3 条图片类型、会话名正确、图片文件路径正确 (media_file_path)。 - 已重新打包
dist/企业微信导出助手.exe。
只导出单聊(过滤群聊/应用消息):
- 用户需求:只导出用户之间的聊天记录,群组数据不导出
- 会话类型判定(基于 message.db 真实数据归纳):
M:xxx=微信单聊、S:UID_UID=企微单聊、R:xxx=群聊、Y:xxx=应用消息、O:xxx=第三方应用/服务号(SCRM/加粉工具)、APPROVAL/MAIL等=系统虚拟会话 - 新增
is_personal_chat(conv_id)(wxwork_export_final.py):白名单保留M:与S:数字_数字,其余全过滤 - 三个导出入口全部加
personal_only=True参数(默认单聊):export_messages(final)、export_to_db(db)、export_media(media,SQL 加conversation_id LIKE 'M:%' OR 'S:%');CLI 加--with-groups可恢复全量 - S: 会话名解析修正:S: 两个 UID 顺序不固定(对方_本账号 或 本账号_对方),改为取"不等于 user_dir 的那个 UID"作为对方,避免把本账号名(高兴亮)当对方名
- GUI 新增「仅导出单聊(不含群组/应用消息)」勾选框(config
personal_only,默认 True),do_export 贯穿 4 步导出 - 验证:主账号 2772 条 → 单聊 1394 条(S:1275 + M:119),过滤 1378 条(R:540+Y:716+O:102+AP/MA20 完全对应);今天 12 条完整;会话表 64 个全为真实单聊(霍医生/史医生/甄养堂助理系列)
- 注意:
M:微信单聊(9 会话 119 条)名字本地库查不到(wx_friend/wechat_contactV1 均未命中),显示 UID 数字 - 已重新打包
dist/企业微信导出助手.exe(19.0 MB)
按会话名称过滤官方/系统账号:
- 用户反馈:截图里 "13102691405879689"(M:微信单聊,内容全是"X月X日汇报提醒")和 "企业微信团队"(S: 单聊,对方 user 表名即官方账号名)不想导出,选择"按会话名称过滤"
- 新增
DEFAULT_BLOCKED_CONV_NAMES = ("企业微信团队","微信团队","微信支付","腾讯客服","腾讯新闻")+is_blocked_conv_name()(wxwork_export_final.py,包含匹配,空名不命中) - 过滤同时支持会话名称和会话ID(含 M:/S: 前缀的包含匹配):GUI 输入框可填官方账号名或数字会话ID(如 13102691405879689)
- 三个导出入口全部加
blocked_conv_names=None参数:export_messages / export_to_db(先预扫描 conv_cache 算出 blocked_conv_ids 供媒体同步过滤)/ export_media(新增 blocked_conv_ids 参数,行级跳过) - GUI:设置卡新增输入框「过滤的账号名称或会话ID(逗号分隔)」config
blocked_names(默认含 5 个官方关键词),KeyRelease 自动保存;_export_worker 解析逗号为 tuple 传入 do_export - 验证:全量导出 1394 → 1329 条(过滤官方账号 65 条),会话 64 → 63,官方关键词消息/会话残留均 0;今天消息 11 条完整;媒体"复制 0 个文件"属正常(dest 已存在跳过,非 bug)
- 打包注意:build.py 遇 safe-delete 拦截,需先
rm -rf build/企业微信导出助手 build/*.toc build/*.pyz再删dist/企业微信导出助手.exe,最后非沙箱跑 build.py - 已重新打包
dist/企业微信导出助手.exe(19.0 MB)
Excel 图片显示为编码串修复(媒体路径+超链接):
- 用户反馈:Excel 里图片消息内容列显示十六进制编码串,图片"没用显示"
- 修复 1:
wxwork_export_final.py的export_messages集成wxwork_export_media.export_media,导出 CSV/Excel 时同步复制本地 Cache 媒体文件到out_dir/媒体文件/ - 修复 2: CSV/Excel 新增「媒体文件路径」「媒体URL」两列;图片/截图/语音/视频/文件消息按
server_id从media_map回填真实本地路径 - 修复 3:
wxwork_export_media.py把result[sid]的 key 统一为str(sid),避免 final/db 两端用server_id字符串匹配失败;并把对wxwork_export_final的顶层导入改为export_media函数内部延迟导入,打破循环引用 - 修复 4:
wxwork_export_db.py中media_map.get(str(msg.get("server_id")))同步修正,保证 crm_wecom.db 的media_file_path字段正确 - 修复 5:
MSG_TYPE_MAP[123]从"文本"改为"截图"(企业微信截图实际 content_type=123) - 修复 6:
wxwork_gui.py的csv_to_xlsx把「媒体文件路径」单元格设为file:///超链接(点击直接打开图片),并把图片/截图等媒体行「内容」列的原始编码串替换为[图片]/[截图]等友好提示;列宽调整为 14 列 - 验证:今天 3 条图片/截图消息在 Excel 中均显示
[图片]/[截图]+ 可点击的媒体文件\图片\...路径;数据库crm_wecom.db190 条消息带media_file_path - 已重新打包
dist/企业微信导出助手.exe(19.0 MB)
企业微信数据目录可配置(非默认安装):
- 用户需求:企微非默认安装时(如数据在
C:\Users\Administrator\Documents\WXWork)默认路径~\Documents\WXWork找不到数据库导致提取密钥失败,需要可设置的地方 wxwork_export_final.py新增detect_wxwork_dir(candidates=None):依次尝试 ①用户配置目录 ②当前用户 Documents\WXWork ③注册表 Shell Folders 的 Personal 位置下 WXWork ④扫描 C:\Users*\Documents\WXWork;目录有效性=含账号子目录 Data\message.dbwxwork_find_key.pyextract_wxwork_key()新增db_base参数替代硬编码路径wxwork_gui.py:设置卡新增「企业微信数据目录」输入框+浏览+自动检测按钮(configdb_dir,留空回退默认路径);extract_all_keys(log, db_dir)与do_export(..., db_dir)参数化;启动时若默认目录不存在自动 detect 并回填;提取/导出日志均显示实际数据目录- 验证:detect_wxwork_dir 跳过无效候选、正确回退默认目录;py_compile 全通过
- 已重新打包
dist/企业微信导出助手.exe(19.0 MB)
企业微信数据目录默认路径显示优化:
- 用户反馈:GUI 启动时「企业微信数据目录」输入框为空,希望显示默认值
- 修复:
wxwork_gui.py中db_dir_var初始值改为self.config.get('db_dir', DEFAULT_DB_BASE) or DEFAULT_DB_BASE,启动时始终默认填入~\Documents\WXWork - 启动检测逻辑:若默认路径有效显示绿色 ✓;若默认路径无效则尝试自动检测,找到则替换,未找到保留默认路径并提示"默认位置未检测到"
- 用户手动留空时
get_db_dir()仍回退到默认路径,行为与旧版一致 - 已重新打包
dist/企业微信导出助手.exe(19.0 MB)
语音转文字功能(读缓存 + 本地ASR兜底):
- 用户需求:录音能否直接转成文字导出
- 重大发现:解密后 message.db 有
msg_voice2text表(企微客户端点过"转文字"的语音才有缓存),关联键msg_voice2text.message_id = message_table.server_id(注意不是 message_id 字段) - 主账号 39 条语音消息 39 条全部有转写文本(纯文本质量高);另一账号
msg_voice2text空表说明从未点过转文字 - 新增
wxwork_voice2text.py:load_voice2text(decrypted_dbs)读缓存返回{(user_dir, str(server_id)): text};decode_silk_to_wav(pilk);asr_missing(pilk+faster-whisper 本地识别,懒加载缺依赖时优雅跳过) wxwork_export_final.py/wxwork_export_db.py:export_messages/export_to_db新增voice2text_map=None, voice_asr=False参数;CSV/SQLite/MySQL 三处新增「语音转文字」列(字段 voice_text);语音消息(.silk/.amr)若有转写文本,内容列直接显示转写文本wxwork_gui.py:新增「无缓存语音用本地 AI 识别」勾选框(configvoice_asr默认关,勾选后 do_export 预读缓存→asr_missing 合并,CSV 与 DB 共享避免重复识别);csv_to_xlsx语音列自动换行、有转写文本时不替换为[语音]- 验证:全量导出 23 条语音消息带转写文本(CSV/SQLite 均正确);MySQL SQL 建表与 INSERT 均含 voice_text
- build.py 新增
--hidden-import wxwork_voice2text(pilk/faster-whisper 不打包,懒加载) - 已重新打包
dist/企业微信导出助手.exe
ASR 兜底识别验证与打包决策:
- pilk 0.2.4 API 变化:
decode(silk, pcm, pcm_rate=)只输出 PCM,需用pilk.silk_to_wav(silk, wav, rate=)便捷函数;已兼容两种 API - faster-whisper 1.2.1 + ctranslate2 4.8.1 + onnxruntime + av + numpy 安装到 gui311 venv(约 155MB 依赖)
- huggingface_hub 1.28 与 hf-mirror 不兼容(FileMetadataError: Distant resource does not seem to be on huggingface.co),必须直连官方源;已移除 HF_ENDPOINT 镜像设置,仅保留 HF_HUB_DISABLE_XET=1(xet 后端也不稳)
- Windows 沙箱环境模型缓存符号链接失效 → 快照文件 0 字节,需手动把 blobs 复制到 snapshots/ 目录;tree json 可查文件→blob 映射(tokenizer.json 2203239B vs vocabulary.txt 459861B,勿搞反)
- ASR 链路验证:base 模型加载 1.8s、7 秒语音识别 1.2s,结果「主要是我就找不到这个人的名字我们这边看不到」质量高;asr_missing 合并 {('',sid):text} 正常
- 默认模型定为 base(~145MB,与承诺的 150MB 一致),GUI 文案已更新
- 决策:ASR 依赖打包进 exe(否则勾选框在 exe 中无效),build.py 加 pilk/faster_whisper/ctranslate2/tokenizers/av/onnxruntime hidden-import
- 最终打包成功:
dist/企业微信导出助手.exe96.1 MB(含 ASR 依赖;GUI 两个开关:读缓存默认开、本地 AI 识别默认关)