diff --git a/server/services/wechat/AGENTS.md b/server/services/wechat/AGENTS.md new file mode 100644 index 0000000..728ecc4 --- /dev/null +++ b/server/services/wechat/AGENTS.md @@ -0,0 +1,58 @@ +# AGENTS.md — 微信自动回复 bot 维护入口 + +> 接手维护这台 Pixel 微信自动回复 bot 的 agent,先读这份,再读 `HANDOFF.md`(完整细节,尤其 **§零 环境须知**)。 +> 这个文件夹 `~/hermes-wechat/` 同时是**运行时**和**唯一源**(手机上无 git,改完要同步回 Mac repo)。 + +## 这是什么 + +Pixel 本机的微信自动回复机器人(账号「小火子」`wxid_51ap3m6p9xs722`)。链路: +轮询微信加密 DB(sqlcipher)拿新消息 → 调本机 hermes 网关 `127.0.0.1:8642` 生成回复 → `am broadcast` 触发 `HermesAccessibilityService` → A11y UI 自动化点微信(搜联系人→进聊天→输入→发送)。**唯一能真把消息发出去的方式 = A11y UI 自动化。** + +## 协作分工(混合模式:claude-run + hermes) + +| 角色 | 跑在 | 能力 | 干什么 | +|------|------|------|--------| +| **claude-run(Claude)= 脑子** | proot 容器 | 能编辑本目录 `*.js`、读 `auto-reply.log`、读改 `.whitelist.json`;**不能 su** | 写 / 改 / 审代码、看日志诊断 | +| **hermes(Kimi)= 手** | Termux(能 su) | 能 `su -c` 拿 Magisk root | 跑需要 root 的设备操作:重启 bot、查微信 DB、发测试消息、logcat、重开 A11y | + +> claude-run 在 proot 里够不到 Magisk root,也看不到微信 DB(没挂载)。需要 root 的步骤交给 hermes 或 Termux shell 执行。 + +## 核心文件(bot 实跑只这三个,别被目录里其他文件带偏) + +- `auto-reply.js` — 主程序(消息 FSM、白名单门禁/授权、流式 SSE、引用回复 normalize) +- `android-wechat-tracker-sqlcipher.js` — 轮询微信加密 `EnMicroMsg.db` 检测新消息 +- `android-wechat-sender.js` — `am broadcast` → A11y service 发送 +- `HANDOFF.md` — 完整部署地图 / 命令速查 / 避雷清单 / 架构 / 白名单授权 +- 其余 `index.js / wechat-*.js / *-frida.js / *-sqlite.js / test-*.js / *.apk / *.dex` **都不参与 bot 运行** + +## 环境铁律(细节见 HANDOFF §零) + +1. **bot 以 root 跑、agent 以 u0_a327 跑** → 看进程 / 读 root 文件 / 查微信 DB / 重启 都要 `su -c`(只有 Termux 侧能 su;proot 里的 claude-run 不行)。 +2. **唯一 live 日志 = 本目录 `auto-reply.log`**。别处任何同名副本都是死的(曾坑过接手者误判「bot 没在跑」)。 +3. claude-run 的 Bash 若报 `EACCES: permission denied, mkdir '/root/.claude/session-env'`:是 `~/.claude` 被 chown 成 root 了。在 **Termux shell**(不是 claude)跑 `su -c 'chown -R u0_a327:u0_a327 /data/data/com.termux/files/home/.claude'`,再重开 claude-run。 + +## 改代码 → 生效(内循环) + +1. **改**:claude-run 直接编辑本目录 `*.js`。 +2. **重启**(需 root → hermes / Termux 执行):`su -c 'pkill -f auto-reply.js'` → keepalive(每 15s 巡检)自动用新代码以 root 重启。 + 确认新进程:`su -c 'ps -ef | grep -E node.auto-reply.js | grep -v grep'`(**别用 `pgrep -f`,会自匹配 su shell**)。 +3. **看**:`tail -f ~/hermes-wechat/auto-reply.log`(claude-run 不带 su 也能读,日志 644)。 +4. **同步回 Mac**:手机无 git,改完把改动同步回 Mac repo `server/services/wechat/`,否则下次 Mac 跑 `deploy-to-phone.sh` 会覆盖你的改动。 + +## 调试设备状态的常用 su 命令(交给 hermes / Termux) + +```bash +# 查微信 DB 最近消息(确认 tracker 是否捕获/被过滤) +su -c 'cp /data/data/com.tencent.mm/MicroMsg/653cdc2320abb089e149a3f7a7683fcf/EnMicroMsg.db ~/_chk.db' +sqlcipher ~/_chk.db "PRAGMA key=\"e03ce81\"; PRAGMA cipher_compatibility=1; SELECT msgId,type,isSend,talker,substr(content,1,60) FROM message ORDER BY msgId DESC LIMIT 10;" +# 手动发测试消息 +su -c "am broadcast -a com.hermes.wechat.SEND_MSG --es toUser yibooo --es toUserDisplayName 火星人 --es content 'test'" +# A11y service 实际 UI 操作日志 +su -c 'logcat -s HermesA11y:*' +# A11y 被关了重新启用 +su -c 'settings put secure enabled_accessibility_services com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService; settings put secure accessibility_enabled 1' +``` + +## 已知 bug / TODO + +见 `HANDOFF.md` §八(避雷清单)和 §七(待办)。bot 回复模型当前是 hermes 默认的 Kimi-k2.6(claude-run 用 Claude 写代码;hermes 因 Anthropic 第三方计费策略不复用订阅 token)。 diff --git a/server/services/wechat/CLAUDE.md b/server/services/wechat/CLAUDE.md new file mode 100644 index 0000000..db4972d --- /dev/null +++ b/server/services/wechat/CLAUDE.md @@ -0,0 +1,10 @@ +# CLAUDE.md + +微信自动回复 bot 目录。接手维护 / 调试前,**先读 `AGENTS.md`(入口分工 + 环境铁律)和 `HANDOFF.md`(完整细节)**。 + +要点(细节见那两份): +- 这个文件夹在 Pixel 上是 `~/hermes-wechat/`,既是运行时也是唯一源(手机无 git,改完同步回此 Mac repo 目录)。 +- 混合分工:**claude-run(你,Claude)改代码**;**hermes(Termux,能 su)跑需要 root 的设备操作**(重启 bot / 查微信 DB / 发测试 / logcat)。 +- bot 以 root 跑、agent 以 u0_a327 跑 → 需 root 的步骤都要 `su -c`,proot 里的 claude-run 做不了,交给 hermes / Termux。 +- bot 实跑只依赖 `auto-reply.js` + `android-wechat-tracker-sqlcipher.js` + `android-wechat-sender.js`。 +- 唯一 live 日志 = 本目录 `auto-reply.log`。 diff --git a/server/services/wechat/HANDOFF.md b/server/services/wechat/HANDOFF.md index 3bc1baa..f8a7598 100644 --- a/server/services/wechat/HANDOFF.md +++ b/server/services/wechat/HANDOFF.md @@ -3,8 +3,31 @@ > **谁会读这份文档**:刚接手这台 Pixel 部署的 claude-run / 开发者,没有上下文。 > **目标**:5 分钟搞清楚正在跑什么、文件在哪、怎么改、怎么验证。 > -> **生成时间**:2026-05-25(v3,加 FSM / 流式 / hermes 补丁 / 白名单授权 章节)。请定期核对实际状态, -> 因为本文件不会随代码自动更新。 +> **生成时间**:2026-05-25(v4:加 §零「claude-run 环境须知」;删除 lemon-acp 完整 clone, +> 运行时收敛为单一 `~/hermes-wechat/`;deploy 脚本修正为推真正的 runtime 文件;isQuoteMe 引用回复修复)。 +> 请定期核对实际状态,因为本文件不会随代码自动更新。 + +--- + +## 零、claude-run 接手前必读(环境须知) + +**你(手机上的 claude-run)以 `u0_a327`(Termux app) 身份运行;bot 以 root 运行。** 这条差异是大多数"没权限 / 看不到进程 / 改不动文件"问题的根源: + +1. **Bash 工具起不来,报 `EACCES: permission denied, mkdir '/root/.claude/session-env'`** + → `.claude` 目录被 chown 成了 root,而你的真实 uid 是 u0_a327,写不进去。在**电脑端 adb** 或别的能 su 的 shell 跑一次: + ```bash + su -c 'chown -R u0_a327:u0_a327 /data/data/com.termux/files/home/.claude' + ``` + 然后重开 claude-run 会话。(proot 里你"看起来"是 root,但 proot 对真实文件系统的操作用的是 u0_a327。) + +2. **bot 跑在 root**:要看 bot 进程、读它写的 root 属主文件(`auto-reply.log` / `.whitelist.json` / `.auto-reply.pid`)、查微信加密 DB、重启 bot——**全部要套 `su -c`**。Termux 有 Magisk root,u0_a327 可以 `su -c`。直接不带 su 的 `ps` / `cat` 会看不到 root 进程、读不到 root 文件。 + +3. **运行时只有一处:`~/hermes-wechat/`。** 这里的 `auto-reply.log` 是**唯一的真 live 日志**。 + ⚠️ 历史上有人在 `~/lemon-acp/server/services/wechat/` 留过一份完整 clone,里面的 `auto-reply.log` 是**永不运行的死副本**——曾导致接手者读错日志、误判"bot 没在跑"。该 clone 已于 v4 删除。**只认 `~/hermes-wechat/`。** + +4. **bot 实跑只依赖 3 个文件**:`auto-reply.js` + `android-wechat-tracker-sqlcipher.js` + `android-wechat-sender.js`(`auto-reply.js` 只 require 这俩)。其余 `index.js / wechat-tracker.js / wechat-reader.js / *-frida.js / *-sqlite.js` 都不参与 bot 运行,别被带偏。 + +5. **改代码 → 生效**:`~/hermes-wechat/*.js` 现在属主是 u0_a327,你可以直接编辑。改完 `su -c 'pkill -f auto-reply.js'`,keepalive(每 15s 巡检)会以 root 自动用新代码重启。改完记得把改动同步回 Mac repo(见 §六)。 --- @@ -36,7 +59,8 @@ adb shell "su -c 'pgrep -x com.tencent.mm'" # 微信主进程 | 进程 | 启动方 | 作用 | |------|--------|------| -| `node auto-reply.js` | `~/hermes-wechat/start-ar.sh` 通过 `su -c` 起,**以 root 运行** | 微信回复主程序 | +| `bash keepalive.sh` | 以 u0_a327 跑(`nohup bash ~/hermes-wechat/keepalive.sh &`),**守护进程**,每 15s 巡检 | 挂了就重启下面四项:wake-lock / A11y / gateway / auto-reply | +| `node auto-reply.js` | **由 keepalive.sh 通过 `su -c` 内联 env 直接起,以 root 运行**(不走 start-ar.sh 的 dash wrapper,避开 shell snapshot hook 污染) | 微信回复主程序 | | `python -m gateway.run` | `~/.hermes/scripts/start-gateway.sh`,**以 termux 用户 u0_a327 运行** | hermes 网关,监听 `127.0.0.1:8642` | | `com.tencent.mm` | 系统 | 微信前台 UI,A11y 操作目标 | | `HermesAccessibilityService` | Android AccessibilityManager 系统拉起 | **链路核心**:执行 UI 自动化 | @@ -46,8 +70,9 @@ adb shell "su -c 'pgrep -x com.tencent.mm'" # 微信主进程 ## 二、文件位置 / 与 Mac repo 对应 -> Mac repo: `git@gitlab-wuwei.hexpm.com:yibo/pixel-wechat.git`(remote 名 `pixel-wechat`) -> 注意:手机上 **没有 git 仓库**,是 `adb push` 单文件部署的。 +> Mac repo: `server/services/wechat/`(aipcrm 仓库内)。 +> 部署机制:Mac 上跑 `./deploy-to-phone.sh` → 经 `/sdcard` 中转 `dd` 落到 `~/hermes-wechat/` + chown 回 u0_a327。 +> 手机上 **没有 git 仓库**(v4 删了 lemon-acp 完整 clone);`~/hermes-wechat/` 是 `adb push`/dd 部署的运行时,不是 checkout。 ### 2.1 微信回复主程序 @@ -229,34 +254,35 @@ NO_PROXY=127.0.0.1,localhost --- -## 六、给 claude-run 接手的两种开发流程 +## 六、改代码 / 部署 / 重启工作流 -### 方式 1(推荐):在 phone 上 clone 同一个 gitlab 仓库到独立目录 +手机上**没有 git 仓库**,运行时就是 `~/hermes-wechat/`。两种改法,看你在哪端: + +### 方式 A:手机上的 claude-run 直接改(最快,适合现场调试) ```bash -# 在 phone termux shell 里(不要污染现有 ~/hermes-wechat/) -cd ~ -git clone git@gitlab-wuwei.hexpm.com:yibo/pixel-wechat.git pixel-wechat-dev -# 或 HTTPS(需要 PAT): -# git clone https://gitlab-wuwei.hexpm.com/yibo/pixel-wechat.git pixel-wechat-dev - -# 编辑 ~/pixel-wechat-dev/server/services/wechat/*.js -# commit + push -git add -A && git commit -m "..." && git push pixel-wechat main - -# 部署到运行目录 -cp ~/pixel-wechat-dev/server/services/wechat/{auto-reply,android-wechat-sender,android-wechat-tracker-sqlcipher}.js ~/hermes-wechat/ - -# 重启 -pkill -f auto-reply.js -~/hermes-wechat/start-ar.sh & +# 1. ~/hermes-wechat/*.js 属主是 u0_a327,直接用 Edit 改 +# 2. 重启(bot 跑在 root,要 su;keepalive 15s 内会自动重启,手动起也行) +su -c 'pkill -f auto-reply.js' +# 确认新进程(不能用 pgrep -f,会自匹配 su shell) +su -c 'ps -ef | grep -E node.auto-reply.js | grep -v grep' +# 3. 看 live 日志确认起来了 +su -c 'tail -f ~/hermes-wechat/auto-reply.log' +# 4. ⚠️ 改动同步回 Mac repo(否则下次 Mac deploy 会覆盖你的改动): +# 把改过的文件内容贴回 Mac,或 adb pull 回 server/services/wechat/ 后 commit ``` -> 第一次 push 需要在 phone 上配 git 用户 + SSH key 推到 gitlab,或者用 HTTPS PAT。 +### 方式 B:在 Mac 改 + deploy(适合正式提交) -### 方式 2:直接改 `~/hermes-wechat/` 下文件 + 手动 scp 回 Mac 提交 +```bash +# Mac 上编辑 server/services/wechat/*.js,然后: +cd server/services/wechat && ./deploy-to-phone.sh # 推 runtime+文档,自动 chown+md5 校验 +adb shell "su -c 'pkill -f auto-reply.js'" # 重启,keepalive 自动拉起 +git add -A && git commit -m "..." # 提交到 aipcrm 仓库 +``` -简单但容易和 Mac 端开发冲突,**不推荐多人协作**。 +> **冲突警告**:方式 A(手机直接改)和方式 B(Mac 改 deploy)会互相覆盖。同一时间只用一种。 +> 手机现场改完一定要尽快同步回 Mac,因为 Mac 端 `deploy-to-phone.sh` 会无条件覆盖手机文件。 --- @@ -301,6 +327,7 @@ pkill -f auto-reply.js | auto-reply node 进程被 keepalive 反复重启,CPU 占用高、log 文件被洪水般写入 | 流式场景下高频 `message.delta`/`reasoning.delta` 事件触发同步 `fs.appendFileSync` + 每条都 console.log,sync I/O 卡死 node;keepalive 检测心跳超时 → kill → restart | `log()` 去掉 `appendFileSync`(stdout 已由 start 脚本重定向到 log file);诊断事件加节流(高频事件 5 秒打一次) | | Hermes `/v1/runs` 只输出"最终一条回复",没多步进度 | hermes 默认 `/v1/runs` 只挂 `stream_delta_callback` + `tool_progress_callback`,缺 `reasoning_callback` + `interim_assistant_callback`(gateway 平台用 7+ 个回调) | 补丁 `gateway/platforms/api_server.py`,在 `_create_agent` 签名 + AIAgent 构造 + run handler 三处都加上缺的两个回调,emit `reasoning.delta` / `message.complete` SSE 事件。详见 §十二.3 | | owner 回「同意」授权后没生效、pending 没清 | owner 用微信**引用回复**发「同意」,content 是 type=57 appmsg XML(`