Commit 71e983e7bbd18c47700ed64880db99d3126e696d

Authored by yibo
1 parent cae03feb

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>
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 &quot;su -c &#39;pgrep -x com.tencent.mm&#39;&quot; # 微信主进程
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 &quot;su -c &#39;pgrep -x com.tencent.mm&#39;&quot; # 微信主进程
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'\""
... ...