SKILL.md
6.71 KB
WeChat 消息发送与跟踪 Skill
能力概述
在手机端(Pixel 10 Pro Fold,Termux/Debian)直接发送微信消息、实时跟踪收发消息,无需 adb。
环境要求
- Root 权限 — 必须已 root(Magisk 30.7+)
- HermesWeChatSendMsg 模块已安装(lsposed-wechat-sendmsg APK)
- AccessibilityService 已启用 — 这是最容易踩的坑,详见下文
- 微信主进程运行中且在前台(广播触发时 termux/其他 app 在前台会导致 A11y service 拿到错误的 root)
启用 / 验证 AccessibilityService
# 启用
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 自动化
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 }
底层等价于:
adb shell "am broadcast -a com.hermes.wechat.SEND_MSG \
--es toUser yibooo \
--es toUserDisplayName 火星人 \
--es content '你好'"
A11y service 收到后会:启动微信 → 搜索 火星人 → 进入聊天 → 写文本 → 点发送按钮,全程约 5-8 秒。
关键约束
- 必须传
toUserDisplayName(WeChat nickname)。如果只传toUser(wxid),A11y 会用 wxid 去搜——绝大多数情况搜不到,发送失败。 - 微信必须在前台或至少不被其他高优先级 app(如全屏游戏)阻挡。如果广播触发时手机停在 termux/其他 app,A11y 的
startActivity(LauncherUI)可能被忽略。 - 不要短时间内连发。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 接口,语义最清晰。
批量发送
const { sendBatch } = require('./android-wechat-sender');
const results = await sendBatch([
{ toUser: 'filehelper', content: '消息1' },
{ toUser: 'wxid_abc123', content: '消息2' },
], 2000); // 每条间隔 2 秒
查询发送状态
const { queryStatus } = require('./android-wechat-sender');
const s = await queryStatus();
console.log(s); // { status: 'pending' } | { status: 'true' } | { status: 'false' }
跟踪消息
方式1:SQLCipher 轮询(推荐)
直接查询微信加密 DB,不依赖 LSPosed hook:
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(备用)
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();
一次性读取最近消息
const msgs = await tracker.fetchRecent(20); // 仅 AndroidWechatTracker 支持
消息格式
{
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 |