From 1a63dbe0bc3ceb4bff93fd4634e1c6f87bc436ce Mon Sep 17 00:00:00 2001 From: yibo Date: Sun, 24 May 2026 11:14:02 +0800 Subject: [PATCH] fix: sqlcipher tracker _parseRows 支持多行 TEXT 字段(修群聊 @ 检测失败) --- server/services/wechat/android-wechat-tracker-sqlcipher.js | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/server/services/wechat/android-wechat-tracker-sqlcipher.js b/server/services/wechat/android-wechat-tracker-sqlcipher.js index 00dbe26..3ff05a3 100644 --- a/server/services/wechat/android-wechat-tracker-sqlcipher.js +++ b/server/services/wechat/android-wechat-tracker-sqlcipher.js @@ -188,25 +188,50 @@ class AndroidWechatTrackerSQLCipher extends EventEmitter { const messages = []; if (!text) return messages; - // .mode line 输出格式:每行一个 "column = value",记录之间用空行分隔 + // .mode line 输出格式: + // key1 = value1 + // key2 = value2_line1 + // value2_line2 <-- 续行(带前导空格对齐,不含 ":"/"=") + // key3 = value3 + // (空行分隔下一条记录) + // + // 注意:多行 TEXT 字段(如 message.content)的换行在输出里保留, + // 续行没有 ": " / " = ",必须并入上一个 key 的 value,否则会 + // 丢内容并导致 @ / 引用等检测失败。 const records = []; let current = {}; - for (const line of text.split('\n')) { - const trimmed = line.trim(); - if (!trimmed) { + let lastKey = null; + // 已知所有合法 column 名(用于判断一行是不是新 key=value),避免 + // 把内容里碰巧带 "xxx = yyy" 的文本误识别为新字段 + const KNOWN_COLS = new Set(['msgId', 'type', 'isSend', 'talker', 'content', 'createTime']); + + for (const rawLine of text.split('\n')) { + if (!rawLine.trim()) { if (Object.keys(current).length > 0) { records.push(current); current = {}; + lastKey = null; } continue; } - // sqlcipher .mode line 输出格式: "key: value" 或 "key = value" - let sepIdx = trimmed.indexOf(': '); - if (sepIdx < 0) sepIdx = trimmed.indexOf(' = '); + + // 试图解析为 "key = value" 或 "key: value";只在 key 是已知列名时才当作新字段 + let sepIdx = rawLine.indexOf(' = '); + let sepLen = 3; + if (sepIdx < 0) { sepIdx = rawLine.indexOf(': '); sepLen = 2; } + let isNewField = false; if (sepIdx > 0) { - const key = trimmed.substring(0, sepIdx).trim(); - const val = trimmed.substring(sepIdx + 2).trim(); - current[key] = val; + const key = rawLine.substring(0, sepIdx).trim(); + if (KNOWN_COLS.has(key)) { + current[key] = rawLine.substring(sepIdx + sepLen); + lastKey = key; + isNewField = true; + } + } + if (!isNewField && lastKey) { + // 续行:sqlcipher 给续行加了和首行 value 等宽的前导空白对齐, + // 去掉与上一字段名等长 + " = " 的前缀长度即可;保守起见用 trimStart + current[lastKey] += '\n' + rawLine.replace(/^\s+/, ''); } } if (Object.keys(current).length > 0) { -- libgit2 0.21.4