Commit 423e2229b5b66ef52fd095d9ab4702461d45b270
1 parent
6470ea10
chore: 清理收尾 — 群昵称查询 + wake-lock 保活 + Frida 残留
#17 群昵称查询:
- android-wechat-tracker-sqlcipher.js:getContactNickname 删掉
if (wxid.endsWith('@chatroom')) return wxid 的早退分支
- 群(xxxx@chatroom)和私聊都走同一 rcontact 表查询,nickname 就是群名
- A11y 现在可以用群名搜索进入群聊
- 在 Pixel 已部署并重启 auto-reply (PID 18678)
#18 wake-lock + keepalive 重写:
- 通过 am startservice com.termux.service_wake_lock 启用 wake-lock
(确认 dumpsys power 已持有 'termux:service-wakelock')
- 重写 keepalive.sh 对齐当前部署:
- 守护 4 项:wake-lock / A11y service / hermes gateway / auto-reply
- hermes 走 termux native gateway 而不是 proot-distro Ubuntu(已淘汰)
- 健康检查端口 8642(不再是 3010)
- A11y 被关掉时自动 settings put secure 重新启用
- 在 Pixel 已部署到 ~/hermes-wechat/keepalive.sh
#16 残留清理(仅 phone 操作,无 git 文件):
- pkill -9 frida-server + rm -rf ~/.frida_queue/
- ~/.hermes/auth.json 删掉 credential_pool['kimi-coding'] 老 sk-kimi 条目
保留 anthropic + kimi-coding-cn
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Showing
2 changed files
with
102 additions
and
96 deletions
server/services/wechat/android-wechat-tracker-sqlcipher.js
| @@ -144,11 +144,7 @@ class AndroidWechatTrackerSQLCipher extends EventEmitter { | @@ -144,11 +144,7 @@ class AndroidWechatTrackerSQLCipher extends EventEmitter { | ||
| 144 | }; | 144 | }; |
| 145 | if (builtin[wxid]) return builtin[wxid]; | 145 | if (builtin[wxid]) return builtin[wxid]; |
| 146 | 146 | ||
| 147 | - // 群聊 ID 走另一个表(chatroom),先简单返回 wxid 让上层处理 | ||
| 148 | - if (wxid.endsWith('@chatroom')) { | ||
| 149 | - return wxid; | ||
| 150 | - } | ||
| 151 | - | 147 | + // 群(xxxx@chatroom)和私聊都用 rcontact 表 — nickname 就是群名 / 个人昵称 |
| 152 | const safeWxid = String(wxid).replace(/'/g, "''"); | 148 | const safeWxid = String(wxid).replace(/'/g, "''"); |
| 153 | const sql = `SELECT conRemark, nickname FROM rcontact WHERE username='${safeWxid}' LIMIT 1;`; | 149 | const sql = `SELECT conRemark, nickname FROM rcontact WHERE username='${safeWxid}' LIMIT 1;`; |
| 154 | try { | 150 | try { |
wechat_plain/scripts/local-testkit/keepalive.sh
| 1 | -#!/bin/bash | ||
| 2 | -# keepalive.sh — Termux 保活 + 进程守护脚本 | 1 | +#!/data/data/com.termux/files/usr/bin/bash |
| 2 | +# keepalive.sh — Pixel 微信自动回复保活脚本 | ||
| 3 | # | 3 | # |
| 4 | -# 功能: | ||
| 5 | -# 1. 获取 wake-lock,防止息屏后 CPU 被冻结 | ||
| 6 | -# 2. 检查并启动 hermes server | ||
| 7 | -# 3. 检查并启动 wechat auto-reply plugin | ||
| 8 | -# 4. 进程死了自动重启 | 4 | +# 守护以下进程,挂了就重启: |
| 5 | +# 1. termux wake-lock — 防 Doze 冻结 Termux:API(通知监听依赖它) | ||
| 6 | +# 2. HermesAccessibilityService — A11y 易被系统/更新关掉,发现 enabled_accessibility_services=null 就重启 | ||
| 7 | +# 3. hermes-agent gateway — :8642,LLM 网关 | ||
| 8 | +# 4. auto-reply.js — 微信回复主程序 | ||
| 9 | # | 9 | # |
| 10 | -# 用法: | ||
| 11 | -# chmod +x keepalive.sh | ||
| 12 | -# nohup ./keepalive.sh > keepalive.log 2>&1 & | 10 | +# 用法(在 Termux 用户 shell 里运行;不要在 root shell 起,wake-lock 拿不住 Termux 进程): |
| 11 | +# nohup bash ~/hermes-wechat/keepalive.sh > ~/hermes-wechat/keepalive.log 2>&1 & | ||
| 13 | # | 12 | # |
| 14 | -# 前置条件(必须手动设置): | ||
| 15 | -# 1. Android 设置 → 应用 → Termux → 电池优化 → 不优化 | ||
| 16 | -# 2. Android 设置 → 应用 → Termux:API → 电池优化 → 不优化 | ||
| 17 | -# 3. Android 设置 → 应用 → 微信 → 电池优化 → 不优化 | ||
| 18 | -# 4. Android 设置 → 应用 → Termux → 允许自启动 | ||
| 19 | -# 5. Termux:API 已安装,并已授予通知访问权限 | 13 | +# 前置条件(一次性设置): |
| 14 | +# 1. Android 设置 → 应用 → Termux / Termux:API / 微信 → 电池优化 → 不优化 | ||
| 15 | +# 2. Termux:API 已安装,并已授予通知访问权限 | ||
| 16 | +# 3. lsposed-wechat-sendmsg APK 已安装(含 HermesAccessibilityService) | ||
| 17 | +# 4. ~/hermes-wechat/start-ar.sh 存在并已设置 MY_WXID / MY_NICKNAME | ||
| 20 | 18 | ||
| 21 | -set -e | ||
| 22 | - | ||
| 23 | -# 确保 Termux 工具在 PATH 中(su 环境下可能缺失) | ||
| 24 | export PATH="/data/data/com.termux/files/usr/bin:$PATH" | 19 | export PATH="/data/data/com.termux/files/usr/bin:$PATH" |
| 25 | - | ||
| 26 | -HERMES_DIR="${HERMES_DIR:-/data/data/com.termux/files/home/hermes-wechat}" | ||
| 27 | -HERMES_HOST="${HERMES_HOST:-localhost}" | ||
| 28 | -HERMES_PORT="${HERMES_PORT:-3010}" | ||
| 29 | -MY_WXID="${MY_WXID:-}" | ||
| 30 | -MY_NICKNAME="${MY_NICKNAME:-}" | ||
| 31 | -CHECK_INTERVAL_SEC="${CHECK_INTERVAL_SEC:-10}" | ||
| 32 | - | ||
| 33 | -log() { | ||
| 34 | - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | ||
| 35 | -} | ||
| 36 | - | ||
| 37 | -# ── 1. Wake lock ───────────────────────────────────────────────── | ||
| 38 | -termux-wake-lock 2>/dev/null || true | ||
| 39 | -log "Wake lock acquired" | ||
| 40 | - | ||
| 41 | -# ── 2. 检查前置条件 ────────────────────────────────────────────── | ||
| 42 | -check_prerequisites() { | ||
| 43 | - _ok=0 | ||
| 44 | - | ||
| 45 | - if ! command -v termux-notification-list >/dev/null 2>&1; then | ||
| 46 | - log "[WARN] termux-notification-list not found. Install: pkg install termux-api" | ||
| 47 | - _ok=1 | 20 | +export HOME="/data/data/com.termux/files/home" |
| 21 | + | ||
| 22 | +HERMES_DIR="${HERMES_DIR:-$HOME/hermes-wechat}" | ||
| 23 | +HERMES_PORT="${HERMES_PORT:-8642}" | ||
| 24 | +GATEWAY_START="${GATEWAY_START:-$HOME/.hermes/scripts/start-gateway.sh}" | ||
| 25 | +AR_START="${AR_START:-$HERMES_DIR/start-ar.sh}" | ||
| 26 | +A11Y_SVC="com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService" | ||
| 27 | +CHECK_INTERVAL_SEC="${CHECK_INTERVAL_SEC:-15}" | ||
| 28 | + | ||
| 29 | +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } | ||
| 30 | + | ||
| 31 | +# ── 1. Termux wake-lock ────────────────────────────────────────── | ||
| 32 | +ensure_wake_lock() { | ||
| 33 | + # 用命令行 termux-wake-lock(依赖 Termux:API app);am startservice 是备用 | ||
| 34 | + if command -v termux-wake-lock >/dev/null 2>&1; then | ||
| 35 | + termux-wake-lock 2>/dev/null | ||
| 36 | + else | ||
| 37 | + # 后备:用 su 调系统 ServiceManager | ||
| 38 | + su -c "am startservice --user 0 -a com.termux.service_wake_lock com.termux/.app.TermuxService" 2>/dev/null || true | ||
| 48 | fi | 39 | fi |
| 40 | +} | ||
| 49 | 41 | ||
| 50 | - if [ ! -d "$HERMES_DIR" ]; then | ||
| 51 | - log "[WARN] HERMES_DIR not found: $HERMES_DIR" | ||
| 52 | - _ok=1 | ||
| 53 | - fi | 42 | +is_wake_lock_held() { |
| 43 | + su -c "dumpsys power 2>/dev/null | grep -q 'termux:service-wakelock'" | ||
| 44 | +} | ||
| 54 | 45 | ||
| 55 | - if [ -z "$MY_WXID" ] || [ -z "$MY_NICKNAME" ]; then | ||
| 56 | - log "[WARN] MY_WXID or MY_NICKNAME not set" | ||
| 57 | - log " export MY_WXID=wxid_xxxxxx" | ||
| 58 | - log " export MY_NICKNAME=你的昵称" | ||
| 59 | - _ok=1 | ||
| 60 | - fi | 46 | +# ── 2. A11y service ────────────────────────────────────────────── |
| 47 | +is_a11y_enabled() { | ||
| 48 | + out=$(su -c "settings get secure enabled_accessibility_services" 2>/dev/null) | ||
| 49 | + case "$out" in | ||
| 50 | + *com.hermes.wechat.xposed*HermesAccessibilityService*) return 0 ;; | ||
| 51 | + *) return 1 ;; | ||
| 52 | + esac | ||
| 53 | +} | ||
| 61 | 54 | ||
| 62 | - return $_ok | 55 | +enable_a11y() { |
| 56 | + log "Enabling HermesAccessibilityService..." | ||
| 57 | + su -c "settings put secure enabled_accessibility_services $A11Y_SVC; settings put secure accessibility_enabled 1" 2>/dev/null | ||
| 63 | } | 58 | } |
| 64 | 59 | ||
| 65 | -# ── 3. 检查 hermes server 是否运行 ─────────────────────────────── | ||
| 66 | -is_hermes_running() { | ||
| 67 | - curl -s "http://${HERMES_HOST}:${HERMES_PORT}/api/health" >/dev/null 2>&1 | 60 | +# ── 3. hermes gateway ──────────────────────────────────────────── |
| 61 | +is_gateway_running() { | ||
| 62 | + # 用 /sys/class/net 风格的端口探测:通过 nc 或 curl,二选一 | ||
| 63 | + if command -v curl >/dev/null 2>&1; then | ||
| 64 | + # gateway 在 termux 用户下需要 Clash 代理(与本机回环无关); | ||
| 65 | + # 但 127.0.0.1:8642 走本机,不用代理 | ||
| 66 | + env -u HTTPS_PROXY -u HTTP_PROXY \ | ||
| 67 | + curl -s --max-time 3 "http://127.0.0.1:${HERMES_PORT}/v1/models" >/dev/null 2>&1 | ||
| 68 | + else | ||
| 69 | + # nc fallback | ||
| 70 | + echo > /dev/tcp/127.0.0.1/$HERMES_PORT 2>/dev/null | ||
| 71 | + fi | ||
| 68 | } | 72 | } |
| 69 | 73 | ||
| 70 | -# ── 4. 启动 hermes server ──────────────────────────────────────── | ||
| 71 | -# 必须在 proot Ubuntu 内以 ubuntu 用户运行(Claude CLI 拒绝 root + --dangerously-skip-permissions) | ||
| 72 | -start_hermes() { | ||
| 73 | - log "Starting hermes server in proot Ubuntu user=ubuntu..." | ||
| 74 | - cd "$HERMES_DIR" | ||
| 75 | - nohup proot-distro login ubuntu --user ubuntu -- bash -c \ | ||
| 76 | - "cd $HERMES_DIR && . .env && node server/index.js" \ | ||
| 77 | - > hermes-server.log 2>&1 & | ||
| 78 | - sleep 5 | ||
| 79 | - if is_hermes_running; then | ||
| 80 | - log "Hermes server started PID=$!" | 74 | +start_gateway() { |
| 75 | + log "Starting hermes gateway..." | ||
| 76 | + if [ -x "$GATEWAY_START" ]; then | ||
| 77 | + bash "$GATEWAY_START" >> "$HOME/.hermes/logs/gateway-start.log" 2>&1 | ||
| 81 | else | 78 | else |
| 82 | - log "[WARN] Hermes server may have failed to start, check hermes-server.log" | 79 | + log "[ERROR] $GATEWAY_START not found" |
| 83 | fi | 80 | fi |
| 84 | } | 81 | } |
| 85 | 82 | ||
| 86 | -# ── 5. 检查 auto-reply 是否运行 ────────────────────────────────── | 83 | +# ── 4. auto-reply.js ───────────────────────────────────────────── |
| 87 | is_auto_reply_running() { | 84 | is_auto_reply_running() { |
| 88 | - pgrep -f "node auto-reply.js" >/dev/null 2>&1 | 85 | + pgrep -f "node.*auto-reply.js" >/dev/null 2>&1 |
| 89 | } | 86 | } |
| 90 | 87 | ||
| 91 | -# ── 6. 启动 auto-reply ─────────────────────────────────────────── | ||
| 92 | start_auto_reply() { | 88 | start_auto_reply() { |
| 93 | log "Starting auto-reply..." | 89 | log "Starting auto-reply..." |
| 94 | - cd "$HERMES_DIR/server/services/wechat" | ||
| 95 | - env MY_WXID="$MY_WXID" MY_NICKNAME="$MY_NICKNAME" \ | ||
| 96 | - nohup node auto-reply.js > auto-reply.log 2>&1 & | ||
| 97 | - log "Auto-reply started PID=$!" | 90 | + if [ -x "$AR_START" ]; then |
| 91 | + # auto-reply 走 root 才能 input keyevent / am broadcast / 读微信 DB | ||
| 92 | + su -c "nohup bash $AR_START > /dev/null 2>&1 &" | ||
| 93 | + else | ||
| 94 | + log "[ERROR] $AR_START not found" | ||
| 95 | + fi | ||
| 98 | } | 96 | } |
| 99 | 97 | ||
| 100 | -# ── 7. 主循环 ──────────────────────────────────────────────────── | 98 | +# ── 5. 主循环 ──────────────────────────────────────────────────── |
| 101 | log "=== Keepalive started ===" | 99 | log "=== Keepalive started ===" |
| 102 | -log "HERMES_DIR=$HERMES_DIR" | ||
| 103 | -log "MY_WXID=${MY_WXID:-not-set}" | ||
| 104 | -log "MY_NICKNAME=${MY_NICKNAME:-not-set}" | 100 | +log "HERMES_DIR=$HERMES_DIR GATEWAY_START=$GATEWAY_START AR_START=$AR_START" |
| 101 | +log "Check interval: ${CHECK_INTERVAL_SEC}s" | ||
| 105 | 102 | ||
| 106 | -if ! check_prerequisites; then | ||
| 107 | - log "[ERROR] Prerequisites check failed, exiting" | ||
| 108 | - exit 1 | ||
| 109 | -fi | 103 | +# 首次进入:把所有依赖拉到位 |
| 104 | +ensure_wake_lock | ||
| 105 | +is_a11y_enabled || enable_a11y | ||
| 106 | +is_gateway_running || start_gateway | ||
| 107 | +sleep 5 | ||
| 108 | +is_auto_reply_running || start_auto_reply | ||
| 110 | 109 | ||
| 111 | while true; do | 110 | while true; do |
| 112 | - # 检查 hermes | ||
| 113 | - if ! is_hermes_running; then | ||
| 114 | - log "Hermes server not responding, restarting..." | ||
| 115 | - start_hermes | 111 | + sleep "$CHECK_INTERVAL_SEC" |
| 112 | + | ||
| 113 | + # wake-lock 偶尔会因 Termux 重启 / 系统重启而丢,保险起见周期性 re-acquire | ||
| 114 | + if ! is_wake_lock_held; then | ||
| 115 | + log "Wake lock lost, re-acquiring..." | ||
| 116 | + ensure_wake_lock | ||
| 117 | + fi | ||
| 118 | + | ||
| 119 | + # A11y 易被系统升级 / 用户在设置中关掉 | ||
| 120 | + if ! is_a11y_enabled; then | ||
| 121 | + log "A11y service disabled, re-enabling..." | ||
| 122 | + enable_a11y | ||
| 123 | + fi | ||
| 124 | + | ||
| 125 | + if ! is_gateway_running; then | ||
| 126 | + log "Gateway not responding on :$HERMES_PORT, restarting..." | ||
| 127 | + start_gateway | ||
| 128 | + sleep 6 | ||
| 116 | fi | 129 | fi |
| 117 | 130 | ||
| 118 | - # 检查 auto-reply | ||
| 119 | if ! is_auto_reply_running; then | 131 | if ! is_auto_reply_running; then |
| 120 | - log "Auto-reply not running, restarting..." | 132 | + log "auto-reply.js not running, restarting..." |
| 121 | start_auto_reply | 133 | start_auto_reply |
| 122 | fi | 134 | fi |
| 123 | - | ||
| 124 | - sleep "$CHECK_INTERVAL_SEC" | ||
| 125 | done | 135 | done |