From 81821e74d8ef656ec4b4d8bf04b2f9475f142b8e Mon Sep 17 00:00:00 2001 From: linyuchen Date: Sun, 7 Apr 2024 17:37:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=89=8B=E5=8A=A8=E9=A2=91=E7=B9=81?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E8=81=8A=E5=A4=A9=E7=AA=97=E5=8F=A3=E6=97=B6?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=97=A7=E7=9A=84=E7=AA=97=E5=8F=A3=E6=8E=A5?= =?UTF-8?q?=E6=94=B6=E4=B8=8D=E5=88=B0=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ntqqapi/hook.ts | 769 +++++++++++++++++++++++------------------- src/ntqqapi/ntcall.ts | 1 + 2 files changed, 414 insertions(+), 356 deletions(-) diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index 9b32d23..6cd3f2f 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -1,8 +1,8 @@ import {BrowserWindow} from 'electron'; -import {NTQQApiClass} from "./ntcall"; +import {NTQQApiClass, NTQQApiMethod} from "./ntcall"; import {NTQQMsgApi, sendMessagePool} from "./api/msg" import {ChatType, Group, GroupMember, GroupMemberRole, RawMessage, User} from "./types"; -import {friends, getGroupMember, groups, selfInfo, tempGroupCodeMap, uidMaps} from "../common/data"; +import {friends, getFriend, getGroupMember, groups, selfInfo, tempGroupCodeMap, uidMaps} from "../common/data"; import {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent"; import {v4 as uuidv4} from "uuid" import {postOB11Event} from "../onebot11/server/postOB11Event"; @@ -11,446 +11,503 @@ import fs from "fs"; import {dbUtil} from "../common/db"; import {NTQQGroupApi} from "./api/group"; import {log} from "../common/utils/log"; -import {sleep} from "../common/utils/helper"; +import {isNumeric, sleep} from "../common/utils/helper"; import {OB11Constructor} from "../onebot11/constructor"; export let hookApiCallbacks: Record void> = {} export let ReceiveCmdS = { - RECENT_CONTACT: "nodeIKernelRecentContactListener/onRecentContactListChangedVer2", - UPDATE_MSG: "nodeIKernelMsgListener/onMsgInfoListUpdate", - UPDATE_ACTIVE_MSG: "nodeIKernelMsgListener/onActiveMsgInfoUpdate", - NEW_MSG: `nodeIKernelMsgListener/onRecvMsg`, - NEW_ACTIVE_MSG: `nodeIKernelMsgListener/onRecvActiveMsg`, - SELF_SEND_MSG: "nodeIKernelMsgListener/onAddSendMsg", - USER_INFO: "nodeIKernelProfileListener/onProfileSimpleChanged", - USER_DETAIL_INFO: "nodeIKernelProfileListener/onProfileDetailInfoChanged", - GROUPS: "nodeIKernelGroupListener/onGroupListUpdate", - GROUPS_STORE: "onGroupListUpdate", - GROUP_MEMBER_INFO_UPDATE: "nodeIKernelGroupListener/onMemberInfoChange", - FRIENDS: "onBuddyListChange", - MEDIA_DOWNLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaDownloadComplete", - UNREAD_GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated", - GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupSingleScreenNotifies", - FRIEND_REQUEST: "nodeIKernelBuddyListener/onBuddyReqChange", - SELF_STATUS: 'nodeIKernelProfileListener/onSelfStatusChanged', - CACHE_SCAN_FINISH: "nodeIKernelStorageCleanListener/onFinishScan", - MEDIA_UPLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaUploadComplete", - SKEY_UPDATE: "onSkeyUpdate" + RECENT_CONTACT: "nodeIKernelRecentContactListener/onRecentContactListChangedVer2", + UPDATE_MSG: "nodeIKernelMsgListener/onMsgInfoListUpdate", + UPDATE_ACTIVE_MSG: "nodeIKernelMsgListener/onActiveMsgInfoUpdate", + NEW_MSG: `nodeIKernelMsgListener/onRecvMsg`, + NEW_ACTIVE_MSG: `nodeIKernelMsgListener/onRecvActiveMsg`, + SELF_SEND_MSG: "nodeIKernelMsgListener/onAddSendMsg", + USER_INFO: "nodeIKernelProfileListener/onProfileSimpleChanged", + USER_DETAIL_INFO: "nodeIKernelProfileListener/onProfileDetailInfoChanged", + GROUPS: "nodeIKernelGroupListener/onGroupListUpdate", + GROUPS_STORE: "onGroupListUpdate", + GROUP_MEMBER_INFO_UPDATE: "nodeIKernelGroupListener/onMemberInfoChange", + FRIENDS: "onBuddyListChange", + MEDIA_DOWNLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaDownloadComplete", + UNREAD_GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated", + GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupSingleScreenNotifies", + FRIEND_REQUEST: "nodeIKernelBuddyListener/onBuddyReqChange", + SELF_STATUS: 'nodeIKernelProfileListener/onSelfStatusChanged', + CACHE_SCAN_FINISH: "nodeIKernelStorageCleanListener/onFinishScan", + MEDIA_UPLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaUploadComplete", + SKEY_UPDATE: "onSkeyUpdate" } export type ReceiveCmd = typeof ReceiveCmdS[keyof typeof ReceiveCmdS] interface NTQQApiReturnData extends Array { - 0: { - "type": "request", - "eventName": NTQQApiClass, - "callbackId"?: string - }, - 1: - { - cmdName: ReceiveCmd, - cmdType: "event", - payload: PayloadType - }[] + 0: { + "type": "request", + "eventName": NTQQApiClass, + "callbackId"?: string + }, + 1: + { + cmdName: ReceiveCmd, + cmdType: "event", + payload: PayloadType + }[] } let receiveHooks: Array<{ - method: ReceiveCmd[], - hookFunc: ((payload: any) => void | Promise) - id: string + method: ReceiveCmd[], + hookFunc: ((payload: any) => void | Promise) + id: string }> = [] -export function hookNTQQApiReceive(window: BrowserWindow) { - const originalSend = window.webContents.send; - const patchSend = (channel: string, ...args: NTQQApiReturnData) => { - // console.log("hookNTQQApiReceive", channel, args) - let isLogger = false - try { - isLogger = args[0]?.eventName?.startsWith("ns-LoggerApi") - } catch (e) { +let callHooks: Array<{ + method: NTQQApiMethod[], + hookFunc: ((callParams: unknown[]) => void | Promise) +}> = [] + + +export function hookNTQQApiReceive(window: BrowserWindow) { + const originalSend = window.webContents.send; + const patchSend = (channel: string, ...args: NTQQApiReturnData) => { + // console.log("hookNTQQApiReceive", channel, args) + let isLogger = false + try { + isLogger = args[0]?.eventName?.startsWith("ns-LoggerApi") + } catch (e) { - } - if (!isLogger) { - try { - HOOK_LOG && log(`received ntqq api message: ${channel}`, args) - } catch (e) { - log("hook log error", e, args) - } - } - try { - if (args?.[1] instanceof Array) { - for (let receiveData of args?.[1]) { - const ntQQApiMethodName = receiveData.cmdName; - // log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData)) - for (let hook of receiveHooks) { - if (hook.method.includes(ntQQApiMethodName)) { - new Promise((resolve, reject) => { - try { - let _ = hook.hookFunc(receiveData.payload) - if (hook.hookFunc.constructor.name === "AsyncFunction") { - (_ as Promise).then() - } - } catch (e) { - log("hook error", e, receiveData.payload) - } - }).then() - } - } - } - } - if (args[0]?.callbackId) { - // log("hookApiCallback", hookApiCallbacks, args) - const callbackId = args[0].callbackId; - if (hookApiCallbacks[callbackId]) { - // log("callback found") - new Promise((resolve, reject) => { - hookApiCallbacks[callbackId](args[1]); - }).then() - delete hookApiCallbacks[callbackId]; - } - } - } catch (e) { - log("hookNTQQApiReceive error", e.stack.toString(), args) - } - originalSend.call(window.webContents, channel, ...args); } - window.webContents.send = patchSend; + if (!isLogger) { + try { + HOOK_LOG && log(`received ntqq api message: ${channel}`, args) + } catch (e) { + log("hook log error", e, args) + } + } + try { + if (args?.[1] instanceof Array) { + for (let receiveData of args?.[1]) { + const ntQQApiMethodName = receiveData.cmdName; + // log(`received ntqq api message: ${channel} ${ntQQApiMethodName}`, JSON.stringify(receiveData)) + for (let hook of receiveHooks) { + if (hook.method.includes(ntQQApiMethodName)) { + new Promise((resolve, reject) => { + try { + let _ = hook.hookFunc(receiveData.payload) + if (hook.hookFunc.constructor.name === "AsyncFunction") { + (_ as Promise).then() + } + } catch (e) { + log("hook error", e, receiveData.payload) + } + }).then() + } + } + } + } + if (args[0]?.callbackId) { + // log("hookApiCallback", hookApiCallbacks, args) + const callbackId = args[0].callbackId; + if (hookApiCallbacks[callbackId]) { + // log("callback found") + new Promise((resolve, reject) => { + hookApiCallbacks[callbackId](args[1]); + }).then() + delete hookApiCallbacks[callbackId]; + } + } + } catch (e) { + log("hookNTQQApiReceive error", e.stack.toString(), args) + } + originalSend.call(window.webContents, channel, ...args); + } + window.webContents.send = patchSend; } export function hookNTQQApiCall(window: BrowserWindow) { - // 监听调用NTQQApi - let webContents = window.webContents as any; - const ipc_message_proxy = webContents._events["-ipc-message"]?.[0] || webContents._events["-ipc-message"]; + // 监听调用NTQQApi + let webContents = window.webContents as any; + const ipc_message_proxy = webContents._events["-ipc-message"]?.[0] || webContents._events["-ipc-message"]; - const proxyIpcMsg = new Proxy(ipc_message_proxy, { - apply(target, thisArg, args) { - // console.log(thisArg, args); - let isLogger = false - try { - isLogger = args[3][0].eventName.startsWith("ns-LoggerApi") - } catch (e) { + const proxyIpcMsg = new Proxy(ipc_message_proxy, { + apply(target, thisArg, args) { + // console.log(thisArg, args); + let isLogger = false + try { + isLogger = args[3][0].eventName.startsWith("ns-LoggerApi") + } catch (e) { - } - if (!isLogger) { - try { - HOOK_LOG && log("call NTQQ api", thisArg, args); - } catch (e) { + } + if (!isLogger) { + try { + HOOK_LOG && log("call NTQQ api", thisArg, args); + } catch (e) { - } - } - return target.apply(thisArg, args); - }, - }); - if (webContents._events["-ipc-message"]?.[0]) { - webContents._events["-ipc-message"][0] = proxyIpcMsg; - } else { - webContents._events["-ipc-message"] = proxyIpcMsg; - } - - const ipc_invoke_proxy = webContents._events["-ipc-invoke"]?.[0] || webContents._events["-ipc-invoke"]; - const proxyIpcInvoke = new Proxy(ipc_invoke_proxy, { - apply(target, thisArg, args) { - // console.log(args); - HOOK_LOG && log("call NTQQ invoke api", thisArg, args) - args[0]["_replyChannel"]["sendReply"] = new Proxy(args[0]["_replyChannel"]["sendReply"], { - apply(sendtarget, sendthisArg, sendargs) { - sendtarget.apply(sendthisArg, sendargs); - } - }); - let ret = target.apply(thisArg, args); - try { - HOOK_LOG && log("call NTQQ invoke api return", ret) - } catch (e) { - - } - return ret; } - }); - if (webContents._events["-ipc-invoke"]?.[0]) { - webContents._events["-ipc-invoke"][0] = proxyIpcInvoke; - } else { - webContents._events["-ipc-invoke"] = proxyIpcInvoke; + try { + const _args: unknown[] = args[3][1]; + const cmdName: NTQQApiMethod = _args[0] as NTQQApiMethod; + const callParams = _args.slice(1); + callHooks.forEach(hook => { + if (hook.method.includes(cmdName)) { + new Promise((resolve, reject) => { + try { + let _ = hook.hookFunc(callParams) + if (hook.hookFunc.constructor.name === "AsyncFunction") { + (_ as Promise).then() + } + } catch (e) { + log("hook call error", e, _args) + } + }).then() + } + }) + } catch (e) { + + } + } + return target.apply(thisArg, args); + }, + }); + if (webContents._events["-ipc-message"]?.[0]) { + webContents._events["-ipc-message"][0] = proxyIpcMsg; + } else { + webContents._events["-ipc-message"] = proxyIpcMsg; + } + + const ipc_invoke_proxy = webContents._events["-ipc-invoke"]?.[0] || webContents._events["-ipc-invoke"]; + const proxyIpcInvoke = new Proxy(ipc_invoke_proxy, { + apply(target, thisArg, args) { + // console.log(args); + HOOK_LOG && log("call NTQQ invoke api", thisArg, args) + args[0]["_replyChannel"]["sendReply"] = new Proxy(args[0]["_replyChannel"]["sendReply"], { + apply(sendtarget, sendthisArg, sendargs) { + sendtarget.apply(sendthisArg, sendargs); + } + }); + let ret = target.apply(thisArg, args); + try { + HOOK_LOG && log("call NTQQ invoke api return", ret) + } catch (e) { + + } + return ret; } + }); + if (webContents._events["-ipc-invoke"]?.[0]) { + webContents._events["-ipc-invoke"][0] = proxyIpcInvoke; + } else { + webContents._events["-ipc-invoke"] = proxyIpcInvoke; + } } export function registerReceiveHook(method: ReceiveCmd | ReceiveCmd[], hookFunc: (payload: PayloadType) => void): string { - const id = uuidv4() - if (!Array.isArray(method)) { - method = [method] - } - receiveHooks.push({ - method, - hookFunc, - id - }) - return id; + const id = uuidv4() + if (!Array.isArray(method)) { + method = [method] + } + receiveHooks.push({ + method, + hookFunc, + id + }) + return id; +} + +export function registerCallHook(method: NTQQApiMethod | NTQQApiMethod[], hookFunc: (callParams: unknown[]) => void | Promise): void { + if (!Array.isArray(method)) { + method = [method] + } + callHooks.push({ + method, + hookFunc + }) } export function removeReceiveHook(id: string) { - const index = receiveHooks.findIndex(h => h.id === id) - receiveHooks.splice(index, 1); + const index = receiveHooks.findIndex(h => h.id === id) + receiveHooks.splice(index, 1); } let activatedGroups: string[] = []; async function updateGroups(_groups: Group[], needUpdate: boolean = true) { - for (let group of _groups) { - log("update group", group) - // if (!activatedGroups.includes(group.groupCode)) { - NTQQMsgApi.activateChat({peerUid: group.groupCode, chatType: ChatType.group}).then((r) => { - // activatedGroups.push(group.groupCode); - // log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r) - // if (r.result !== 0) { - // setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500); - // }else { - // } - }).catch(log) - // } - let existGroup = groups.find(g => g.groupCode == group.groupCode); - if (existGroup) { - Object.assign(existGroup, group); - } else { - groups.push(group); - existGroup = group; - } - - if (needUpdate) { - const members = await NTQQGroupApi.getGroupMembers(group.groupCode); - - if (members) { - existGroup.members = members; - } - } + for (let group of _groups) { + log("update group", group) + // if (!activatedGroups.includes(group.groupCode)) { + NTQQMsgApi.activateChat({peerUid: group.groupCode, chatType: ChatType.group}).then((r) => { + // activatedGroups.push(group.groupCode); + // log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r) + // if (r.result !== 0) { + // setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500); + // }else { + // } + }).catch(log) + // } + let existGroup = groups.find(g => g.groupCode == group.groupCode); + if (existGroup) { + Object.assign(existGroup, group); + } else { + groups.push(group); + existGroup = group; } + + if (needUpdate) { + const members = await NTQQGroupApi.getGroupMembers(group.groupCode); + + if (members) { + existGroup.members = members; + } + } + } } async function processGroupEvent(payload: { groupList: Group[] }) { - try { - const newGroupList = payload.groupList; - for (const group of newGroupList) { - let existGroup = groups.find(g => g.groupCode == group.groupCode); - if (existGroup) { - if (existGroup.memberCount > group.memberCount) { - log(`群(${group.groupCode})成员数量减少${existGroup.memberCount} -> ${group.memberCount}`); - const oldMembers = existGroup.members; + try { + const newGroupList = payload.groupList; + for (const group of newGroupList) { + let existGroup = groups.find(g => g.groupCode == group.groupCode); + if (existGroup) { + if (existGroup.memberCount > group.memberCount) { + log(`群(${group.groupCode})成员数量减少${existGroup.memberCount} -> ${group.memberCount}`); + const oldMembers = existGroup.members; - await sleep(200); // 如果请求QQ API的速度过快,通常无法正确拉取到最新的群信息,因此这里人为引入一个延时 - const newMembers = await NTQQGroupApi.getGroupMembers(group.groupCode); + await sleep(200); // 如果请求QQ API的速度过快,通常无法正确拉取到最新的群信息,因此这里人为引入一个延时 + const newMembers = await NTQQGroupApi.getGroupMembers(group.groupCode); - group.members = newMembers; - const newMembersSet = new Set(); // 建立索引降低时间复杂度 + group.members = newMembers; + const newMembersSet = new Set(); // 建立索引降低时间复杂度 - for (const member of newMembers) { - newMembersSet.add(member.uin); - } - - // 判断bot是否是管理员,如果是管理员不需要从这里得知有人退群,这里的退群无法得知是主动退群还是被踢 - let bot = await getGroupMember(group.groupCode, selfInfo.uin) - if (bot.role == GroupMemberRole.admin || bot.role == GroupMemberRole.owner) { - continue - } - for (const member of oldMembers) { - if (!newMembersSet.has(member.uin) && member.uin != selfInfo.uin) { - postOB11Event(new OB11GroupDecreaseEvent(parseInt(group.groupCode), parseInt(member.uin), parseInt(member.uin), "leave")); - break; - } - } - } + for (const member of newMembers) { + newMembersSet.add(member.uin); + } + // 判断bot是否是管理员,如果是管理员不需要从这里得知有人退群,这里的退群无法得知是主动退群还是被踢 + let bot = await getGroupMember(group.groupCode, selfInfo.uin) + if (bot.role == GroupMemberRole.admin || bot.role == GroupMemberRole.owner) { + continue + } + for (const member of oldMembers) { + if (!newMembersSet.has(member.uin) && member.uin != selfInfo.uin) { + postOB11Event(new OB11GroupDecreaseEvent(parseInt(group.groupCode), parseInt(member.uin), parseInt(member.uin), "leave")); + break; } + } } - updateGroups(newGroupList, false).then(); - } catch (e) { - updateGroups(payload.groupList).then(); - log("更新群信息错误", e.stack.toString()); + } } + + updateGroups(newGroupList, false).then(); + } catch (e) { + updateGroups(payload.groupList).then(); + log("更新群信息错误", e.stack.toString()); + } } // 群列表变动 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(); - } + // 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(); - } + // 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 + 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); - } + 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); } - // 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); - // } - // } - // } + } + // 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: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[] + data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[] }>(ReceiveCmdS.FRIENDS, payload => { - 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) - } - } + 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; + // 保存一下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 {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 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) - } + 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 + selfInfo.online = info.info.status !== 20 }) let activatedPeerUids: string[] = [] registerReceiveHook<{ - changedRecentContactLists: { - listType: number, sortedContactList: string[], - changedList: { - id: string, // peerUid - chatType: ChatType - }[] + 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() - } - } + 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 df01ef5..336c384 100644 --- a/src/ntqqapi/ntcall.ts +++ b/src/ntqqapi/ntcall.ts @@ -26,6 +26,7 @@ export enum NTQQApiMethod { ACTIVE_CHAT_HISTORY = "nodeIKernelMsgService/getMsgsIncludeSelfAndAddActiveChat", // 激活聊天窗口,有时候必须这样才能收到消息, 并返回历史消息 HISTORY_MSG = "nodeIKernelMsgService/getMsgsIncludeSelf", GET_MULTI_MSG = "nodeIKernelMsgService/getMultiMsg", + DELETE_ACTIVE_CHAT = "nodeIKernelMsgService/deleteActiveChatByUid", LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike", SELF_INFO = "fetchAuthData",