From d1f68553f197c52f5a9590b595c3315a39b99f55 Mon Sep 17 00:00:00 2001 From: linyuchen Date: Sat, 1 Jun 2024 20:18:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8A=A0=E8=BD=BD=E5=8D=A1=E9=A1=BF?= =?UTF-8?q?=EF=BC=8C=E7=BE=A4=E6=88=90=E5=91=98=E5=90=8D=E7=89=87=E5=8F=98?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/main.ts | 38 +++- src/ntqqapi/api/group.ts | 16 +- src/ntqqapi/hook.ts | 396 ++++++++++++++++++++------------------- src/ntqqapi/ntcall.ts | 14 +- src/version.ts | 2 +- 5 files changed, 256 insertions(+), 210 deletions(-) diff --git a/src/main/main.ts b/src/main/main.ts index 139cda0..d713a69 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -25,7 +25,7 @@ import { selfInfo, uidMaps, } 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 { ChatType, @@ -200,6 +200,7 @@ function onLoad() { } async function startReceiveHook() { + startHook().then() if (getConfigUtil().getConfig().enablePoke) { crychic.loadNode() crychic.registerPokeHandler((id, isGroup) => { @@ -436,14 +437,33 @@ function onLoad() { uidMaps[value] = key } }) - startReceiveHook().then() - NTQQGroupApi.getGroups(true).then(groups=> { - for (let group of groups) { - } - } - ).catch(log) - NTQQGroupApi.activateMemberInfoChange().then().catch(log) - NTQQGroupApi.activateMemberListChange().then().catch(log) + try{ + log('start get groups') + const _groups = await NTQQGroupApi.getGroups() + log('_groups', _groups) + await Promise.all( + _groups.map(async (group) => { + try { + 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() if (config.ob11.enableHttp) { ob11HTTPServer.start(config.ob11.httpPort) diff --git a/src/ntqqapi/api/group.ts b/src/ntqqapi/api/group.ts index cbc4803..06d00c5 100644 --- a/src/ntqqapi/api/group.ts +++ b/src/ntqqapi/api/group.ts @@ -35,14 +35,20 @@ export class NTQQGroupApi { }) } static async getGroups(forced = false) { - let cbCmd = ReceiveCmdS.GROUPS - if (process.platform != 'win32') { - cbCmd = ReceiveCmdS.GROUPS_STORE - } + // let cbCmd = ReceiveCmdS.GROUPS + // if (process.platform != 'win32') { + // cbCmd = ReceiveCmdS.GROUPS_STORE + // } const result = await callNTQQApi<{ updateType: number 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 } static async getGroupMembers(groupQQ: string, num = 3000): Promise { diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index 8c1a480..ca2708d 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -22,6 +22,7 @@ import { NTQQGroupApi } from './api/group' import { log } from '@/common/utils' import { isNumeric, sleep } from '@/common/utils' import { OB11Constructor } from '../onebot11/constructor' +import { OB11GroupCardEvent } from '../onebot11/event/notice/OB11GroupCardEvent' export let hookApiCallbacks: Record void> = {} @@ -324,207 +325,222 @@ async function processGroupEvent(payload: { groupList: Group[] }) { } } -// 群列表变动 -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() - } - } -}) +export async function startHook() { -registerReceiveHook<{ - groupCode: string - dataSource: number - members: Set -}>(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) { - Object.assign(existMember, member) +// 群列表变动 + registerReceiveHook<{ groupList: Group[]; updateType: number }>(ReceiveCmdS.GROUPS, (payload) => { + // updateType 3是群列表变动,2是群成员变动 + // log("群列表变动", payload.updateType, payload.groupList) + if (payload.updateType != 2) { + updateGroups(payload.groupList).then() } - } - // 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); - // } - // } - // } -}) + 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 + dataSource: number + members: Set + }>(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<{ - data:CategoryFriend[] -}>(ReceiveCmdS.FRIENDS, (payload) => { - rawFriends.length = 0; - rawFriends.push(...payload.data); - for (const fData of payload.data) { - const _friends = fData.buddyList - for (let friend of _friends) { - NTQQMsgApi.activateChat({ peerUid: friend.uid, chatType: ChatType.friend }).then() - let existFriend = friends.find((f) => f.uin == friend.uin) - if (!existFriend) { - friends.push(friend) - } else { - Object.assign(existFriend, friend) + registerReceiveHook<{ + data: CategoryFriend[] + }>(ReceiveCmdS.FRIENDS, (payload) => { + rawFriends.length = 0; + rawFriends.push(...payload.data); + for (const fData of payload.data) { + const _friends = fData.buddyList + for (let friend of _friends) { + NTQQMsgApi.activateChat({ peerUid: friend.uid, chatType: ChatType.friend }).then() + let existFriend = friends.find((f) => f.uin == friend.uin) + if (!existFriend) { + friends.push(friend) + } + else { + Object.assign(existFriend, friend) + } } } - } -}) + }) -registerReceiveHook<{ msgList: Array }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => { - // 保存一下uid - for (const message of payload.msgList) { - const uid = message.senderUid - const uin = message.senderUin - if (uid && uin) { - if (message.chatType === ChatType.temp) { - dbUtil.getReceivedTempUinMap().then((receivedTempUinMap) => { - if (!receivedTempUinMap[uin]) { - receivedTempUinMap[uin] = uid - 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)) + registerReceiveHook<{ msgList: Array }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], (payload) => { + // 保存一下uid + for (const message of payload.msgList) { + const uid = message.senderUid + const uin = message.senderUin + if (uid && uin) { + if (message.chatType === ChatType.temp) { + dbUtil.getReceivedTempUinMap().then((receivedTempUinMap) => { + if (!receivedTempUinMap[uin]) { + receivedTempUinMap[uin] = uid + dbUtil.setReceivedTempUinMap(receivedTempUinMap) } }) - }) - } else { - NTQQMsgApi.activateChat(peer).then() + } + uidMaps[uid] = uin } } - } -}) -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 { 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) + } } - } - 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 }) + }) + }) + +} \ No newline at end of file diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts index 0177765..c9370e4 100644 --- a/src/ntqqapi/ntcall.ts +++ b/src/ntqqapi/ntcall.ts @@ -107,7 +107,7 @@ interface NTQQApiParams { channel?: NTQQApiChannel classNameIsRegister?: boolean args?: unknown[] - cbCmd?: ReceiveCmd | null + cbCmd?: ReceiveCmd | ReceiveCmd[] | null cmdCB?: (payload: any) => boolean afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd timeoutSecond?: number @@ -147,7 +147,8 @@ export function callNTQQApi(params: NTQQApiParams) { success = true resolve(r) } - } else { + } + else { // 这里的callback比较特殊,QQ后端先返回是否调用成功,再返回一条结果数据 const secondCallback = () => { const hookId = registerReceiveHook(cbCmd, (payload) => { @@ -158,7 +159,8 @@ export function callNTQQApi(params: NTQQApiParams) { success = true resolve(payload) } - } else { + } + else { removeReceiveHook(hookId) success = true resolve(payload) @@ -170,7 +172,8 @@ export function callNTQQApi(params: NTQQApiParams) { log(`${methodName} callback`, result) if (result?.result == 0 || result === undefined) { afterFirstCmd && secondCallback() - } else { + } + else { success = true reject(`ntqq api call failed, ${result.errMsg}`) } @@ -188,7 +191,8 @@ export function callNTQQApi(params: NTQQApiParams) { channel, { sender: { - send: (..._args: unknown[]) => {}, + send: (..._args: unknown[]) => { + }, }, }, { type: 'request', callbackId: uuid, eventName }, diff --git a/src/version.ts b/src/version.ts index 7ff7712..751ae18 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const version = '3.26.4' +export const version = '3.26.5'