fix: 加载卡顿,群成员名片变动

This commit is contained in:
linyuchen 2024-06-01 20:18:38 +08:00
parent f47f0800de
commit d1f68553f1
5 changed files with 256 additions and 210 deletions

View File

@ -25,7 +25,7 @@ import {
selfInfo, selfInfo,
uidMaps, uidMaps,
} from '../common/data' } from '../common/data'
import { hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmdS, registerReceiveHook } from '../ntqqapi/hook' import { hookNTQQApiCall, hookNTQQApiReceive, ReceiveCmdS, registerReceiveHook, startHook } from '../ntqqapi/hook'
import { OB11Constructor } from '../onebot11/constructor' import { OB11Constructor } from '../onebot11/constructor'
import { import {
ChatType, ChatType,
@ -200,6 +200,7 @@ function onLoad() {
} }
async function startReceiveHook() { async function startReceiveHook() {
startHook().then()
if (getConfigUtil().getConfig().enablePoke) { if (getConfigUtil().getConfig().enablePoke) {
crychic.loadNode() crychic.loadNode()
crychic.registerPokeHandler((id, isGroup) => { crychic.registerPokeHandler((id, isGroup) => {
@ -436,14 +437,33 @@ function onLoad() {
uidMaps[value] = key uidMaps[value] = key
} }
}) })
startReceiveHook().then() try{
NTQQGroupApi.getGroups(true).then(groups=> { log('start get groups')
for (let group of groups) { const _groups = await NTQQGroupApi.getGroups()
} log('_groups', _groups)
} await Promise.all(
).catch(log) _groups.map(async (group) => {
NTQQGroupApi.activateMemberInfoChange().then().catch(log) try {
NTQQGroupApi.activateMemberListChange().then().catch(log) const members = await NTQQGroupApi.getGroupMembers(group.groupCode)
group.members = members
groups.push(group)
} catch (e) {
log('获取群成员失败', e)
}
})
)
}
catch (e) {
log('获取群列表失败', e)
}
finally {
log('start activate group member info')
NTQQGroupApi.activateMemberInfoChange().then().catch(log)
NTQQGroupApi.activateMemberListChange().then().catch(log)
startReceiveHook().then()
}
const config = getConfigUtil().getConfig() const config = getConfigUtil().getConfig()
if (config.ob11.enableHttp) { if (config.ob11.enableHttp) {
ob11HTTPServer.start(config.ob11.httpPort) ob11HTTPServer.start(config.ob11.httpPort)

View File

@ -35,14 +35,20 @@ export class NTQQGroupApi {
}) })
} }
static async getGroups(forced = false) { static async getGroups(forced = false) {
let cbCmd = ReceiveCmdS.GROUPS // let cbCmd = ReceiveCmdS.GROUPS
if (process.platform != 'win32') { // if (process.platform != 'win32') {
cbCmd = ReceiveCmdS.GROUPS_STORE // cbCmd = ReceiveCmdS.GROUPS_STORE
} // }
const result = await callNTQQApi<{ const result = await callNTQQApi<{
updateType: number updateType: number
groupList: Group[] groupList: Group[]
}>({ methodName: NTQQApiMethod.GROUPS, args: [{ force_update: forced }, undefined], cbCmd }) }>({
methodName: NTQQApiMethod.GROUPS,
args: [{ force_update: forced }, undefined],
cbCmd: [ReceiveCmdS.GROUPS, ReceiveCmdS.GROUPS_STORE],
afterFirstCmd: false,
})
log('get groups result', result)
return result.groupList return result.groupList
} }
static async getGroupMembers(groupQQ: string, num = 3000): Promise<GroupMember[]> { static async getGroupMembers(groupQQ: string, num = 3000): Promise<GroupMember[]> {

View File

@ -22,6 +22,7 @@ import { NTQQGroupApi } from './api/group'
import { log } from '@/common/utils' import { log } from '@/common/utils'
import { isNumeric, sleep } from '@/common/utils' import { isNumeric, sleep } from '@/common/utils'
import { OB11Constructor } from '../onebot11/constructor' import { OB11Constructor } from '../onebot11/constructor'
import { OB11GroupCardEvent } from '../onebot11/event/notice/OB11GroupCardEvent'
export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {} export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
@ -324,207 +325,222 @@ async function processGroupEvent(payload: { groupList: Group[] }) {
} }
} }
// 群列表变动 export async function startHook() {
registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS, (payload) => {
// updateType 3是群列表变动2是群成员变动
// log("群列表变动", payload.updateType, payload.groupList)
if (payload.updateType != 2) {
updateGroups(payload.groupList).then()
} else {
if (process.platform == 'win32') {
processGroupEvent(payload).then()
}
}
})
registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS_STORE, (payload) => {
// updateType 3是群列表变动2是群成员变动
// log("群列表变动", payload.updateType, payload.groupList)
if (payload.updateType != 2) {
updateGroups(payload.groupList).then()
} else {
if (process.platform != 'win32') {
processGroupEvent(payload).then()
}
}
})
registerReceiveHook<{ // 群列表变动
groupCode: string registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS, (payload) => {
dataSource: number // updateType 3是群列表变动2是群成员变动
members: Set<GroupMember> // log("群列表变动", payload.updateType, payload.groupList)
}>(ReceiveCmdS.GROUP_MEMBER_INFO_UPDATE, async (payload) => { if (payload.updateType != 2) {
const groupCode = payload.groupCode updateGroups(payload.groupList).then()
const members = Array.from(payload.members.values())
// log("群成员信息变动", groupCode, members)
for (const member of members) {
const existMember = await getGroupMember(groupCode, member.uin)
if (existMember) {
Object.assign(existMember, member)
} }
} else {
// const existGroup = groups.find(g => g.groupCode == groupCode); if (process.platform == 'win32') {
// if (existGroup) { processGroupEvent(payload).then()
// log("对比群成员", existGroup.members, members) }
// for (const member of members) { }
// const existMember = existGroup.members.find(m => m.uin == member.uin); })
// if (existMember) { registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS_STORE, (payload) => {
// log("对比群名片", existMember.cardName, member.cardName) // updateType 3是群列表变动2是群成员变动
// if (existMember.cardName != member.cardName) { // log("群列表变动", payload.updateType, payload.groupList)
// postOB11Event(new OB11GroupCardEvent(parseInt(existGroup.groupCode), parseInt(member.uin), member.cardName, existMember.cardName)); if (payload.updateType != 2) {
// } updateGroups(payload.groupList).then()
// Object.assign(existMember, member); }
// } else {
// } if (process.platform != 'win32') {
// } processGroupEvent(payload).then()
}) }
}
})
registerReceiveHook<{
groupCode: string
dataSource: number
members: Set<GroupMember>
}>(ReceiveCmdS.GROUP_MEMBER_INFO_UPDATE, async (payload) => {
const groupCode = payload.groupCode
const members = Array.from(payload.members.values())
// log("群成员信息变动", groupCode, members)
for (const member of members) {
const existMember = await getGroupMember(groupCode, member.uin)
if (existMember) {
if (member.cardName != existMember.cardName) {
log('群成员名片变动', `${groupCode}: ${existMember.uin}`, existMember.cardName, '->', member.cardName)
postOb11Event(
new OB11GroupCardEvent(parseInt(groupCode), parseInt(member.uin), member.cardName, existMember.cardName),
)
}
Object.assign(existMember, member)
}
}
// const existGroup = groups.find(g => g.groupCode == groupCode);
// if (existGroup) {
// log("对比群成员", existGroup.members, members)
// for (const member of members) {
// const existMember = existGroup.members.find(m => m.uin == member.uin);
// if (existMember) {
// log("对比群名片", existMember.cardName, member.cardName)
// if (existMember.cardName != member.cardName) {
// postOB11Event(new OB11GroupCardEvent(parseInt(existGroup.groupCode), parseInt(member.uin), member.cardName, existMember.cardName));
// }
// Object.assign(existMember, member);
// }
// }
// }
})
// 好友列表变动 // 好友列表变动
registerReceiveHook<{ registerReceiveHook<{
data:CategoryFriend[] data: CategoryFriend[]
}>(ReceiveCmdS.FRIENDS, (payload) => { }>(ReceiveCmdS.FRIENDS, (payload) => {
rawFriends.length = 0; rawFriends.length = 0;
rawFriends.push(...payload.data); rawFriends.push(...payload.data);
for (const fData of payload.data) { for (const fData of payload.data) {
const _friends = fData.buddyList const _friends = fData.buddyList
for (let friend of _friends) { for (let friend of _friends) {
NTQQMsgApi.activateChat({ peerUid: friend.uid, chatType: ChatType.friend }).then() NTQQMsgApi.activateChat({ peerUid: friend.uid, chatType: ChatType.friend }).then()
let existFriend = friends.find((f) => f.uin == friend.uin) let existFriend = friends.find((f) => f.uin == friend.uin)
if (!existFriend) { if (!existFriend) {
friends.push(friend) friends.push(friend)
} else { }
Object.assign(existFriend, friend) else {
Object.assign(existFriend, friend)
}
} }
} }
} })
})
registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => { registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => {
// 保存一下uid // 保存一下uid
for (const message of payload.msgList) { for (const message of payload.msgList) {
const uid = message.senderUid const uid = message.senderUid
const uin = message.senderUin const uin = message.senderUin
if (uid && uin) { if (uid && uin) {
if (message.chatType === ChatType.temp) { if (message.chatType === ChatType.temp) {
dbUtil.getReceivedTempUinMap().then((receivedTempUinMap) => { dbUtil.getReceivedTempUinMap().then((receivedTempUinMap) => {
if (!receivedTempUinMap[uin]) { if (!receivedTempUinMap[uin]) {
receivedTempUinMap[uin] = uid receivedTempUinMap[uin] = uid
dbUtil.setReceivedTempUinMap(receivedTempUinMap) dbUtil.setReceivedTempUinMap(receivedTempUinMap)
}
})
}
uidMaps[uid] = uin
}
}
// 自动清理新消息文件
const { autoDeleteFile } = getConfigUtil().getConfig()
if (!autoDeleteFile) {
return
}
for (const message of payload.msgList) {
// log("收到新消息push到历史记录", message.msgId)
// dbUtil.addMsg(message).then()
// 清理文件
for (const msgElement of message.elements) {
setTimeout(() => {
const picPath = msgElement.picElement?.sourcePath
const picThumbPath = [...msgElement.picElement?.thumbPath.values()]
const pttPath = msgElement.pttElement?.filePath
const filePath = msgElement.fileElement?.filePath
const videoPath = msgElement.videoElement?.filePath
const videoThumbPath: string[] = [...msgElement.videoElement?.thumbPath.values()]
const pathList = [picPath, ...picThumbPath, pttPath, filePath, videoPath, ...videoThumbPath]
if (msgElement.picElement) {
pathList.push(...Object.values(msgElement.picElement.thumbPath))
}
const aioOpGrayTipElement = msgElement.grayTipElement?.aioOpGrayTipElement
if (aioOpGrayTipElement) {
tempGroupCodeMap[aioOpGrayTipElement.peerUid] = aioOpGrayTipElement.fromGrpCodeOfTmpChat
}
// log("需要清理的文件", pathList);
for (const path of pathList) {
if (path) {
fs.unlink(picPath, () => {
log('删除文件成功', path)
})
}
}
}, getConfigUtil().getConfig().autoDeleteFileSecond * 1000)
}
}
})
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
const message = msgRecord
const peerUid = message.peerUid
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
dbUtil.addMsg(message).then()
const sendCallback = sendMessagePool[peerUid]
if (sendCallback) {
try {
sendCallback(message)
} catch (e) {
log('receive self msg error', e.stack)
}
}
})
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
selfInfo.online = info.info.status !== 20
})
let activatedPeerUids: string[] = []
registerReceiveHook<{
changedRecentContactLists: {
listType: number
sortedContactList: string[]
changedList: {
id: string // peerUid
chatType: ChatType
}[]
}[]
}>(ReceiveCmdS.RECENT_CONTACT, async (payload) => {
for (const recentContact of payload.changedRecentContactLists) {
for (const changedContact of recentContact.changedList) {
if (activatedPeerUids.includes(changedContact.id)) continue
activatedPeerUids.push(changedContact.id)
const peer = { peerUid: changedContact.id, chatType: changedContact.chatType }
if (changedContact.chatType === ChatType.temp) {
log('收到临时会话消息', peer)
NTQQMsgApi.activateChatAndGetHistory(peer).then(() => {
NTQQMsgApi.getMsgHistory(peer, '', 20).then(({ msgList }) => {
let lastTempMsg = msgList.pop()
log('激活窗口之前的第一条临时会话消息:', lastTempMsg)
if (Date.now() / 1000 - parseInt(lastTempMsg.msgTime) < 5) {
OB11Constructor.message(lastTempMsg).then((r) => postOb11Event(r))
} }
}) })
}) }
} else { uidMaps[uid] = uin
NTQQMsgApi.activateChat(peer).then()
} }
} }
}
})
registerCallHook(NTQQApiMethod.DELETE_ACTIVE_CHAT, async (payload) => { // 自动清理新消息文件
const peerUid = payload[0] as string const { autoDeleteFile } = getConfigUtil().getConfig()
log('激活的聊天窗口被删除,准备重新激活', peerUid) if (!autoDeleteFile) {
let chatType = ChatType.friend return
if (isNumeric(peerUid)) { }
chatType = ChatType.group for (const message of payload.msgList) {
} else { // log("收到新消息push到历史记录", message.msgId)
// 检查是否好友 // dbUtil.addMsg(message).then()
if (!(await getFriend(peerUid))) { // 清理文件
chatType = ChatType.temp
for (const msgElement of message.elements) {
setTimeout(() => {
const picPath = msgElement.picElement?.sourcePath
const picThumbPath = [...msgElement.picElement?.thumbPath.values()]
const pttPath = msgElement.pttElement?.filePath
const filePath = msgElement.fileElement?.filePath
const videoPath = msgElement.videoElement?.filePath
const videoThumbPath: string[] = [...msgElement.videoElement?.thumbPath.values()]
const pathList = [picPath, ...picThumbPath, pttPath, filePath, videoPath, ...videoThumbPath]
if (msgElement.picElement) {
pathList.push(...Object.values(msgElement.picElement.thumbPath))
}
const aioOpGrayTipElement = msgElement.grayTipElement?.aioOpGrayTipElement
if (aioOpGrayTipElement) {
tempGroupCodeMap[aioOpGrayTipElement.peerUid] = aioOpGrayTipElement.fromGrpCodeOfTmpChat
}
// log("需要清理的文件", pathList);
for (const path of pathList) {
if (path) {
fs.unlink(picPath, () => {
log('删除文件成功', path)
})
}
}
}, getConfigUtil().getConfig().autoDeleteFileSecond * 1000)
}
} }
}
const peer = { peerUid, chatType }
await sleep(1000)
NTQQMsgApi.activateChat(peer).then((r) => {
log('重新激活聊天窗口', peer, { result: r.result, errMsg: r.errMsg })
}) })
})
registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({ msgRecord }) => {
const message = msgRecord
const peerUid = message.peerUid
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
// log("收到自己发送成功的消息", message.msgId, message.msgSeq);
dbUtil.addMsg(message).then()
const sendCallback = sendMessagePool[peerUid]
if (sendCallback) {
try {
sendCallback(message)
} catch (e) {
log('receive self msg error', e.stack)
}
}
})
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
selfInfo.online = info.info.status !== 20
})
let activatedPeerUids: string[] = []
registerReceiveHook<{
changedRecentContactLists: {
listType: number
sortedContactList: string[]
changedList: {
id: string // peerUid
chatType: ChatType
}[]
}[]
}>(ReceiveCmdS.RECENT_CONTACT, async (payload) => {
for (const recentContact of payload.changedRecentContactLists) {
for (const changedContact of recentContact.changedList) {
if (activatedPeerUids.includes(changedContact.id)) continue
activatedPeerUids.push(changedContact.id)
const peer = { peerUid: changedContact.id, chatType: changedContact.chatType }
if (changedContact.chatType === ChatType.temp) {
log('收到临时会话消息', peer)
NTQQMsgApi.activateChatAndGetHistory(peer).then(() => {
NTQQMsgApi.getMsgHistory(peer, '', 20).then(({ msgList }) => {
let lastTempMsg = msgList.pop()
log('激活窗口之前的第一条临时会话消息:', lastTempMsg)
if (Date.now() / 1000 - parseInt(lastTempMsg.msgTime) < 5) {
OB11Constructor.message(lastTempMsg).then((r) => postOb11Event(r))
}
})
})
}
else {
NTQQMsgApi.activateChat(peer).then()
}
}
}
})
registerCallHook(NTQQApiMethod.DELETE_ACTIVE_CHAT, async (payload) => {
const peerUid = payload[0] as string
log('激活的聊天窗口被删除,准备重新激活', peerUid)
let chatType = ChatType.friend
if (isNumeric(peerUid)) {
chatType = ChatType.group
}
else {
// 检查是否好友
if (!(await getFriend(peerUid))) {
chatType = ChatType.temp
}
}
const peer = { peerUid, chatType }
await sleep(1000)
NTQQMsgApi.activateChat(peer).then((r) => {
log('重新激活聊天窗口', peer, { result: r.result, errMsg: r.errMsg })
})
})
}

