Commit 0a537853df7185c76a275eb43fbe3c575eac5d59
1 parent
1c662a14
feat: 微信 bot 手机端接手配置 — isQuoteMe 修复 + deploy 修正 + 接手文档
- fix(auto-reply): 群引用回复改用 rawContent 调 isQuoteMe。normalize 已剥掉 <refermsg> XML,而 isQuoteMe 需原始 XML 判断引用的是不是机器人,否则永远 false → 只引用不 @ 的回复被静默忽略([NORMALIZE] 之后无下文) - deploy-to-phone.sh: 修正推送清单(原漏了 auto-reply.js / sqlcipher tracker, 从未真正部署过运行时代码);改走 /sdcard dd + chown u0_a327 + md5 校验 - HANDOFF.md v4: 加 §零「claude-run 环境须知」(.claude 被 chown root 致 Bash 全挂 / 唯一 live 日志 / bot root vs agent u0_a327 须用 su);删 lemon-acp clone - AGENTS.md + CLAUDE.md: agent 一次性可读的接手入口(claude-run 改代码 + hermes 跑 su 操作 的混合分工) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Showing
5 changed files
with
160 additions
and
55 deletions
server/services/wechat/AGENTS.md
0 → 100644
| 1 | +# AGENTS.md — 微信自动回复 bot 维护入口 | |
| 2 | + | |
| 3 | +> 接手维护这台 Pixel 微信自动回复 bot 的 agent,先读这份,再读 `HANDOFF.md`(完整细节,尤其 **§零 环境须知**)。 | |
| 4 | +> 这个文件夹 `~/hermes-wechat/` 同时是**运行时**和**唯一源**(手机上无 git,改完要同步回 Mac repo)。 | |
| 5 | + | |
| 6 | +## 这是什么 | |
| 7 | + | |
| 8 | +Pixel 本机的微信自动回复机器人(账号「小火子」`wxid_51ap3m6p9xs722`)。链路: | |
| 9 | +轮询微信加密 DB(sqlcipher)拿新消息 → 调本机 hermes 网关 `127.0.0.1:8642` 生成回复 → `am broadcast` 触发 `HermesAccessibilityService` → A11y UI 自动化点微信(搜联系人→进聊天→输入→发送)。**唯一能真把消息发出去的方式 = A11y UI 自动化。** | |
| 10 | + | |
| 11 | +## 协作分工(混合模式:claude-run + hermes) | |
| 12 | + | |
| 13 | +| 角色 | 跑在 | 能力 | 干什么 | | |
| 14 | +|------|------|------|--------| | |
| 15 | +| **claude-run(Claude)= 脑子** | proot 容器 | 能编辑本目录 `*.js`、读 `auto-reply.log`、读改 `.whitelist.json`;**不能 su** | 写 / 改 / 审代码、看日志诊断 | | |
| 16 | +| **hermes(Kimi)= 手** | Termux(能 su) | 能 `su -c` 拿 Magisk root | 跑需要 root 的设备操作:重启 bot、查微信 DB、发测试消息、logcat、重开 A11y | | |
| 17 | + | |
| 18 | +> claude-run 在 proot 里够不到 Magisk root,也看不到微信 DB(没挂载)。需要 root 的步骤交给 hermes 或 Termux shell 执行。 | |
| 19 | + | |
| 20 | +## 核心文件(bot 实跑只这三个,别被目录里其他文件带偏) | |
| 21 | + | |
| 22 | +- `auto-reply.js` — 主程序(消息 FSM、白名单门禁/授权、流式 SSE、引用回复 normalize) | |
| 23 | +- `android-wechat-tracker-sqlcipher.js` — 轮询微信加密 `EnMicroMsg.db` 检测新消息 | |
| 24 | +- `android-wechat-sender.js` — `am broadcast` → A11y service 发送 | |
| 25 | +- `HANDOFF.md` — 完整部署地图 / 命令速查 / 避雷清单 / 架构 / 白名单授权 | |
| 26 | +- 其余 `index.js / wechat-*.js / *-frida.js / *-sqlite.js / test-*.js / *.apk / *.dex` **都不参与 bot 运行** | |
| 27 | + | |
| 28 | +## 环境铁律(细节见 HANDOFF §零) | |
| 29 | + | |
| 30 | +1. **bot 以 root 跑、agent 以 u0_a327 跑** → 看进程 / 读 root 文件 / 查微信 DB / 重启 都要 `su -c`(只有 Termux 侧能 su;proot 里的 claude-run 不行)。 | |
| 31 | +2. **唯一 live 日志 = 本目录 `auto-reply.log`**。别处任何同名副本都是死的(曾坑过接手者误判「bot 没在跑」)。 | |
| 32 | +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。 | |
| 33 | + | |
| 34 | +## 改代码 → 生效(内循环) | |
| 35 | + | |
| 36 | +1. **改**:claude-run 直接编辑本目录 `*.js`。 | |
| 37 | +2. **重启**(需 root → hermes / Termux 执行):`su -c 'pkill -f auto-reply.js'` → keepalive(每 15s 巡检)自动用新代码以 root 重启。 | |
| 38 | + 确认新进程:`su -c 'ps -ef | grep -E node.auto-reply.js | grep -v grep'`(**别用 `pgrep -f`,会自匹配 su shell**)。 | |
| 39 | +3. **看**:`tail -f ~/hermes-wechat/auto-reply.log`(claude-run 不带 su 也能读,日志 644)。 | |
| 40 | +4. **同步回 Mac**:手机无 git,改完把改动同步回 Mac repo `server/services/wechat/`,否则下次 Mac 跑 `deploy-to-phone.sh` 会覆盖你的改动。 | |
| 41 | + | |
| 42 | +## 调试设备状态的常用 su 命令(交给 hermes / Termux) | |
| 43 | + | |
| 44 | +```bash | |
| 45 | +# 查微信 DB 最近消息(确认 tracker 是否捕获/被过滤) | |
| 46 | +su -c 'cp /data/data/com.tencent.mm/MicroMsg/653cdc2320abb089e149a3f7a7683fcf/EnMicroMsg.db ~/_chk.db' | |
| 47 | +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;" | |
| 48 | +# 手动发测试消息 | |
| 49 | +su -c "am broadcast -a com.hermes.wechat.SEND_MSG --es toUser yibooo --es toUserDisplayName 火星人 --es content 'test'" | |
| 50 | +# A11y service 实际 UI 操作日志 | |
| 51 | +su -c 'logcat -s HermesA11y:*' | |
| 52 | +# A11y 被关了重新启用 | |
| 53 | +su -c 'settings put secure enabled_accessibility_services com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService; settings put secure accessibility_enabled 1' | |
| 54 | +``` | |
| 55 | + | |
| 56 | +## 已知 bug / TODO | |
| 57 | + | |
| 58 | +见 `HANDOFF.md` §八(避雷清单)和 §七(待办)。bot 回复模型当前是 hermes 默认的 Kimi-k2.6(claude-run 用 Claude 写代码;hermes 因 Anthropic 第三方计费策略不复用订阅 token)。 | ... | ... |
server/services/wechat/CLAUDE.md
0 → 100644
| 1 | +# CLAUDE.md | |
| 2 | + | |
| 3 | +微信自动回复 bot 目录。接手维护 / 调试前,**先读 `AGENTS.md`(入口分工 + 环境铁律)和 `HANDOFF.md`(完整细节)**。 | |
| 4 | + | |
| 5 | +要点(细节见那两份): | |
| 6 | +- 这个文件夹在 Pixel 上是 `~/hermes-wechat/`,既是运行时也是唯一源(手机无 git,改完同步回此 Mac repo 目录)。 | |
| 7 | +- 混合分工:**claude-run(你,Claude)改代码**;**hermes(Termux,能 su)跑需要 root 的设备操作**(重启 bot / 查微信 DB / 发测试 / logcat)。 | |
| 8 | +- bot 以 root 跑、agent 以 u0_a327 跑 → 需 root 的步骤都要 `su -c`,proot 里的 claude-run 做不了,交给 hermes / Termux。 | |
| 9 | +- bot 实跑只依赖 `auto-reply.js` + `android-wechat-tracker-sqlcipher.js` + `android-wechat-sender.js`。 | |
| 10 | +- 唯一 live 日志 = 本目录 `auto-reply.log`。 | ... | ... |
server/services/wechat/HANDOFF.md
| ... | ... | @@ -3,8 +3,31 @@ |
| 3 | 3 | > **谁会读这份文档**:刚接手这台 Pixel 部署的 claude-run / 开发者,没有上下文。 |
| 4 | 4 | > **目标**:5 分钟搞清楚正在跑什么、文件在哪、怎么改、怎么验证。 |
| 5 | 5 | > |
| 6 | -> **生成时间**:2026-05-25(v3,加 FSM / 流式 / hermes 补丁 / 白名单授权 章节)。请定期核对实际状态, | |
| 7 | -> 因为本文件不会随代码自动更新。 | |
| 6 | +> **生成时间**:2026-05-25(v4:加 §零「claude-run 环境须知」;删除 lemon-acp 完整 clone, | |
| 7 | +> 运行时收敛为单一 `~/hermes-wechat/`;deploy 脚本修正为推真正的 runtime 文件;isQuoteMe 引用回复修复)。 | |
| 8 | +> 请定期核对实际状态,因为本文件不会随代码自动更新。 | |
| 9 | + | |
| 10 | +--- | |
| 11 | + | |
| 12 | +## 零、claude-run 接手前必读(环境须知) | |
| 13 | + | |
| 14 | +**你(手机上的 claude-run)以 `u0_a327`(Termux app) 身份运行;bot 以 root 运行。** 这条差异是大多数"没权限 / 看不到进程 / 改不动文件"问题的根源: | |
| 15 | + | |
| 16 | +1. **Bash 工具起不来,报 `EACCES: permission denied, mkdir '/root/.claude/session-env'`** | |
| 17 | + → `.claude` 目录被 chown 成了 root,而你的真实 uid 是 u0_a327,写不进去。在**电脑端 adb** 或别的能 su 的 shell 跑一次: | |
| 18 | + ```bash | |
| 19 | + su -c 'chown -R u0_a327:u0_a327 /data/data/com.termux/files/home/.claude' | |
| 20 | + ``` | |
| 21 | + 然后重开 claude-run 会话。(proot 里你"看起来"是 root,但 proot 对真实文件系统的操作用的是 u0_a327。) | |
| 22 | + | |
| 23 | +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 文件。 | |
| 24 | + | |
| 25 | +3. **运行时只有一处:`~/hermes-wechat/`。** 这里的 `auto-reply.log` 是**唯一的真 live 日志**。 | |
| 26 | + ⚠️ 历史上有人在 `~/lemon-acp/server/services/wechat/` 留过一份完整 clone,里面的 `auto-reply.log` 是**永不运行的死副本**——曾导致接手者读错日志、误判"bot 没在跑"。该 clone 已于 v4 删除。**只认 `~/hermes-wechat/`。** | |
| 27 | + | |
| 28 | +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 运行,别被带偏。 | |
| 29 | + | |
| 30 | +5. **改代码 → 生效**:`~/hermes-wechat/*.js` 现在属主是 u0_a327,你可以直接编辑。改完 `su -c 'pkill -f auto-reply.js'`,keepalive(每 15s 巡检)会以 root 自动用新代码重启。改完记得把改动同步回 Mac repo(见 §六)。 | |
| 8 | 31 | |
| 9 | 32 | --- |
| 10 | 33 | |
| ... | ... | @@ -36,7 +59,8 @@ adb shell "su -c 'pgrep -x com.tencent.mm'" # 微信主进程 |
| 36 | 59 | |
| 37 | 60 | | 进程 | 启动方 | 作用 | |
| 38 | 61 | |------|--------|------| |
| 39 | -| `node auto-reply.js` | `~/hermes-wechat/start-ar.sh` 通过 `su -c` 起,**以 root 运行** | 微信回复主程序 | | |
| 62 | +| `bash keepalive.sh` | 以 u0_a327 跑(`nohup bash ~/hermes-wechat/keepalive.sh &`),**守护进程**,每 15s 巡检 | 挂了就重启下面四项:wake-lock / A11y / gateway / auto-reply | | |
| 63 | +| `node auto-reply.js` | **由 keepalive.sh 通过 `su -c` 内联 env 直接起,以 root 运行**(不走 start-ar.sh 的 dash wrapper,避开 shell snapshot hook 污染) | 微信回复主程序 | | |
| 40 | 64 | | `python -m gateway.run` | `~/.hermes/scripts/start-gateway.sh`,**以 termux 用户 u0_a327 运行** | hermes 网关,监听 `127.0.0.1:8642` | |
| 41 | 65 | | `com.tencent.mm` | 系统 | 微信前台 UI,A11y 操作目标 | |
| 42 | 66 | | `HermesAccessibilityService` | Android AccessibilityManager 系统拉起 | **链路核心**:执行 UI 自动化 | |
| ... | ... | @@ -46,8 +70,9 @@ adb shell "su -c 'pgrep -x com.tencent.mm'" # 微信主进程 |
| 46 | 70 | |
| 47 | 71 | ## 二、文件位置 / 与 Mac repo 对应 |
| 48 | 72 | |
| 49 | -> Mac repo: `git@gitlab-wuwei.hexpm.com:yibo/pixel-wechat.git`(remote 名 `pixel-wechat`) | |
| 50 | -> 注意:手机上 **没有 git 仓库**,是 `adb push` 单文件部署的。 | |
| 73 | +> Mac repo: `server/services/wechat/`(aipcrm 仓库内)。 | |
| 74 | +> 部署机制:Mac 上跑 `./deploy-to-phone.sh` → 经 `/sdcard` 中转 `dd` 落到 `~/hermes-wechat/` + chown 回 u0_a327。 | |
| 75 | +> 手机上 **没有 git 仓库**(v4 删了 lemon-acp 完整 clone);`~/hermes-wechat/` 是 `adb push`/dd 部署的运行时,不是 checkout。 | |
| 51 | 76 | |
| 52 | 77 | ### 2.1 微信回复主程序 |
| 53 | 78 | |
| ... | ... | @@ -229,34 +254,35 @@ NO_PROXY=127.0.0.1,localhost |
| 229 | 254 | |
| 230 | 255 | --- |
| 231 | 256 | |
| 232 | -## 六、给 claude-run 接手的两种开发流程 | |
| 257 | +## 六、改代码 / 部署 / 重启工作流 | |
| 233 | 258 | |
| 234 | -### 方式 1(推荐):在 phone 上 clone 同一个 gitlab 仓库到独立目录 | |
| 259 | +手机上**没有 git 仓库**,运行时就是 `~/hermes-wechat/`。两种改法,看你在哪端: | |
| 260 | + | |
| 261 | +### 方式 A:手机上的 claude-run 直接改(最快,适合现场调试) | |
| 235 | 262 | |
| 236 | 263 | ```bash |
| 237 | -# 在 phone termux shell 里(不要污染现有 ~/hermes-wechat/) | |
| 238 | -cd ~ | |
| 239 | -git clone git@gitlab-wuwei.hexpm.com:yibo/pixel-wechat.git pixel-wechat-dev | |
| 240 | -# 或 HTTPS(需要 PAT): | |
| 241 | -# git clone https://gitlab-wuwei.hexpm.com/yibo/pixel-wechat.git pixel-wechat-dev | |
| 242 | - | |
| 243 | -# 编辑 ~/pixel-wechat-dev/server/services/wechat/*.js | |
| 244 | -# commit + push | |
| 245 | -git add -A && git commit -m "..." && git push pixel-wechat main | |
| 246 | - | |
| 247 | -# 部署到运行目录 | |
| 248 | -cp ~/pixel-wechat-dev/server/services/wechat/{auto-reply,android-wechat-sender,android-wechat-tracker-sqlcipher}.js ~/hermes-wechat/ | |
| 249 | - | |
| 250 | -# 重启 | |
| 251 | -pkill -f auto-reply.js | |
| 252 | -~/hermes-wechat/start-ar.sh & | |
| 264 | +# 1. ~/hermes-wechat/*.js 属主是 u0_a327,直接用 Edit 改 | |
| 265 | +# 2. 重启(bot 跑在 root,要 su;keepalive 15s 内会自动重启,手动起也行) | |
| 266 | +su -c 'pkill -f auto-reply.js' | |
| 267 | +# 确认新进程(不能用 pgrep -f,会自匹配 su shell) | |
| 268 | +su -c 'ps -ef | grep -E node.auto-reply.js | grep -v grep' | |
| 269 | +# 3. 看 live 日志确认起来了 | |
| 270 | +su -c 'tail -f ~/hermes-wechat/auto-reply.log' | |
| 271 | +# 4. ⚠️ 改动同步回 Mac repo(否则下次 Mac deploy 会覆盖你的改动): | |
| 272 | +# 把改过的文件内容贴回 Mac,或 adb pull 回 server/services/wechat/ 后 commit | |
| 253 | 273 | ``` |
| 254 | 274 | |
| 255 | -> 第一次 push 需要在 phone 上配 git 用户 + SSH key 推到 gitlab,或者用 HTTPS PAT。 | |
| 275 | +### 方式 B:在 Mac 改 + deploy(适合正式提交) | |
| 256 | 276 | |
| 257 | -### 方式 2:直接改 `~/hermes-wechat/` 下文件 + 手动 scp 回 Mac 提交 | |
| 277 | +```bash | |
| 278 | +# Mac 上编辑 server/services/wechat/*.js,然后: | |
| 279 | +cd server/services/wechat && ./deploy-to-phone.sh # 推 runtime+文档,自动 chown+md5 校验 | |
| 280 | +adb shell "su -c 'pkill -f auto-reply.js'" # 重启,keepalive 自动拉起 | |
| 281 | +git add -A && git commit -m "..." # 提交到 aipcrm 仓库 | |
| 282 | +``` | |
| 258 | 283 | |
| 259 | -简单但容易和 Mac 端开发冲突,**不推荐多人协作**。 | |
| 284 | +> **冲突警告**:方式 A(手机直接改)和方式 B(Mac 改 deploy)会互相覆盖。同一时间只用一种。 | |
| 285 | +> 手机现场改完一定要尽快同步回 Mac,因为 Mac 端 `deploy-to-phone.sh` 会无条件覆盖手机文件。 | |
| 260 | 286 | |
| 261 | 287 | --- |
| 262 | 288 | |
| ... | ... | @@ -301,6 +327,7 @@ pkill -f auto-reply.js |
| 301 | 327 | | 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 秒打一次) | |
| 302 | 328 | | 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 | |
| 303 | 329 | | owner 回「同意」授权后没生效、pending 没清 | owner 用微信**引用回复**发「同意」,content 是 type=57 appmsg XML(`<title>同意</title>` + `<refermsg>`),不是纯文本,`parseApprovalCommand` 正则匹配不上 | parseApprovalCommand 检测 `<appmsg>`/`<refermsg>` → 提取 `<title>` 作回复文本 + `<refermsg>` 作 quotedContext 匹配目标。详见 §十三.6 | |
| 330 | +| 群里**引用机器人消息但不 @** 的回复(如引用后回「[强]」)被静默忽略、`[NORMALIZE]` 之后再无下文 | `handleMessage` 先把引用回复 normalize 成 `prefix+title`(剥掉 `<refermsg>` XML),却又用这个剥过的 `content` 调 `isQuoteMe`;而 `isQuoteMe` 第一步就 `if(!content.includes('<refermsg'))return false` → 永远 false。只有带 `@小火子` 的引用回复能走 `isAtMe` 命中 | **已修(5/25)**:`auto-reply.js:829` 改成 `isQuoteMe(rawContent, ...)`,用保留的原始 XML 判断引用的是不是机器人(`<refermsg>` 里 `<displayname>` 是否 == myNickname) | | |
| 304 | 331 | |
| 305 | 332 | --- |
| 306 | 333 | ... | ... |
server/services/wechat/auto-reply.js
| ... | ... | @@ -826,7 +826,8 @@ class AutoReplyBot { |
| 826 | 826 | |
| 827 | 827 | if (CONFIG.replyAtInGroup && isAtMe(content, CONFIG.myNickname)) { |
| 828 | 828 | replyType = 'group-at'; |
| 829 | - } else if (CONFIG.replyQuoteInGroup && isQuoteMe(content, CONFIG.myNickname)) { | |
| 829 | + } else if (CONFIG.replyQuoteInGroup && isQuoteMe(rawContent, CONFIG.myNickname)) { | |
| 830 | + // 用 rawContent:normalize 已把 <refermsg> XML 剥掉,isQuoteMe 需要原始 XML 判断引用的是不是机器人 | |
| 830 | 831 | replyType = 'group-quote'; |
| 831 | 832 | } |
| 832 | 833 | } | ... | ... |
server/services/wechat/deploy-to-phone.sh
| 1 | 1 | #!/bin/bash |
| 2 | -# deploy-to-phone.sh — 把 wechat 服务代码推送到 Pixel 手机 | |
| 2 | +# deploy-to-phone.sh — 把 wechat 运行时代码 + 文档推送到 Pixel 的 hermes-wechat 运行目录 | |
| 3 | +# | |
| 4 | +# 在 Mac 上运行。机制:adb push 到 /sdcard 中转 → su dd 落到 /data/data → chown 回 Termux uid。 | |
| 5 | +# - 用 dd 而不是 cp:避开 phone 上 dash shell snapshot hook 往 *.js 末尾追内容的坑 | |
| 6 | +# - chown 回 u0_a327:否则 adb/root 写出来的文件属主是 root,claude-run(u0_a327) 改不动 | |
| 7 | +# - 推完做 md5 校验 | |
| 3 | 8 | # |
| 4 | 9 | # 用法: |
| 5 | 10 | # ./deploy-to-phone.sh [手机目标目录] |
| ... | ... | @@ -9,40 +14,44 @@ set -e |
| 9 | 14 | |
| 10 | 15 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| 11 | 16 | PHONE_DIR="${1:-/data/data/com.termux/files/home/hermes-wechat}" |
| 17 | +TERMUX_UID="${TERMUX_UID:-u0_a327}" # Termux app 的 uid(claude-run 也是它)。换机器需改。 | |
| 18 | + | |
| 19 | +# 运行时必需:auto-reply.js 只 require 这三个文件,bot 实跑就这些 | |
| 20 | +RUNTIME="auto-reply.js android-wechat-tracker-sqlcipher.js android-wechat-sender.js" | |
| 21 | +# 文档(接手 / 维护必读)。AGENTS.md/CLAUDE.md 是 agent 一次性读的入口 | |
| 22 | +DOCS="AGENTS.md CLAUDE.md HANDOFF.md README.md SKILL.md" | |
| 23 | +FILES="$RUNTIME $DOCS" | |
| 24 | + | |
| 25 | +# md5 工具:Mac 用 `md5 -q`,Linux 用 `md5sum` | |
| 26 | +md5_local() { | |
| 27 | + if command -v md5 >/dev/null 2>&1; then md5 -q "$1"; else md5sum "$1" | awk '{print $1}'; fi | |
| 28 | +} | |
| 12 | 29 | |
| 13 | -echo "=== Deploying wechat services to phone ===" | |
| 30 | +echo "=== Deploying wechat runtime + docs to phone ===" | |
| 14 | 31 | echo "Source: $SCRIPT_DIR" |
| 15 | -echo "Target: $PHONE_DIR" | |
| 32 | +echo "Target: $PHONE_DIR (owner -> $TERMUX_UID)" | |
| 16 | 33 | echo "" |
| 17 | 34 | |
| 18 | -# 确保手机目录存在 | |
| 19 | 35 | adb shell "su -c 'mkdir -p $PHONE_DIR'" |
| 20 | 36 | |
| 21 | -# 推送所有 js 文件和 SKILL.md | |
| 22 | -for f in \ | |
| 23 | - android-wechat-sender.js \ | |
| 24 | - android-wechat-tracker.js \ | |
| 25 | - wechat-sender.js \ | |
| 26 | - wechat-tracker.js \ | |
| 27 | - wechat-reader.js \ | |
| 28 | - wechat-importer.js \ | |
| 29 | - index.js \ | |
| 30 | - test-phone.js \ | |
| 31 | - SKILL.md | |
| 32 | -do | |
| 33 | - if [ -f "$SCRIPT_DIR/$f" ]; then | |
| 34 | - echo "Pushing $f ..." | |
| 35 | - adb push "$SCRIPT_DIR/$f" "$PHONE_DIR/$f" >/dev/null | |
| 36 | - fi | |
| 37 | +fail=0 | |
| 38 | +for f in $FILES; do | |
| 39 | + if [ ! -f "$SCRIPT_DIR/$f" ]; then echo "SKIP (missing) $f"; continue; fi | |
| 40 | + echo "Pushing $f ..." | |
| 41 | + adb push "$SCRIPT_DIR/$f" "/sdcard/_deploy_$f" >/dev/null | |
| 42 | + adb shell "su -c 'dd if=/sdcard/_deploy_$f of=$PHONE_DIR/$f 2>/dev/null && chown $TERMUX_UID:$TERMUX_UID $PHONE_DIR/$f && chmod 644 $PHONE_DIR/$f'" | |
| 43 | + adb shell "rm -f /sdcard/_deploy_$f" >/dev/null 2>&1 || true | |
| 44 | + lmd5=$(md5_local "$SCRIPT_DIR/$f") | |
| 45 | + pmd5=$(adb shell "su -c 'md5sum $PHONE_DIR/$f'" | awk '{print $1}' | tr -d '\r') | |
| 46 | + if [ "$lmd5" = "$pmd5" ]; then echo " ok ($lmd5)"; else echo " !! MD5 MISMATCH local=$lmd5 phone=$pmd5"; fail=1; fi | |
| 37 | 47 | done |
| 38 | 48 | |
| 39 | 49 | echo "" |
| 40 | -echo "=== Deploy complete ===" | |
| 41 | -echo "Files in $PHONE_DIR:" | |
| 42 | -adb shell "su -c 'ls -la $PHONE_DIR'" | |
| 50 | +if [ "$fail" = 0 ]; then echo "=== Deploy OK ==="; else echo "=== Deploy had MD5 mismatches — check above ==="; fi | |
| 43 | 51 | echo "" |
| 44 | -echo "Quick test:" | |
| 45 | -echo " adb shell \"su -c 'cd $PHONE_DIR && node -e \\\\\" | |
| 46 | -const {sendTextMessageBroadcast} = require('./android-wechat-sender'); | |
| 47 | -sendTextMessageBroadcast('filehelper', 'hermes phone test').then(r=>console.log(r)); | |
| 48 | -\\\\\"'\"" | |
| 52 | +echo "重启 bot 让新代码生效(keepalive 会在 ~15s 内自动以 root 重新拉起):" | |
| 53 | +echo " adb shell \"su -c 'pkill -f auto-reply.js'\"" | |
| 54 | +echo "确认新进程起来了(不能用 pgrep -f,会自匹配 su shell):" | |
| 55 | +echo " adb shell \"su -c 'ps -ef | grep -E node.auto-reply.js | grep -v grep'\"" | |
| 56 | +echo "看 live 日志(注意:唯一真日志在 hermes-wechat/,别处副本是死的):" | |
| 57 | +echo " adb shell \"su -c 'tail -f $PHONE_DIR/auto-reply.log'\"" | ... | ... |