mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
feat: 一些更新
This commit is contained in:
@@ -208,145 +208,18 @@ class ExportService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 ext_buffer 二进制数据,提取群成员的群昵称
|
||||
* ext_buffer 包含类似 protobuf 编码的数据,格式示例:
|
||||
* wxid_xxx<binary>群昵称<binary>wxid_yyy<binary>群昵称...
|
||||
*/
|
||||
private parseGroupNicknamesFromExtBuffer(buffer: Buffer): Map<string, string> {
|
||||
const nicknameMap = new Map<string, string>()
|
||||
|
||||
try {
|
||||
// 将 buffer 转为字符串,允许部分乱码
|
||||
const raw = buffer.toString('utf8')
|
||||
|
||||
// 提取所有 wxid 格式的字符串: wxid_ 或 wxid_后跟字母数字下划线
|
||||
const wxidPattern = /wxid_[a-z0-9_]+/gi
|
||||
const wxids = raw.match(wxidPattern) || []
|
||||
|
||||
// 对每个 wxid,尝试提取其后的群昵称
|
||||
for (const wxid of wxids) {
|
||||
const wxidLower = wxid.toLowerCase()
|
||||
const wxidIndex = raw.toLowerCase().indexOf(wxidLower)
|
||||
|
||||
if (wxidIndex === -1) continue
|
||||
|
||||
// 从 wxid 结束位置开始查找
|
||||
const afterWxid = raw.slice(wxidIndex + wxid.length)
|
||||
|
||||
// 提取紧跟在 wxid 后面的可打印字符(中文、字母、数字等)
|
||||
// 跳过前面的不可打印字符和特定控制字符
|
||||
let nickname = ''
|
||||
let foundStart = false
|
||||
|
||||
for (let i = 0; i < afterWxid.length && i < 100; i++) {
|
||||
const char = afterWxid[i]
|
||||
const code = char.charCodeAt(0)
|
||||
|
||||
// 判断是否为可打印字符(中文、字母、数字、常见符号)
|
||||
const isPrintable = (
|
||||
(code >= 0x4E00 && code <= 0x9FFF) || // 中文
|
||||
(code >= 0x3000 && code <= 0x303F) || // CJK 符号
|
||||
(code >= 0xFF00 && code <= 0xFFEF) || // 全角字符
|
||||
(code >= 0x20 && code <= 0x7E) // ASCII 可打印字符
|
||||
)
|
||||
|
||||
if (isPrintable && code !== 0x01 && code !== 0x18) {
|
||||
foundStart = true
|
||||
nickname += char
|
||||
} else if (foundStart) {
|
||||
// 遇到不可打印字符,停止
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 清理昵称:去除前后空白和特殊字符
|
||||
nickname = this.normalizeGroupNickname(nickname)
|
||||
|
||||
// 只保存有效的群昵称(长度 > 0 且 < 50)
|
||||
if (nickname && nickname.length > 0 && nickname.length < 50) {
|
||||
nicknameMap.set(wxidLower, nickname)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// 解析失败时返回空 Map
|
||||
console.error('Failed to parse ext_buffer:', e)
|
||||
}
|
||||
|
||||
return nicknameMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 contact.db 的 chat_room 表获取群成员的群昵称
|
||||
* @param chatroomId 群聊ID (如 "xxxxx@chatroom")
|
||||
* @returns Map<wxid, 群昵称>
|
||||
* 从 DLL 获取群成员的群昵称
|
||||
*/
|
||||
async getGroupNicknamesForRoom(chatroomId: string): Promise<Map<string, string>> {
|
||||
console.log('========== getGroupNicknamesForRoom START ==========', chatroomId)
|
||||
try {
|
||||
// 查询 contact.db 的 chat_room 表
|
||||
// path设为null,因为contact.db已经随handle一起打开了
|
||||
const sql = `SELECT ext_buffer FROM chat_room WHERE username = '${chatroomId.replace(/'/g, "''")}'`
|
||||
console.log('执行SQL查询:', sql)
|
||||
|
||||
const result = await wcdbService.execQuery('contact', null, sql)
|
||||
console.log('execQuery结果:', { success: result.success, rowCount: result.rows?.length, error: result.error })
|
||||
|
||||
if (!result.success || !result.rows || result.rows.length === 0) {
|
||||
console.log('❌ 群昵称查询失败或无数据:', chatroomId, result.error)
|
||||
return new Map<string, string>()
|
||||
const result = await wcdbService.getGroupNicknames(chatroomId)
|
||||
if (result.success && result.nicknames) {
|
||||
return new Map(Object.entries(result.nicknames))
|
||||
}
|
||||
|
||||
let extBuffer = result.rows[0].ext_buffer
|
||||
console.log('ext_buffer原始类型:', typeof extBuffer, 'isBuffer:', Buffer.isBuffer(extBuffer))
|
||||
|
||||
// execQuery返回的二进制数据会被编码为字符串(hex或base64)
|
||||
// 需要转换回Buffer
|
||||
if (typeof extBuffer === 'string') {
|
||||
console.log('🔄 ext_buffer是字符串,尝试转换为Buffer...')
|
||||
|
||||
// 尝试判断是hex还是base64
|
||||
if (this.looksLikeHex(extBuffer)) {
|
||||
console.log('✅ 检测到hex编码,使用hex解码')
|
||||
extBuffer = Buffer.from(extBuffer, 'hex')
|
||||
} else if (this.looksLikeBase64(extBuffer)) {
|
||||
console.log('✅ 检测到base64编码,使用base64解码')
|
||||
extBuffer = Buffer.from(extBuffer, 'base64')
|
||||
} else {
|
||||
// 默认尝试hex
|
||||
console.log(' 无法判断编码格式,默认尝试hex')
|
||||
try {
|
||||
extBuffer = Buffer.from(extBuffer, 'hex')
|
||||
} catch (e) {
|
||||
console.log('❌ hex解码失败,尝试base64')
|
||||
extBuffer = Buffer.from(extBuffer, 'base64')
|
||||
}
|
||||
}
|
||||
console.log('✅ 转换后的Buffer长度:', extBuffer.length)
|
||||
}
|
||||
|
||||
if (!extBuffer || !Buffer.isBuffer(extBuffer)) {
|
||||
console.log('❌ ext_buffer转换失败,不是Buffer类型:', typeof extBuffer)
|
||||
return new Map<string, string>()
|
||||
}
|
||||
|
||||
console.log('✅ 开始解析ext_buffer, 长度:', extBuffer.length)
|
||||
const nicknamesMap = this.parseGroupNicknamesFromExtBuffer(extBuffer)
|
||||
console.log('✅ 解析完成, 找到', nicknamesMap.size, '个群昵称')
|
||||
|
||||
// 打印前5个群昵称作为示例
|
||||
let count = 0
|
||||
for (const [wxid, nickname] of nicknamesMap.entries()) {
|
||||
if (count++ < 5) {
|
||||
console.log(` - ${wxid}: "${nickname}"`)
|
||||
}
|
||||
}
|
||||
|
||||
return nicknamesMap
|
||||
} catch (e) {
|
||||
console.error('❌ getGroupNicknamesForRoom异常:', e)
|
||||
return new Map<string, string>()
|
||||
} finally {
|
||||
console.log('========== getGroupNicknamesForRoom END ==========')
|
||||
} catch (e) {
|
||||
console.error('getGroupNicknamesForRoom error:', e)
|
||||
return new Map<string, string>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2329,11 +2202,9 @@ class ExportService {
|
||||
}
|
||||
|
||||
// 预加载群昵称 (仅群聊且完整列模式)
|
||||
console.log('预加载群昵称检查: isGroup=', isGroup, 'useCompactColumns=', useCompactColumns, 'sessionId=', sessionId)
|
||||
const groupNicknamesMap = (isGroup && !useCompactColumns)
|
||||
? await this.getGroupNicknamesForRoom(sessionId)
|
||||
: new Map<string, string>()
|
||||
console.log('群昵称Map大小:', groupNicknamesMap.size)
|
||||
|
||||
|
||||
// 填充数据
|
||||
@@ -2475,11 +2346,11 @@ class ExportService {
|
||||
)
|
||||
: (mediaItem?.relativePath
|
||||
|| this.formatPlainExportContent(
|
||||
msg.content,
|
||||
msg.localType,
|
||||
options,
|
||||
voiceTranscriptMap.get(msg.localId)
|
||||
))
|
||||
msg.content,
|
||||
msg.localType,
|
||||
options,
|
||||
voiceTranscriptMap.get(msg.localId)
|
||||
))
|
||||
|
||||
// 调试日志
|
||||
if (msg.localType === 3 || msg.localType === 47) {
|
||||
@@ -2724,11 +2595,11 @@ class ExportService {
|
||||
)
|
||||
: (mediaItem?.relativePath
|
||||
|| this.formatPlainExportContent(
|
||||
msg.content,
|
||||
msg.localType,
|
||||
options,
|
||||
voiceTranscriptMap.get(msg.localId)
|
||||
))
|
||||
msg.content,
|
||||
msg.localType,
|
||||
options,
|
||||
voiceTranscriptMap.get(msg.localId)
|
||||
))
|
||||
|
||||
let senderRole: string
|
||||
let senderWxid: string
|
||||
|
||||
Reference in New Issue
Block a user