import { Wcdb4Client, Wcdb4MessageQueryOptions } from './wcdb4-client' export interface UserContact { m_nsUsrName: string nickname: string avatar?: string wechatNickname?: string remark?: string isFolded?: boolean isMuted?: boolean } export interface WechatMessage { mesLocalID: string mesDes: number messageType: string msgCreateTime: string msgContent: string // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any } export interface Contact { m_nsUsrName: string m_nsNickName: string md5: string type: 'user' | 'group' avatar?: string } export interface GroupMemberInfo { m_nsUsrName: string nickname: string m_nsHeadImgUrl: string } export class WechatDb { private wcdb4Client: Wcdb4Client private chatMd5ToUsername = new Map() private chatTableMappingLoaded = false static async create(rawKey: string, accountRoot?: string): Promise { const client = new Wcdb4Client(rawKey, accountRoot) await client.openAsync() return new WechatDb(rawKey, accountRoot, client) } constructor( rawKey: string, accountRoot?: string, clientOverride?: Wcdb4Client, initialChatTables?: { name: string; db_number: string }[] ) { console.log(`Initializing WechatDb with key length: ${rawKey.trim().length}`) const client = clientOverride || new Wcdb4Client(rawKey, accountRoot) if (!clientOverride) client.open() this.wcdb4Client = client for (const table of initialChatTables || []) { if (table.name.startsWith('Chat_')) { this.chatMd5ToUsername.set(table.name.substring(5), table.db_number) } } this.chatTableMappingLoaded = Boolean(initialChatTables) } private ensureChatTableMapping(): void { if (this.chatTableMappingLoaded) return for (const table of this.wcdb4Client.getChatTables()) { if (table.name.startsWith('Chat_')) { this.chatMd5ToUsername.set(table.name.substring(5), table.db_number) } } this.chatTableMappingLoaded = true } public getUserList(nicknameFilter?: string): UserContact[] { const keyword = (nicknameFilter || '').trim().toLowerCase() return this.wcdb4Client .getSessions() .map((session) => ({ m_nsUsrName: session.username, nickname: session.nickname || session.username, avatar: session.avatar, wechatNickname: session.wechatNickname, remark: session.remark, isFolded: session.isFolded, isMuted: session.isMuted })) .filter((contact) => { if (!keyword) return true return ( contact.m_nsUsrName.toLowerCase().includes(keyword) || contact.nickname.toLowerCase().includes(keyword) ) }) } public getAllGroupContacts(): Record { const groupContacts: Record = {} for (const session of this.wcdb4Client.getSessions()) { if (session.username.endsWith('@chatroom')) { groupContacts[this.md5(session.username)] = session.nickname || session.username } } return groupContacts } public getAllGroupMembers(): Record { const members: Record = {} for (const session of this.wcdb4Client.getSessions()) { if (!session.username.endsWith('@chatroom')) continue for (const member of this.wcdb4Client.getGroupMembers(session.username)) { if (member.m_nsUsrName) { members[member.m_nsUsrName] = member.nickname || member.m_nsUsrName } } } return members } public getGroupMembersForChat(userMd5: string): Record { this.ensureChatTableMapping() const username = this.chatMd5ToUsername.get(userMd5) if (!username || !username.endsWith('@chatroom')) return {} const members: Record = {} for (const member of this.wcdb4Client.getGroupMembers(username)) { if (member.m_nsUsrName) { members[member.m_nsUsrName] = member.nickname || member.m_nsUsrName } } return members } public getGroupMember(wxid: string, chatroomId?: string): GroupMemberInfo | null { if (!chatroomId) return null return ( this.wcdb4Client.getGroupMembers(chatroomId).find((member) => member.m_nsUsrName === wxid) || null ) } public getAllChatTables(): { name: string; db_number: string }[] { return this.wcdb4Client.getChatTables() } public getMyAvatarUrl(): string | undefined { return this.wcdb4Client.getMyAvatarUrl() } public getWcdb4Client(): Wcdb4Client { return this.wcdb4Client } public close(): void { this.wcdb4Client.close() } public closeAsync(timeoutMs?: number): Promise { return this.wcdb4Client.closeAsync(timeoutMs) } public getUserMessages( userMd5: string, startTime?: number, endTime?: number, options?: Wcdb4MessageQueryOptions ): WechatMessage[] { this.ensureChatTableMapping() const username = this.chatMd5ToUsername.get(userMd5) if (!username) return [] return this.wcdb4Client.getMessages(username, startTime, endTime, options).map((message) => ({ ...message, ...message.raw })) } public async getUserMessagesAsync( userMd5: string, startTime?: number, endTime?: number, options?: Wcdb4MessageQueryOptions ): Promise { this.ensureChatTableMapping() const username = this.chatMd5ToUsername.get(userMd5) if (!username) return [] const messages = await this.wcdb4Client.getMessagesAsync(username, startTime, endTime, options) return messages.map((message) => ({ ...message, ...message.raw })) } public searchAllMessages(keyword: string): string | null { const lowerKeyword = keyword.trim().toLowerCase() if (!lowerKeyword) return null for (const session of this.wcdb4Client.getSessions()) { const found = this.wcdb4Client .getMessages(session.username) .some((message) => message.msgContent.toLowerCase().includes(lowerKeyword)) if (found) return `Chat_${this.md5(session.username)}` } return null } public md5(str: string): string { return this.wcdb4Client.md5(str) } }