mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-05 21:05:39 +08:00
feat: read-only roominfo (owner/announcement/openim) on GroupSnapshot
This commit is contained in:
@@ -180,6 +180,8 @@ export interface GroupSnapshot {
|
||||
roomId: string
|
||||
memberCount: number
|
||||
groupName?: string
|
||||
/** 只读 roominfo(contact.db / chatroom);字段可能随版本缺失。 */
|
||||
roomInfo?: GroupRoomInfo
|
||||
members: {
|
||||
wxid: string
|
||||
nickname: string
|
||||
@@ -190,6 +192,18 @@ export interface GroupSnapshot {
|
||||
}[]
|
||||
}
|
||||
|
||||
/** 群 roominfo 只读字段(列名兼容多版本;缺失则为 undefined)。 */
|
||||
export interface GroupRoomInfo {
|
||||
roomId: string
|
||||
owner?: string
|
||||
announcement?: string
|
||||
announcementEditor?: string
|
||||
maxMemberCount?: number
|
||||
chatName?: string
|
||||
openImAccountType?: string
|
||||
isOpenIm?: boolean
|
||||
}
|
||||
|
||||
export interface GroupMembershipSnapshot {
|
||||
roomId: string
|
||||
memberIds: string[]
|
||||
@@ -1007,7 +1021,7 @@ export function getGroupSnapshot(userMd5: string): GroupSnapshot | null {
|
||||
avatar: member.m_nsHeadImgUrl || ''
|
||||
}))
|
||||
|
||||
return { roomId, memberCount: members.length, members }
|
||||
return { roomId, memberCount: members.length, members, roomInfo: wcdb4Client.getRoomInfo(roomId) || undefined }
|
||||
}
|
||||
|
||||
export async function getGroupSnapshotAsync(userMd5: string): Promise<GroupSnapshot | null> {
|
||||
@@ -1032,7 +1046,8 @@ export async function getGroupSnapshotAsync(userMd5: string): Promise<GroupSnaps
|
||||
roomId,
|
||||
groupName: session?.nickname || undefined,
|
||||
memberCount: members.length,
|
||||
members
|
||||
members,
|
||||
roomInfo: (await wcdb4Client.getRoomInfoAsync(roomId)) || undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,51 @@ import crypto from 'crypto'
|
||||
import { createRequire } from 'module'
|
||||
import { createConnection, Socket } from 'net'
|
||||
import { getResourceRoots } from './resource-paths'
|
||||
|
||||
export type RoomInfoRow = {
|
||||
roomId: string
|
||||
owner?: string
|
||||
announcement?: string
|
||||
announcementEditor?: string
|
||||
maxMemberCount?: number
|
||||
chatName?: string
|
||||
openImAccountType?: string
|
||||
isOpenIm?: boolean
|
||||
}
|
||||
|
||||
function pickString(row: Record<string, unknown>, keys: string[]): string | undefined {
|
||||
for (const key of keys) {
|
||||
const value = row[key]
|
||||
if (typeof value === 'string' && value.trim()) return value.trim()
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return String(value)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function normalizeRoomInfoRow(chatroomId: string, row: Record<string, unknown>): RoomInfoRow {
|
||||
const openImAccountType =
|
||||
pickString(row, ['openim_acct_type', 'openimAcctType', 'openim_type']) || undefined
|
||||
const maxRaw = pickString(row, ['max_member_count', 'maxMemberCount', 'member_max'])
|
||||
return {
|
||||
roomId: chatroomId,
|
||||
owner: pickString(row, ['roomowner', 'room_owner', 'owner', 'owner_id']),
|
||||
announcement: pickString(row, [
|
||||
'textannouncement',
|
||||
'announcement',
|
||||
'announcement_',
|
||||
'chatroomannouncement'
|
||||
]),
|
||||
announcementEditor: pickString(row, [
|
||||
'announcement_editor',
|
||||
'announcement_editor_',
|
||||
'announcementeditor'
|
||||
]),
|
||||
maxMemberCount: maxRaw && Number.isFinite(Number(maxRaw)) ? Number(maxRaw) : undefined,
|
||||
chatName: pickString(row, ['chatroomnick', 'chat_name', 'chatroom_name', 'displayname']),
|
||||
openImAccountType,
|
||||
isOpenIm: openImAccountType ? openImAccountType !== '0' && openImAccountType !== '' : undefined
|
||||
}
|
||||
}
|
||||
import { wcdbDebugLog } from './wcdb-debug'
|
||||
import type { ImageMessageCountProbe } from '../shared/image-text-index'
|
||||
import { imageTextWindowToSeconds } from '../shared/image-text-index'
|
||||
@@ -2678,6 +2723,42 @@ export class Wcdb4Client {
|
||||
return this.accountRoot
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 只读 roominfo(contact.db / chatroom 或兼容列名)。
|
||||
*/
|
||||
getRoomInfo(chatroomId: string): RoomInfoRow | null {
|
||||
if (!this.wcdbExecQuery || !chatroomId.endsWith('@chatroom')) return null
|
||||
const escaped = chatroomId.replace(/'/g, "''")
|
||||
const sql = `SELECT * FROM chatroom WHERE chatroomname = '${escaped}' LIMIT 1`
|
||||
try {
|
||||
const rows = this.callJson<Record<string, unknown>[]>((handle, outJson) =>
|
||||
this.wcdbExecQuery!(handle, 'contact', '', sql, outJson)
|
||||
)
|
||||
return rows?.[0] ? normalizeRoomInfoRow(chatroomId, rows[0]) : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async getRoomInfoAsync(chatroomId: string): Promise<RoomInfoRow | null> {
|
||||
if (!this.wcdbExecQuery || !chatroomId.endsWith('@chatroom')) return null
|
||||
const escaped = chatroomId.replace(/'/g, "''")
|
||||
const sql = `SELECT * FROM chatroom WHERE chatroomname = '${escaped}' LIMIT 1`
|
||||
try {
|
||||
const rows = await this.callJsonAsync<Record<string, unknown>[]>(
|
||||
this.wcdbExecQuery as unknown as KoffiAsyncFunction,
|
||||
'contact',
|
||||
'',
|
||||
sql
|
||||
)
|
||||
return rows?.[0] ? normalizeRoomInfoRow(chatroomId, rows[0]) : null
|
||||
} catch (error) {
|
||||
console.warn('[WCDB4] getRoomInfoAsync failed:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/** 只读列收藏(favorite.db / fav_db_item),供导出与搜索。 */
|
||||
async listFavoriteItems(limit = 200): Promise<Record<string, unknown>[]> {
|
||||
if (!this.wcdbExecQuery) return []
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { normalizeRoomInfoRow } from '../../src/main/wcdb4-client'
|
||||
|
||||
describe('normalizeRoomInfoRow', () => {
|
||||
it('maps versioned chatroom column aliases', () => {
|
||||
expect(
|
||||
normalizeRoomInfoRow('123@chatroom', {
|
||||
chatroomname: '123@chatroom',
|
||||
roomowner: 'wxid_owner',
|
||||
textannouncement: '群公告',
|
||||
announcement_editor_: 'wxid_editor',
|
||||
max_member_count: 500,
|
||||
chatroomnick: '测试群',
|
||||
openim_acct_type: '2'
|
||||
})
|
||||
).toEqual({
|
||||
roomId: '123@chatroom',
|
||||
owner: 'wxid_owner',
|
||||
announcement: '群公告',
|
||||
announcementEditor: 'wxid_editor',
|
||||
maxMemberCount: 500,
|
||||
chatName: '测试群',
|
||||
openImAccountType: '2',
|
||||
isOpenIm: true
|
||||
})
|
||||
expect(normalizeRoomInfoRow('9@chatroom', { room_owner: 'wxid_b' })).toMatchObject({
|
||||
owner: 'wxid_b',
|
||||
isOpenIm: undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user