From 64d5e721afe56d82bb53f8b4d0973dd2c1fb0a7a Mon Sep 17 00:00:00 2001 From: xuncha <1658671838@qq.com> Date: Thu, 5 Feb 2026 18:14:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/services/exportService.ts | 66 +++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/electron/services/exportService.ts b/electron/services/exportService.ts index c4fc8a7..db7c7f4 100644 --- a/electron/services/exportService.ts +++ b/electron/services/exportService.ts @@ -590,6 +590,47 @@ class ExportService { return content.replace(/^[\s]*([a-zA-Z0-9_-]+):(?!\/\/)/, '') } + /** + * 从撤回消息内容中提取撤回者的 wxid + * 撤回消息 XML 格式通常包含 等字段 + * 以及撤回者的 wxid 在某些字段中 + * @returns { isRevoke: true, isSelfRevoke: true } - 是自己撤回的消息 + * @returns { isRevoke: true, revokerWxid: string } - 是别人撤回的消息,提取到撤回者 + * @returns { isRevoke: false } - 不是撤回消息 + */ + private extractRevokerInfo(content: string): { isRevoke: boolean; isSelfRevoke?: boolean; revokerWxid?: string } { + if (!content) return { isRevoke: false } + + // 检查是否是撤回消息 + if (!content.includes('revokemsg') && !content.includes('撤回')) { + return { isRevoke: false } + } + + // 检查是否是 "你撤回了" - 自己撤回 + if (content.includes('你撤回')) { + return { isRevoke: true, isSelfRevoke: true } + } + + // 尝试从 标签提取(格式: wxid_xxx) + const sessionMatch = /([^<]+)<\/session>/i.exec(content) + if (sessionMatch) { + const session = sessionMatch[1].trim() + // 如果 session 是 wxid 格式,返回它 + if (session.startsWith('wxid_') || /^[a-zA-Z][a-zA-Z0-9_-]+$/.test(session)) { + return { isRevoke: true, revokerWxid: session } + } + } + + // 尝试从 提取 + const fromUserMatch = /([^<]+)<\/fromusername>/i.exec(content) + if (fromUserMatch) { + return { isRevoke: true, revokerWxid: fromUserMatch[1].trim() } + } + + // 是撤回消息但无法提取撤回者 + return { isRevoke: true } + } + private extractXmlValue(xml: string, tagName: string): string { const regex = new RegExp(`<${tagName}>([\\s\\S]*?)<\/${tagName}>`, 'i') const match = regex.exec(xml) @@ -1408,7 +1449,30 @@ class ExportService { const isSend = parseInt(isSendRaw, 10) === 1 const localId = parseInt(row.local_id || row.localId || '0', 10) - const actualSender = isSend ? cleanedMyWxid : (senderUsername || sessionId) + // 确定实际发送者 + let actualSender: string + if (localType === 10000 || localType === 266287972401) { + // 系统消息特殊处理 + const revokeInfo = this.extractRevokerInfo(content) + if (revokeInfo.isRevoke) { + // 撤回消息 + if (revokeInfo.isSelfRevoke) { + // "你撤回了" - 发送者是当前用户 + actualSender = cleanedMyWxid + } else if (revokeInfo.revokerWxid) { + // 提取到了撤回者的 wxid + actualSender = revokeInfo.revokerWxid + } else { + // 无法确定撤回者,使用 sessionId + actualSender = sessionId + } + } else { + // 普通系统消息(如"xxx加入群聊"),发送者是群聊ID + actualSender = sessionId + } + } else { + actualSender = isSend ? cleanedMyWxid : (senderUsername || sessionId) + } senderSet.add(actualSender) // 提取媒体相关字段