# WeChat 消息发送与跟踪 Skill ## 能力概述 在手机端(Pixel 10 Pro Fold,Termux/Debian)直接发送微信消息、实时跟踪收发消息,无需 adb。 ## 环境要求 1. **Root 权限** — 必须已 root(Magisk 30.7+) 2. **HermesWeChatSendMsg 模块已安装**(lsposed-wechat-sendmsg APK) 3. **AccessibilityService 已启用** — 这是最容易踩的坑,详见下文 4. **微信主进程运行中且在前台**(广播触发时 termux/其他 app 在前台会导致 A11y service 拿到错误的 root) ### 启用 / 验证 AccessibilityService ```bash # 启用 adb shell "su -c 'settings put secure enabled_accessibility_services com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService; settings put secure accessibility_enabled 1'" # 验证 adb shell "settings get secure enabled_accessibility_services" # 应输出: com.hermes.wechat.xposed/com.hermes.wechat.xposed.accessibility.HermesAccessibilityService adb shell "dumpsys accessibility | grep Hermes" # 应看到 Bound services 和 Enabled services 都包含 HermesAccessibilityService ``` ## 文件位置 ``` server/services/wechat/ ├── android-wechat-sender.js # **主推** 发送(am broadcast → A11y service) ├── android-wechat-tracker-sqlcipher.js # **主推** 消息跟踪(sqlcipher 轮询 DB) ├── android-wechat-tracker.js # 备用跟踪(tail -f LSPosed 日志) ├── android-wechat-sender-frida.js # ⚠️ 已废弃(Frida daemon 队列) ├── auto-reply.js # 自动回复主入口 └── wechat-reader.js # 数据库读取 ``` ## 发送消息 ### 唯一可用方式:am broadcast → A11y UI 自动化 ```js const { sendTextMessageBroadcast } = require('./android-wechat-sender'); // ⚠️ toUser = wxid(写入 message 表用),toUserDisplayName = WeChat nickname(A11y 搜索用,必须传) const r = await sendTextMessageBroadcast('yibooo', '你好,来自 hermes', { toUserDisplayName: '火星人' }); console.log(r); // { ok: true } ``` 底层等价于: ```bash adb shell "am broadcast -a com.hermes.wechat.SEND_MSG \ --es toUser yibooo \ --es toUserDisplayName 火星人 \ --es content '你好'" ``` A11y service 收到后会:启动微信 → 搜索 `火星人` → 进入聊天 → 写文本 → 点发送按钮,全程约 5-8 秒。 ### 关键约束 1. **必须传 `toUserDisplayName`**(WeChat nickname)。如果只传 `toUser` (wxid),A11y 会用 wxid 去搜——绝大多数情况搜不到,发送失败。 2. **微信必须在前台**或至少不被其他高优先级 app(如全屏游戏)阻挡。如果广播触发时手机停在 termux/其他 app,A11y 的 `startActivity(LauncherUI)` 可能被忽略。 3. **不要短时间内连发**。A11y 流程有 3500ms + 800ms + 800ms + 2500ms + 400ms 的多段 delay,单次约 8s。 ### 已废弃路径(不要用) | 文件 / API | 状态 | |----|----| | `sendTextMessageFrida` (`android-wechat-sender-frida.js`) | Frida 17 + 微信 8.0.72 注入崩溃,daemon 起不来 | | `sendTextMessage` ContentProvider 路径 | 触发的 LSPosed `HermesWeChatModule.sendTextMessageDirect` 返回 `true` 但消息不进 DB(主线程 NetScene 提交 NPE 被 swallow) | 注:`sendTextMessage`(ContentProvider)和 `sendTextMessageBroadcast`(broadcast)**最终广播都被 A11y service 接收并执行**,所以从外部看上去 ContentProvider 路径也能工作——但只是因为 A11y 路径在后台兜住了。建议直接用 broadcast 接口,语义最清晰。 ### 批量发送 ```js const { sendBatch } = require('./android-wechat-sender'); const results = await sendBatch([ { toUser: 'filehelper', content: '消息1' }, { toUser: 'wxid_abc123', content: '消息2' }, ], 2000); // 每条间隔 2 秒 ``` ### 查询发送状态 ```js const { queryStatus } = require('./android-wechat-sender'); const s = await queryStatus(); console.log(s); // { status: 'pending' } | { status: 'true' } | { status: 'false' } ``` ## 跟踪消息 ### 方式1:SQLCipher 轮询(推荐) 直接查询微信加密 DB,不依赖 LSPosed hook: ```js const { AndroidWechatTrackerSQLCipher } = require('./android-wechat-tracker-sqlcipher'); const tracker = new AndroidWechatTrackerSQLCipher(); tracker.on('message', msg => { console.log(`[${msg.type.toUpperCase()}] ${msg.talker}: ${msg.content}`); }); tracker.on('error', err => console.error('Tracker error:', err.message)); await tracker.init(); // 读取当前最大 msgId tracker.startPolling(3000); // 每 3 秒轮询 // tracker.stopPolling(); ``` ### 方式2:LSPosed 日志 tail(备用) ```js const { AndroidWechatTracker } = require('./android-wechat-tracker'); const tracker = new AndroidWechatTracker(); tracker.on('message', msg => console.log(msg)); tracker.startPolling(3000); // tracker.startStream(); // tail -f 模式(更实时) // tracker.stopPolling(); ``` ### 一次性读取最近消息 ```js const msgs = await tracker.fetchRecent(20); // 仅 AndroidWechatTracker 支持 ``` ## 消息格式 ```ts { type: 'send' | 'recv', // 发送或接收 talker: string, // 对方 wxid / 群聊ID msgType: number, // 微信消息类型(1=文本) msgId: number, content: string, // 消息内容(截断前 50 字符) raw: string, // 原始日志行 timestamp: number, // 解析时间戳 } ``` ## 故障排查 | 现象 | 原因 | 解决 | |------|------|------| | 接口返回 `ok: true` 但 yibooo 没收到 / `message` 表没新行 | A11y service 没启用 | 见上文「启用 / 验证 AccessibilityService」 | | A11y 日志 `Root package=com.termux` 后直接 reset | broadcast 触发时微信不在前台 | 先 `adb shell am start -n com.tencent.mm/.ui.LauncherUI` | | `Search button not found` | 当前不是 LauncherUI(可能停在另一个聊天里) | A11y 内置了 relaunch 兜底,等下一次会自动恢复;或手动回到主界面 | | `No clickable search result found` | nickname 错 / 不是好友 | 验证 `toUserDisplayName` 是 WeChat 里的实际昵称 | | `[HermesWX] Send result: true` 出现在日志里但消息没发出 | LSPosed 反射路径的误报,**正常现象** | 看 `HermesA11y:*` 日志才反映真实情况 | | 跟踪不到消息 | SQLCipher key 失效 / DB 路径变了 | 重新捕获 key(见 `wechat_plain/scripts/README_Android.md`) | ## 与 Mac 端区别 | | Mac 端 | 手机端 | |--|--------|--------| | 文件 | `wechat-sender.js` / `wechat-tracker.js` | `android-wechat-sender.js` / `android-wechat-tracker.js` | | 连接方式 | `adb shell` | 直接 `su -c` | | 运行位置 | Mac hermes agent | Pixel Termux/Debian hermes agent | | 依赖 | adb | root + su |