From d7f7139f36cc875c191c332b31cf15a664bbc24e Mon Sep 17 00:00:00 2001 From: xuncha <1658671838@qq.com> Date: Fri, 13 Mar 2026 20:21:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BF=AE=E5=A4=8D=20https://?= =?UTF-8?q?github.com/hicccc77/WeFlow/issues/378=20=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/services/chatService.ts | 21 +++++- electron/services/httpService.ts | 111 +++++++++++++++++++++++++------ 2 files changed, 107 insertions(+), 25 deletions(-) diff --git a/electron/services/chatService.ts b/electron/services/chatService.ts index 470933a..6f104fd 100644 --- a/electron/services/chatService.ts +++ b/electron/services/chatService.ts @@ -2974,7 +2974,9 @@ class ChatService { const localType = this.getRowInt(row, ['local_type', 'localType', 'type', 'msg_type', 'msgType', 'WCDB_CT_local_type'], 1) const isSendRaw = this.getRowField(row, ['computed_is_send', 'computedIsSend', 'is_send', 'isSend', 'WCDB_CT_is_send']) let isSend = isSendRaw === null ? null : parseInt(isSendRaw, 10) - const senderUsername = this.getRowField(row, ['sender_username', 'senderUsername', 'sender', 'WCDB_CT_sender_username']) || null + const senderUsername = this.getRowField(row, ['sender_username', 'senderUsername', 'sender', 'WCDB_CT_sender_username']) + || this.extractSenderUsernameFromContent(content) + || null const createTime = this.getRowInt(row, ['create_time', 'createTime', 'createtime', 'msg_create_time', 'msgCreateTime', 'msg_time', 'msgTime', 'time', 'WCDB_CT_create_time'], 0) if (senderUsername && (myWxidLower || cleanedWxidLower)) { @@ -4385,7 +4387,18 @@ class ChatService { } private stripSenderPrefix(content: string): string { - return content.replace(/^[\s]*([a-zA-Z0-9_-]+):(?!\/\/)\s*/, '') + return content.replace(/^[\s]*([a-zA-Z0-9_@-]+):(?!\/\/)(?:\s*(?:\r?\n|)\s*|\s*)/i, '') + } + + private extractSenderUsernameFromContent(content: string): string | null { + if (!content) return null + + const normalized = this.cleanUtf16(this.decodeHtmlEntities(String(content))) + const match = /^\s*([a-zA-Z0-9_@-]{4,}):(?!\/\/)\s*(?:\r?\n|)/i.exec(normalized) + if (!match?.[1]) return null + + const candidate = match[1].trim() + return candidate || null } private decodeHtmlEntities(content: string): string { @@ -6594,7 +6607,9 @@ class ChatService { createTime: this.getRowInt(row, ['create_time', 'createTime', 'createtime', 'msg_create_time', 'msgCreateTime', 'msg_time', 'msgTime', 'time', 'WCDB_CT_create_time'], 0), sortSeq: this.getRowInt(row, ['sort_seq', 'sortSeq', 'seq', 'sequence', 'WCDB_CT_sort_seq'], this.getRowInt(row, ['create_time', 'createTime', 'createtime', 'msg_create_time', 'msgCreateTime', 'msg_time', 'msgTime', 'time', 'WCDB_CT_create_time'], 0)), isSend: this.getRowInt(row, ['computed_is_send', 'computedIsSend', 'is_send', 'isSend', 'WCDB_CT_is_send'], 0), - senderUsername: this.getRowField(row, ['sender_username', 'senderUsername', 'sender', 'WCDB_CT_sender_username']) || null, + senderUsername: this.getRowField(row, ['sender_username', 'senderUsername', 'sender', 'WCDB_CT_sender_username']) + || this.extractSenderUsernameFromContent(rawContent) + || null, rawContent: rawContent, content: rawContent, // 添加原始内容供视频MD5解析使用 parsedContent: this.parseMessageContent(rawContent, this.getRowInt(row, ['local_type', 'localType', 'type', 'msg_type', 'msgType', 'WCDB_CT_local_type'], 0)) diff --git a/electron/services/httpService.ts b/electron/services/httpService.ts index 86ea6c9..bfb8ce1 100644 --- a/electron/services/httpService.ts +++ b/electron/services/httpService.ts @@ -340,6 +340,7 @@ class HttpService { const trimmedRows = allRows.slice(0, limit) const finalHasMore = hasMore || allRows.length > limit const messages = chatService.mapRowsToMessagesForApi(trimmedRows) + await this.backfillMissingSenderUsernames(talker, messages) return { success: true, messages, hasMore: finalHasMore } } finally { await wcdbService.closeMessageCursor(cursor) @@ -359,6 +360,41 @@ class HttpService { return Math.min(Math.max(parsed, min), max) } + private async backfillMissingSenderUsernames(talker: string, messages: Message[]): Promise { + if (!talker.endsWith('@chatroom')) return + + const targets = messages.filter((msg) => !String(msg.senderUsername || '').trim()) + if (targets.length === 0) return + + const myWxid = (this.configService.get('myWxid') || '').trim() + for (const msg of targets) { + const localId = Number(msg.localId || 0) + if (Number.isFinite(localId) && localId > 0) { + try { + const detail = await wcdbService.getMessageById(talker, localId) + if (detail.success && detail.message) { + const hydrated = chatService.mapRowsToMessagesForApi([detail.message])[0] + if (hydrated?.senderUsername) { + msg.senderUsername = hydrated.senderUsername + } + if ((msg.isSend === null || msg.isSend === undefined) && hydrated?.isSend !== undefined) { + msg.isSend = hydrated.isSend + } + if (!msg.rawContent && hydrated?.rawContent) { + msg.rawContent = hydrated.rawContent + } + } + } catch (error) { + console.warn('[HttpService] backfill sender failed:', error) + } + } + + if (!msg.senderUsername && msg.isSend === 1 && myWxid) { + msg.senderUsername = myWxid + } + } + } + private parseBooleanParam(url: URL, keys: string[], defaultValue: boolean = false): boolean { for (const key of keys) { const raw = url.searchParams.get(key) @@ -778,6 +814,49 @@ class HttpService { return {} } + private lookupGroupNickname(groupNicknamesMap: Map, sender: string): string { + if (!sender) return '' + return groupNicknamesMap.get(sender) || groupNicknamesMap.get(sender.toLowerCase()) || '' + } + + private resolveChatLabSenderInfo( + msg: Message, + talkerId: string, + talkerName: string, + myWxid: string, + isGroup: boolean, + senderNames: Record, + groupNicknamesMap: Map + ): { sender: string; accountName: string; groupNickname?: string } { + let sender = String(msg.senderUsername || '').trim() + let usedUnknownPlaceholder = false + const sameAsMe = sender && myWxid && sender.toLowerCase() === myWxid.toLowerCase() + const isSelf = msg.isSend === 1 || sameAsMe + + if (!sender && isSelf && myWxid) { + sender = myWxid + } + + if (!sender) { + if (msg.localType === 10000 || msg.localType === 266287972401) { + sender = talkerId + } else { + sender = `unknown_sender_${msg.localId || msg.createTime || 0}` + usedUnknownPlaceholder = true + } + } + + const groupNickname = isGroup ? this.lookupGroupNickname(groupNicknamesMap, sender) : '' + const displayName = senderNames[sender] || groupNickname || (usedUnknownPlaceholder ? '' : sender) + const accountName = isSelf ? '我' : (displayName || '未知发送者') + + return { + sender, + accountName, + groupNickname: groupNickname || undefined + } + } + /** * 转换为 ChatLab 格式 */ @@ -817,36 +896,24 @@ class HttpService { // 构建成员列表 const memberMap = new Map() for (const msg of messages) { - const sender = msg.senderUsername || '' - if (sender && !memberMap.has(sender)) { - const displayName = senderNames[sender] || sender - const isSelf = sender === myWxid || sender.toLowerCase() === myWxid.toLowerCase() - // 获取群昵称(尝试多种方式) - const groupNickname = isGroup - ? (groupNicknamesMap.get(sender) || groupNicknamesMap.get(sender.toLowerCase()) || '') - : '' - memberMap.set(sender, { - platformId: sender, - accountName: isSelf ? '我' : displayName, - groupNickname: groupNickname || undefined + const senderInfo = this.resolveChatLabSenderInfo(msg, talkerId, talkerName, myWxid, isGroup, senderNames, groupNicknamesMap) + if (!memberMap.has(senderInfo.sender)) { + memberMap.set(senderInfo.sender, { + platformId: senderInfo.sender, + accountName: senderInfo.accountName, + groupNickname: senderInfo.groupNickname }) } } // 转换消息 const chatLabMessages: ChatLabMessage[] = messages.map(msg => { - const sender = msg.senderUsername || '' - const isSelf = msg.isSend === 1 || sender === myWxid - const accountName = isSelf ? '我' : (senderNames[sender] || sender) - // 获取该发送者的群昵称 - const groupNickname = isGroup - ? (groupNicknamesMap.get(sender) || groupNicknamesMap.get(sender.toLowerCase()) || '') - : '' + const senderInfo = this.resolveChatLabSenderInfo(msg, talkerId, talkerName, myWxid, isGroup, senderNames, groupNicknamesMap) return { - sender, - accountName, - groupNickname: groupNickname || undefined, + sender: senderInfo.sender, + accountName: senderInfo.accountName, + groupNickname: senderInfo.groupNickname, timestamp: msg.createTime, type: this.mapMessageType(msg.localType, msg), content: this.getMessageContent(msg),