From 2a044a3db1bf59c354640cc195fa61677018aa1f Mon Sep 17 00:00:00 2001 From: yibo Date: Mon, 25 May 2026 15:56:11 +0800 Subject: [PATCH] fix: 引用回复消息(type=822083633)被 tracker 过滤 + @ 检测失败 --- server/services/wechat/android-wechat-tracker-sqlcipher.js | 5 +++-- server/services/wechat/auto-reply.js | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/server/services/wechat/android-wechat-tracker-sqlcipher.js b/server/services/wechat/android-wechat-tracker-sqlcipher.js index affafeb..9d31411 100644 --- a/server/services/wechat/android-wechat-tracker-sqlcipher.js +++ b/server/services/wechat/android-wechat-tracker-sqlcipher.js @@ -94,8 +94,9 @@ class AndroidWechatTrackerSQLCipher extends EventEmitter { * 获取新消息(msgId > lastMsgId) */ async fetchNew() { - // 只查询文本消息 (type=1) 和撤回通知 (type=10002),排除自己发送的 - const sql = `SELECT msgId, type, isSend, talker, content, createTime FROM message WHERE msgId > ${this.lastMsgId} AND isSend = 0 AND type IN (1, 10002) ORDER BY msgId ASC;`; + // type=1 纯文本,type=10002 撤回通知,type=822083633 引用回复(appmsg) + // 引用回复的 message.type 是这个大整数(0x31000031),@小火子 在 里 + const sql = `SELECT msgId, type, isSend, talker, content, createTime FROM message WHERE msgId > ${this.lastMsgId} AND isSend = 0 AND type IN (1, 10002, 822083633) ORDER BY msgId ASC;`; const out = await this._query(sql, 10000); const msgs = this._parseRows(out); if (msgs.length > 0) { diff --git a/server/services/wechat/auto-reply.js b/server/services/wechat/auto-reply.js index d3d1a35..2f4c455 100644 --- a/server/services/wechat/auto-reply.js +++ b/server/services/wechat/auto-reply.js @@ -788,9 +788,26 @@ class AutoReplyBot { async handleMessage(msg) { if (msg.type !== 'recv') return; - const { talker, content, msgId } = msg; + const { talker, msgId } = msg; + let content = msg.content; + const rawContent = msg.content; // 原始(含 appmsg XML),授权指令解析要用 refermsg if (!talker || !content) return; + // 引用回复(appmsg XML,type=822083633):真正的文本在 <title> 里, + // @小火子 也在 title 里且后面跟 (不是空格)→ isAtMe 匹配不上。 + // 这里规范化成 "senderPrefix:\n",让下游 @ 检测 / quote / hermes 都用干净文本。 + if (content.includes('<appmsg') || content.includes('<refermsg>')) { + let prefix = ''; + const pm = content.match(/^([^:\n]+):\n/); + if (pm) prefix = pm[0]; + const tm = content.match(/<title>([\s\S]*?)<\/title>/); + if (tm) { + const title = tm[1].replace(/ /g, ' ').replace(/&/g, '&').trim(); + content = prefix + title; + log('[NORMALIZE] quote-reply →', content.substring(0, 60)); + } + } + if (CONFIG.ignoreSelf && talker === CONFIG.myWxid) return; if (this.state.hasReplied(msgId)) return; if (this.state.isCooldown(talker)) { @@ -823,7 +840,8 @@ class AutoReplyBot { if (isFromOwner) { // owner 自己发的(私聊 / 群里 @):先看是不是授权指令 - const cmd = parseApprovalCommand(content); + // 用 rawContent(含 refermsg)让 parseApprovalCommand 能判断同意哪条 + const cmd = parseApprovalCommand(rawContent); if (cmd) { this.state.markReplied(msgId, talker); await this.handleApproval(cmd); -- libgit2 0.21.4