--- name: wecom-file-receive description: 通过企业微信接收文件并对接现有工作流(合同审查、文件处理等)。企微群里文件无法同时@人,需要用户引用文件消息后@小Maggie触发。 version: 1.0.0 tags: [企业微信, 文件接收, workflow] --- # 企业微信文件接收与处理 ## 触发条件 - 用户在企微群里发送文件后,引用该文件消息并@小Maggie - 收到的消息中包含文档附件(saved at路径) ## 文件接收机制 ### 用户操作方式 1. 用户在企微群里直接发送文件(此时无法同时@人,小Maggie收不到) 2. 用户**回复那条文件消息**并@小Maggie(如"@小Maggie 审查这个合同") 3. 网关从引用消息(quote)中提取文件的url和aeskey,下载并AES解密后保存到本地 ### 文件保存位置 - 自动保存到 `~/.hermes/cache/documents/` 目录 - 文件名格式:`doc__<原始文件名>` - 消息中会显示 `The file is saved at: <完整路径>` ### 支持的文件类型 - 文档:.docx, .doc, .pdf, .xlsx, .xls - 图片:.jpg, .png, .gif, .webp - 其他:任意文件类型均可接收 ## 收到文件后的通知规则 ### 群里收到的文件 在**同一个群里**回复确认收到,列出文件名和发送人。 ### 私信收到的文件 **不要主动往群里推送通知。** 在私信里回复确认收到即可。如需通知群里的人,等群里有新的@消息时顺便汇报,或私信相关人员。 ### ⚠️ 为什么不主动推群消息(铁律) 企微 AI Bot 不能用 `APP_CMD_SEND` 主动发群消息,Hermes 底层会复用旧的 inbound `req_id` 通过 `APP_CMD_RESPONSE` 发送。这种"拿过期会话凭证推送非用户发起的消息"的行为有风险: - 可能触发企微服务端风控/限流 - 短时间频繁重启后叠加此行为,曾导致部分用户的群@消息不再被推送(2026-06-08事件) - sender 解析可能变成 "unknown",发出不专业的消息 - 2026-06-09 Doro明确指令:"不要往群里发消息,已经触发风险机制了" **`auto_notify_new_file.sh` 守护进程**也遵守此规则——只走 `notify_doro`(私信),不调 `notify_group`。详见 uwf skill。 ### ⚠️ `get_sender()` 发件人检测不可靠 `auto_notify_new_file.sh` 中的 `get_sender()` 函数查 `sessions` 表最近5分钟的最后一个 session 的 `user_id`,经常误判(如邱律师的文件被识别为WeiWei)。准确确认发件人的方法是查 `messages` 表: ```sql SELECT s.user_id, m.content FROM messages m JOIN sessions s ON m.session_id = s.id WHERE m.content LIKE '%sent a document%文件名关键词%' ORDER BY m.timestamp DESC LIMIT 3 ``` ### 通知方式选择 - **群里收到的文件** → 在同一个群里回复通知(这是正常的回复,有 reply_req_id) - **私信收到的文件** → **私信 Doro 通知**,不要主动往群里推。原因:AI Bot 不能 APP_CMD_SEND 主动发群消息,Hermes 靠复用旧 req_id 的 APP_CMD_RESPONSE 发送——旧 req_id 可能已过期,且频繁对过期 req_id 发消息可能被企微风控 ### 邱律师(QiuTing)的合同:收到就开干 邱律师私信发的合同文件(.docx/.doc/.pdf),收到后**不问处理方式,直接执行全流程**: 1. **私信回复邱律师**确认收到文件 2. 上传Nextcloud待审查目录 3. 启动review-contract workflow(`setsid`后台运行) 4. workflow全链路自动跑完(含final_review) 5. **小Maggie作为总负责人做终审质检**——发现问题自己修,确认无误才通知Doro 6. 终审通过→更新合同审查清单.xlsx→上传Nextcloud→私信Doro(简洁格式:`任务交付/【修】xxx.docx`) ⚠️ **总负责人原则**(Doro 2026-06-08确立):Doro看到的必须是终审确认无误的。workflow的final_review只是机器检查,真正的终审是小Maggie的判断。 ⚠️ 全程走Doro私信通知,不发群消息。 ### 其他人的文件:通知后等指示 Doro、洪总等其他人发的文件,私信Doro汇报后**等Doro指示再处理**。 通知方法(私信Doro): 使用 `send_message(target='wecom:doro', message='...')` 即可。 ## 处理流程 ### 场景一:合同审查 用户发文件并说"审查这个合同"时: 1. 从消息中获取文件保存路径 2. 将文件复制到 Nextcloud 待审查目录: ```bash docker cp <本地文件> nextcloud-nextcloud-1:/var/www/html/data/doro/files/Doro合同审查任务/待审查/<原始文件名> docker exec nextcloud-nextcloud-1 chown www-data:www-data <目标路径> docker exec -u www-data nextcloud-nextcloud-1 php occ files:scan doro --path='/doro/files/Doro合同审查任务/待审查/' ``` 3. 按现有合同审查workflow执行(加载contract-reviewer + contract-editor skill) 4. 完成后交付到任务交付目录 ### 场景二:文件提取/处理 用户发文件并要求提取内容、翻译、格式转换等: 1. 从消息中获取文件保存路径 2. 直接在本地处理(读取docx/pdf内容、提取表格等) 3. 按用户指示输出结果(生成新文件、上传到指定目录等) ### 场景三:文件转存 用户发文件并要求存到某个目录: 1. 从消息中获取文件保存路径 2. docker cp 到 Nextcloud 指定目录 3. chown + occ files:scan 同步 ## 文件不在cache——检查Nextcloud直传 当用户说"我上传了XX文件"但 `~/.hermes/cache/documents/` 里没有时,文件可能是通过Nextcloud网页/客户端直接上传的(不经过企微)。 ### 排查步骤 1. **先扫描Nextcloud**刷新文件索引: ```bash sudo docker exec nextcloud-nextcloud-1 php occ files:scan doro --path="/doro/files/<预期目录>" ``` 2. **按文件名关键词搜索**整个Doro目录树: ```bash sudo docker exec nextcloud-nextcloud-1 bash -c 'find /var/www/html/data/doro/files/ -name "*关键词*"' ``` 3. **检查 `.part` 文件**——Nextcloud分块上传的临时文件: ```bash sudo docker exec nextcloud-nextcloud-1 bash -c 'find /var/www/html/data/doro/files/ -name "*.part"' ``` - 文件名格式:`.ocTransferId.part` - `.part` 文件存在 = 上传正在进行中 - 大小在增长 = 还在传,等它完成 - `.part` 消失且没有新文件出现 = **上传失败/取消**,通知用户重新上传 ### ⚠️ 注意事项 - `.part` 文件不会被 `occ files:scan` 发现(Nextcloud不索引临时文件),必须用 `find` 直接查文件系统 - 大文件上传可能很慢(通过Nextcloud网页上传取决于用户网速),不要过早判断失败 - 上传失败时`.part`文件会被Nextcloud自动清理,不留痕迹 - 确认上传失败后,告知用户"文件上传似乎中断了,请重新上传" ## 文件名处理 - cache中的文件名经过URL编码,需要解码还原原始文件名 - 上传到Nextcloud时使用原始文件名(从消息中提取) - 遵循文件命名规则skill(file-naming-convention) ## 注意事项 1. **企微群文件限制**:文件消息无法同时@人,必须通过引用回复触发 2. **URL有效期**:企微文件下载URL有效期约5分钟,网关收到后立即下载,不存在过期问题 3. **AES加密**:企微文件传输经过AES加密,网关自动解密(已修复base64 padding问题) 4. **图片也支持**:图片同样通过引用回复方式接收,保存到cache/images/目录 ## 审计:统计某天某人发了多少文件 当Doro问"邱律师昨天发了多少合同"时,**不能靠Nextcloud文件时间戳或cache/documents/时间戳**。原因: - Nextcloud待审查目录的文件是小Maggie上传的,时间是上传时间不是接收时间 - 有些文件是更早期的积压件(如班组慰问品、夏阳代建在6月5日就在Nextcloud了) - cache/documents/文件可能已被清理 ### 方法1:查Nextcloud数据库(最可靠) 直接查MariaDB获取精确的存入时间和操作人。详见 `references/nextcloud-db-queries.md`。 ```bash # 查文件存入时间 docker exec nextcloud-db-1 mariadb -u nextcloud -p'Nc2026Db!Szw' nextcloud \ -e "SELECT fileid, path, FROM_UNIXTIME(storage_mtime) as stored_utc FROM oc_filecache WHERE path LIKE '%关键词%';" # 查是谁上传的(仅限通过Nextcloud UI上传的文件) docker exec nextcloud-db-1 mariadb -u nextcloud -p'Nc2026Db!Szw' nextcloud \ -e "SELECT FROM_UNIXTIME(timestamp) as time_utc, user, subject, file FROM oc_activity WHERE file LIKE '%关键词%';" ``` ⚠️ docker cp + occ files:scan 上传的文件不会产生 oc_activity 记录。无记录=自动流程上传。 ### 方法2:查gateway日志中的接收确认记录 ```bash # 北京时间6月8日 = UTC Jun 7 16:00 ~ Jun 8 16:00 journalctl --user -u hermes-gateway \ --since "2026-06-07 16:00:00" --until "2026-06-08 16:00:00" \ | grep "收到邱律师私信发来\|邱律师好.*文件已收到" ``` 每条匹配 = 一次文件接收。注意: - 同一份合同可能发了多次(如复达合同发了2次),要区分"文件发送次数"和"不同合同数" - 时区转换:服务器UTC,Doro问的是北京时间,必须先转换再查 - 不要把上传/审查/交付时间当成接收时间 ### 教训(2026-06-09) Doro问"邱律师昨天发了多少合同",小Maggie第一次回答5份(基于Nextcloud文件时间戳),被Doro纠正。查gateway日志后确认是6次文件发送、5份不同合同。 ## Gateway断开期间的文件恢复 **问题**:企微WebSocket断开期间,私信文件消息收不到。 **症状**:`journalctl --user -u hermes-gateway | grep "WeCom.*websocket.*closed"` **恢复方法**: 1. 重启gateway:`systemctl --user restart hermes-gateway` 2. WebSocket重连后,企微服务端通常会重新推送断开期间的消息(但不100%可靠) 3. 重启后检查cache/documents/是否有新文件到达 4. 如果文件没补回来,检查Nextcloud待审查目录是否已有同名文件(可能之前session已上传) 5. 都没有→私信邱律师:"合同X的文件未收到,请重新发送" **预防**:cron每15分钟检查WebSocket健康,发现断开及时重启,缩短丢消息窗口。 ## needs_clarification处理(问邱律师,不问Doro) 当classifier无法判断顾问单位(如甲方信息空白)返回needs_clarification时: 1. **私信邱律师**询问:"合同X甲方信息空白,请确认甲方单位" 2. 不问Doro——Doro是最终确认人,不应被拉进执行环节 3. 邱律师回复后,带补充信息创建新thread重新跑 4. 同时跳过该合同,继续处理其他排队的合同