mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-18 04:20:57 +08:00
feat: 成员Tab中支持设置Owner视角
This commit is contained in:
@@ -357,19 +357,41 @@ async function* parseEchotrace(options: ParseOptions): AsyncGenerator<ParseEvent
|
||||
// 提取群头像(从 avatars 中获取群ID对应的头像)
|
||||
const groupAvatar = groupId ? avatarsMap.get(groupId) : undefined
|
||||
|
||||
// 发送 meta
|
||||
// 快速扫描获取 ownerId(通过 isSend === 1 推断)
|
||||
let ownerId: string | undefined
|
||||
try {
|
||||
await new Promise<void>((resolve) => {
|
||||
const scanStream = fs.createReadStream(filePath, { encoding: 'utf-8' })
|
||||
const scanPipeline = chain([scanStream, parser(), pick({ filter: /^messages\.\d+$/ }), streamValues()])
|
||||
|
||||
scanPipeline.on('data', ({ value }: { value: EchotraceMessage }) => {
|
||||
if (value.isSend === 1 && value.senderUsername && !value.senderUsername.endsWith('@chatroom')) {
|
||||
ownerId = value.senderUsername
|
||||
scanStream.destroy() // 找到后立即停止扫描
|
||||
}
|
||||
})
|
||||
|
||||
scanStream.on('close', () => resolve())
|
||||
scanPipeline.on('end', () => resolve())
|
||||
scanPipeline.on('error', () => resolve())
|
||||
})
|
||||
} catch {
|
||||
// 扫描失败,ownerId 保持 undefined
|
||||
}
|
||||
|
||||
// 发送 meta(包含推断的 ownerId)
|
||||
const meta: ParsedMeta = {
|
||||
name: chatName,
|
||||
platform: KNOWN_PLATFORMS.WECHAT,
|
||||
type: chatType,
|
||||
groupId,
|
||||
groupAvatar,
|
||||
ownerId,
|
||||
}
|
||||
yield { type: 'meta', data: meta }
|
||||
|
||||
// 收集成员和消息
|
||||
const memberMap = new Map<string, MemberInfo>()
|
||||
let messageBatch: ParsedMessage[] = []
|
||||
|
||||
// 流式解析
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -518,4 +540,3 @@ const module_: FormatModule = {
|
||||
}
|
||||
|
||||
export default module_
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface ParsedMeta {
|
||||
type: ChatType
|
||||
groupId?: string // 群ID(群聊类型有值)
|
||||
groupAvatar?: string // 群头像(base64 Data URL)
|
||||
ownerId?: string // 所有者/导出者的 platformId
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,15 +18,7 @@ import {
|
||||
type ParsedMessage,
|
||||
} from '../../parser'
|
||||
import { getDbDir } from '../core'
|
||||
import {
|
||||
initPerfLog,
|
||||
logPerf,
|
||||
logPerfDetail,
|
||||
resetPerfLog,
|
||||
logInfo,
|
||||
logError,
|
||||
logSummary,
|
||||
} from '../core'
|
||||
import { initPerfLog, logPerf, logPerfDetail, resetPerfLog, logInfo, logError, logSummary } from '../core'
|
||||
|
||||
/** 流式导入结果 */
|
||||
export interface StreamImportResult {
|
||||
@@ -75,7 +67,9 @@ function createTempDatabase(dbPath: string): Database.Database {
|
||||
platform TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
group_id TEXT,
|
||||
group_avatar TEXT
|
||||
group_avatar TEXT,
|
||||
owner_id TEXT,
|
||||
schema_version INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS member (
|
||||
@@ -154,7 +148,9 @@ function createDatabaseWithoutIndexes(sessionId: string): Database.Database {
|
||||
type TEXT NOT NULL,
|
||||
imported_at INTEGER NOT NULL,
|
||||
group_id TEXT,
|
||||
group_avatar TEXT
|
||||
group_avatar TEXT,
|
||||
owner_id TEXT,
|
||||
schema_version INTEGER DEFAULT 1
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS member (
|
||||
@@ -264,7 +260,7 @@ export async function streamImport(filePath: string, requestId: string): Promise
|
||||
|
||||
// 准备语句
|
||||
const insertMeta = db.prepare(`
|
||||
INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar) VALUES (?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`)
|
||||
const insertMember = db.prepare(`
|
||||
INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)
|
||||
@@ -386,7 +382,8 @@ export async function streamImport(filePath: string, requestId: string): Promise
|
||||
meta.type,
|
||||
Math.floor(Date.now() / 1000),
|
||||
meta.groupId || null,
|
||||
meta.groupAvatar || null
|
||||
meta.groupAvatar || null,
|
||||
meta.ownerId || null
|
||||
)
|
||||
metaInserted = true
|
||||
}
|
||||
@@ -394,7 +391,12 @@ export async function streamImport(filePath: string, requestId: string): Promise
|
||||
|
||||
onMembers: (members: ParsedMember[]) => {
|
||||
for (const member of members) {
|
||||
insertMember.run(member.platformId, member.accountName || null, member.groupNickname || null, member.avatar || null)
|
||||
insertMember.run(
|
||||
member.platformId,
|
||||
member.accountName || null,
|
||||
member.groupNickname || null,
|
||||
member.avatar || null
|
||||
)
|
||||
const row = getMemberId.get(member.platformId) as { id: number } | undefined
|
||||
if (row) {
|
||||
memberIdMap.set(member.platformId, row.id)
|
||||
@@ -730,7 +732,9 @@ export async function streamParseFileInfo(filePath: string, requestId: string):
|
||||
const db = createTempDatabase(tempDbPath)
|
||||
|
||||
// 准备语句
|
||||
const insertMeta = db.prepare('INSERT INTO meta (name, platform, type, group_id, group_avatar) VALUES (?, ?, ?, ?, ?)')
|
||||
const insertMeta = db.prepare(
|
||||
'INSERT INTO meta (name, platform, type, group_id, group_avatar, owner_id) VALUES (?, ?, ?, ?, ?, ?)'
|
||||
)
|
||||
const insertMember = db.prepare(
|
||||
'INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, avatar) VALUES (?, ?, ?, ?)'
|
||||
)
|
||||
@@ -764,7 +768,8 @@ export async function streamParseFileInfo(filePath: string, requestId: string):
|
||||
parsedMeta.platform,
|
||||
parsedMeta.type,
|
||||
parsedMeta.groupId || null,
|
||||
parsedMeta.groupAvatar || null
|
||||
parsedMeta.groupAvatar || null,
|
||||
parsedMeta.ownerId || null
|
||||
)
|
||||
metaInserted = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user