• 症状:群里 @小火子 发消息,auto-reply 完全没响应,日志里只有 [NOTIF]
    没有 [GROUP-AT],但 DB 里这条消息确实存在。
    
    根因:sqlcipher .mode line 输出多行 TEXT(如 message.content 是
    "yibooo:\n@小火子 在吗")时长这样:
    
        content = yibooo:
                  @小火子 在吗
        createTime = 1779591001000
    
    旧 _parseRows 看到第二行 "@小火子 在吗" 没有 ": " 或 " = " 分隔符,
    就直接 IGNORED,导致 content 被截断成 "yibooo:"。isAtMe("yibooo:")
    返回 false,replyType=null,handleMessage 静默 return(没 log)。
    
    修复:
    - 引入 KNOWN_COLS 白名单(msgId/type/isSend/talker/content/createTime),
      只在 key 是已知列名时才当作新字段,避免内容里碰巧带 "xxx = yyy"
      误判为新字段
    - 不匹配新字段的行作为上一字段 value 的续行追加(带 \n)
    - 添加单元测试式的语义注释解释 .mode line 输出格式
    
    unit test (synthetic input):
      input:  'content = yibooo:\n@小火子 在吗\ncreateTime = 1779...'
      before: { content: 'yibooo:', createTime: '1779...' }  ❌
      after:  { content: 'yibooo:\n@小火子 在吗', createTime: '1779...' }  ✅
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 新增功能:
    - HermesAccessibilityService 支持广播 extras quoteText / atSender
    - quoteText: 在聊天页找文本含此 substring 的 bkl 气泡 → 长按 → 点「引用」
      菜单项 → 输入回复文本 → 点发送
    - atSender: 在输入框先打「@」触发选人面板 → 找匹配成员点击 → 追加回复
    
    实测路径完整跑通(msgId 1409 验证):
    1. 广播触发 A11y service
    2. 进入聊天页("already in correct chat" 或经过搜索)
    3. findBubbleByPartialText 在 bkl 列表里倒序匹配
    4. dispatchGesture 派发 800ms 合成长按手势(ACTION_LONG_CLICK 在 WeChat
       GestureDetector 实现上不触发菜单,必须用 gesture 派发)
    5. 等 1.2s 后用 findClickableByTextAllWindows 搜「引用」 — 关键:菜单在
       popup window 不在 active window,必须遍历 getWindows() 才能找到
    6. 点击引用 → 引用 chip 注入到输入框 → 写文本 → 点发送
    7. DB 写入的 message 是 type=57 (微信内部引用回复消息类型)
    
    关键依赖(Android AccessibilityService):
    - accessibility_service_config.xml 必须设 canPerformGestures="true"
      否则 dispatchGesture 不回调 onCompleted
    
    群聊路径:
    - auto-reply.js handleMessage 在 replyType=group-at/group-quote 时自动
      从触发消息 content 解析 bubbleText 当 quoteText,senderName 当 atSender
    - android-wechat-sender.js sendTextMessageBroadcast 接受 options.quoteText
      / options.atSender,拼到 am broadcast --es 参数里
    
    测试覆盖(在 Pixel 实测):
    - ✅ 私聊回归:plain text 路径不影响(msgId 1408)
    - ✅ 引用回复:私聊场景已验证(msgId 1409 type=57)
    - ⏸ 群聊 + @ 群成员:代码已写完,需要真实群消息触发才能测
    
    APK 已重编 + 用 debug.keystore 签 + adb install -r 升级(A11y 权限保留)。
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • #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>
    yibo authored
     
    Browse Dir »
  • 包含:
    - 运行中进程清单(auto-reply / hermes gateway / A11y service)
    - phone 路径 ↔ Mac repo 路径对照表
    - 启停 / A11y 校验 / 日志查看 / 手动测试发送命令速查
    - Clash mixed proxy 网络拓扑陷阱(Termux UID 被 VPN 排除,必须走 7890)
    - 当前 hermes 后端配置(Moonshot CN + kimi-k2.6)
    - 两种接手开发流程(phone 上 clone repo / 手动 scp)
    - 已知待办(群昵称查询、引用@回复、wake-lock、清理)
    - 避雷清单(前人踩过的所有坑)
    
    同步到 phone: /data/data/com.termux/files/home/hermes-wechat/HANDOFF.md
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 把 6 步链路接到唯一可用的发送路径(HermesAccessibilityService):
      1. termux-notification-list 监听通知(息屏兜底)
      2. input keyevent 224 点亮屏幕 + wm dismiss-keyguard 解锁
      3. am start LauncherUI 切微信前台
      4. sqlcipher 轮询 EnMicroMsg.db 拉新消息 + 最近 N 条上下文
      5. 调 hermes-agent /v1/chat/completions 生成回复
      6. am broadcast com.hermes.wechat.SEND_MSG → A11y UI 自动化发送
    
    关键改动:
    - android-wechat-sender.js
      - sendTextMessageBroadcast 增加 options.toUserDisplayName(A11y 搜索必传)
      - 新增 checkAccessibilityServiceEnabled / enableAccessibilityService
    - android-wechat-tracker-sqlcipher.js
      - 新增 getContactNickname(wxid):查 rcontact 拿 conRemark/nickname,
        带 filehelper/weixin 等内置 helper 的硬编码 fallback
      - 新增 fetchHistory(talker, limit):拉最近 N 条文本消息正序返回
    - auto-reply.js
      - 移除已废弃的 sendTextMessageFrida
      - 启动时校验 A11y service,未启用则尝试自动启用并打印 manual fix 命令
      - handleMessage 中:先 getContactNickname 拿 displayName、再 fetchHistory
        给 hermes 做多轮上下文,最后用 broadcast + displayName 发送
      - buildHermesMessages 重写:自动把历史按 isSend 转 user/assistant 多轮
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • 通过 DB 实证与 LSPosed 日志对比定位到:
    
    - LSPosed `HermesWeChatModule.sendTextMessageDirect` 在 WeChat 8.0.72 上
      返回 `Send result: true` 后,主线程异步 NetScene 提交触发 NPE 被 swallow,
      消息**不进 message 表**。这是误报。
    - Frida daemon 队列方案(`android-wechat-sender-frida.js`)在 Frida 17 +
      微信 8.0.72 上注入会触发反调试,进程崩溃,无法生产使用。
    - 实际可用路径是 `HermesAccessibilityService`(lsposed-wechat-sendmsg 模块内):
      监听同一广播 `com.hermes.wechat.SEND_MSG`,纯 UI 自动化点击微信完成发送。
    - A11y service 易被系统关闭(`enabled_accessibility_services = null`),
      这是消息发送失败的最常见根因,docs 中加上启用 / 验证命令。
    
    更新范围:
    - CLAUDE.md
    - server/services/wechat/README.md, SKILL.md
    - server/services/wechat/android-wechat-sender-frida.js(标注废弃)
    - wechat_plain/scripts/local-testkit/docs/README.md, HERMES_WECHAT_ARCHITECTURE.md
    - wechat_plain/scripts/lsposed-wechat-sendmsg/README.md
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »

  • - HERMES_WECHAT_ARCHITECTURE.md: v2.0 → v3.0, rewrite for Node.js plugin
    - README.md: update architecture diagram, file list, API endpoint, env vars
    - server/services/wechat/README.md: add sqlcipher tracker + frida sender
    - SKILL.md: add Frida daemon and SQLCipher tracker usage
    - wechat-auto-reply-status.md: mark SQLCipher + Frida as verified, update plans
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - auto-reply.js 接入 hermes-agent OpenAI-compatible API (:8642)
    - 新增 android-wechat-tracker-sqlcipher.js 替代 LSPosed 日志 tail
    - 新增 android-wechat-sender-frida.js 替代 am broadcast 发送
    - 更新 keepalive.sh、README、HermesWeChatModule.java
    - 添加 .gitignore SQLite 变体规则
    - 新增 docs: 息屏发送尝试记录与屏幕自动唤醒方案
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • - auto-reply.js: connect to hermes /api/master/chat for AI reply generation
      - SSE response parsing, session isolation per talker
      - notification watcher (termux-notification-list) for screen-off fallback
      - wechat foreground check + wake before send
    - hermes_wechat_daemon.py: prefer hermes API, fallback to direct LLM API
    - config.json: add hermes host/port for local API connection
    - keepalive.sh: termux-wake-lock + process guardian for hermes + auto-reply
    - Move docs into local-testkit/docs/ directory
    - .gitignore: exclude server/db/
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »
  • Mac side (lemonPCRM):
    - Express server with /api/master/chat SSE endpoint
    - MasterSession multi-round orchestration
    - AgentManager + ToolRegistry
    - ClaudeCliProtocol ACP implementation
    
    Pixel side (wechat_plain/scripts/):
    - hermes_wechat_daemon.py: standalone daemon
      - SQLCipher DB decryption with WAL checkpoint fix
      - Direct LLM API calls (Kimi/Anthropic/OpenAI)
      - am broadcast to trigger LSPosed send
      - Auto wake WeChat to foreground before poll/send
    - lsposed-wechat-sendmsg: LSPosed module
      - sendTextMessageDirect via WeChat internal API
      - u01.r1.a -> builder -> m1.a() full chain
      - Background thread NetScene submission
    
    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    yibo authored
     
    Browse Dir »