mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-18 03:57:02 +08:00
179 lines
5.2 KiB
TypeScript
179 lines
5.2 KiB
TypeScript
import { Wcdb4Client, Wcdb4MessageQueryOptions } from './wcdb4-client'
|
|
|
|
export interface UserContact {
|
|
m_nsUsrName: string
|
|
nickname: string
|
|
avatar?: string
|
|
}
|
|
|
|
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<string, string>()
|
|
|
|
static async create(rawKey: string, accountRoot?: string): Promise<WechatDb> {
|
|
// WCDB native init must run on the Electron main process; worker threads
|
|
// get -1006 from wcdb_init. Initialize the client synchronously here
|
|
// (and keep create() async for callers that already await it).
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
const client = new Wcdb4Client(rawKey, accountRoot)
|
|
client.open()
|
|
resolve(new WechatDb(rawKey, accountRoot, client))
|
|
} catch (error) {
|
|
reject(error instanceof Error ? error : new Error(String(error)))
|
|
}
|
|
})
|
|
}
|
|
|
|
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 || client.getChatTables()) {
|
|
if (table.name.startsWith('Chat_')) {
|
|
this.chatMd5ToUsername.set(table.name.substring(5), table.db_number)
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}))
|
|
.filter((contact) => {
|
|
if (!keyword) return true
|
|
return (
|
|
contact.m_nsUsrName.toLowerCase().includes(keyword) ||
|
|
contact.nickname.toLowerCase().includes(keyword)
|
|
)
|
|
})
|
|
}
|
|
|
|
public getAllGroupContacts(): Record<string, string> {
|
|
const groupContacts: Record<string, string> = {}
|
|
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<string, string> {
|
|
const members: Record<string, string> = {}
|
|
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<string, string> {
|
|
const username = this.chatMd5ToUsername.get(userMd5)
|
|
if (!username || !username.endsWith('@chatroom')) return {}
|
|
|
|
const members: Record<string, string> = {}
|
|
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 getUserMessages(
|
|
userMd5: string,
|
|
startTime?: number,
|
|
endTime?: number,
|
|
options?: Wcdb4MessageQueryOptions
|
|
): WechatMessage[] {
|
|
const username = this.chatMd5ToUsername.get(userMd5)
|
|
if (!username) return []
|
|
return this.wcdb4Client.getMessages(username, startTime, endTime, options).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)
|
|
}
|
|
}
|