View File

@ -107,7 +107,7 @@ interface NTQQApiParams {
channel?: NTQQApiChannel channel?: NTQQApiChannel
classNameIsRegister?: boolean classNameIsRegister?: boolean
args?: unknown[] args?: unknown[]
cbCmd?: ReceiveCmd | null cbCmd?: ReceiveCmd | ReceiveCmd[] | null
cmdCB?: (payload: any) => boolean cmdCB?: (payload: any) => boolean
afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd
timeoutSecond?: number timeoutSecond?: number
@ -147,7 +147,8 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
success = true success = true
resolve(r) resolve(r)
} }
} else { }
else {
// 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据 // 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据
const secondCallback = () => { const secondCallback = () => {
const hookId = registerReceiveHook<ReturnType>(cbCmd, (payload) => { const hookId = registerReceiveHook<ReturnType>(cbCmd, (payload) => {
@ -158,7 +159,8 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
success = true success = true
resolve(payload) resolve(payload)
} }
} else { }
else {
removeReceiveHook(hookId) removeReceiveHook(hookId)
success = true success = true
resolve(payload) resolve(payload)
@ -170,7 +172,8 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
log(`${methodName} callback`, result) log(`${methodName} callback`, result)
if (result?.result == 0 || result === undefined) { if (result?.result == 0 || result === undefined) {
afterFirstCmd && secondCallback() afterFirstCmd && secondCallback()
} else { }
else {
success = true success = true
reject(`ntqq api call failed, ${result.errMsg}`) reject(`ntqq api call failed, ${result.errMsg}`)
} }
@ -188,7 +191,8 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
channel, channel,
{ {
sender: { sender: {
send: (..._args: unknown[]) => {}, send: (..._args: unknown[]) => {
},
}, },
}, },
{ type: 'request', callbackId: uuid, eventName }, { type: 'request', callbackId: uuid, eventName },

View File

@ -1 +1 @@
export const version = '3.26.4' export const version = '3.26.5'