fix: 手动频繁切换聊天窗口时导致旧的窗口接收不到消息

This commit is contained in:
linyuchen 2024-04-07 17:37:52 +08:00
parent 959eab441e
commit 81821e74d8
2 changed files with 414 additions and 356 deletions

View File

@ -1,8 +1,8 @@
import {BrowserWindow} from 'electron'; import {BrowserWindow} from 'electron';
import {NTQQApiClass} from "./ntcall"; import {NTQQApiClass, NTQQApiMethod} from "./ntcall";
import {NTQQMsgApi, sendMessagePool} from "./api/msg" import {NTQQMsgApi, sendMessagePool} from "./api/msg"
import {ChatType, Group, GroupMember, GroupMemberRole, RawMessage, User} from "./types"; 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 {OB11GroupDecreaseEvent} from "../onebot11/event/notice/OB11GroupDecreaseEvent";
import {v4 as uuidv4} from "uuid" import {v4 as uuidv4} from "uuid"
import {postOB11Event} from "../onebot11/server/postOB11Event"; import {postOB11Event} from "../onebot11/server/postOB11Event";
@ -11,446 +11,503 @@ import fs from "fs";
import {dbUtil} from "../common/db"; import {dbUtil} from "../common/db";
import {NTQQGroupApi} from "./api/group"; import {NTQQGroupApi} from "./api/group";
import {log} from "../common/utils/log"; import {log} from "../common/utils/log";
import {sleep} from "../common/utils/helper"; import {isNumeric, sleep} from "../common/utils/helper";
import {OB11Constructor} from "../onebot11/constructor"; import {OB11Constructor} from "../onebot11/constructor";
export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {} export let hookApiCallbacks: Record<string, (apiReturn: any) => void> = {}
export let ReceiveCmdS = { export let ReceiveCmdS = {
RECENT_CONTACT: "nodeIKernelRecentContactListener/onRecentContactListChangedVer2", RECENT_CONTACT: "nodeIKernelRecentContactListener/onRecentContactListChangedVer2",
UPDATE_MSG: "nodeIKernelMsgListener/onMsgInfoListUpdate", UPDATE_MSG: "nodeIKernelMsgListener/onMsgInfoListUpdate",
UPDATE_ACTIVE_MSG: "nodeIKernelMsgListener/onActiveMsgInfoUpdate", UPDATE_ACTIVE_MSG: "nodeIKernelMsgListener/onActiveMsgInfoUpdate",
NEW_MSG: `nodeIKernelMsgListener/onRecvMsg`, NEW_MSG: `nodeIKernelMsgListener/onRecvMsg`,
NEW_ACTIVE_MSG: `nodeIKernelMsgListener/onRecvActiveMsg`, NEW_ACTIVE_MSG: `nodeIKernelMsgListener/onRecvActiveMsg`,
SELF_SEND_MSG: "nodeIKernelMsgListener/onAddSendMsg", SELF_SEND_MSG: "nodeIKernelMsgListener/onAddSendMsg",
USER_INFO: "nodeIKernelProfileListener/onProfileSimpleChanged", USER_INFO: "nodeIKernelProfileListener/onProfileSimpleChanged",
USER_DETAIL_INFO: "nodeIKernelProfileListener/onProfileDetailInfoChanged", USER_DETAIL_INFO: "nodeIKernelProfileListener/onProfileDetailInfoChanged",
GROUPS: "nodeIKernelGroupListener/onGroupListUpdate", GROUPS: "nodeIKernelGroupListener/onGroupListUpdate",
GROUPS_STORE: "onGroupListUpdate", GROUPS_STORE: "onGroupListUpdate",
GROUP_MEMBER_INFO_UPDATE: "nodeIKernelGroupListener/onMemberInfoChange", GROUP_MEMBER_INFO_UPDATE: "nodeIKernelGroupListener/onMemberInfoChange",
FRIENDS: "onBuddyListChange", FRIENDS: "onBuddyListChange",
MEDIA_DOWNLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaDownloadComplete", MEDIA_DOWNLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaDownloadComplete",
UNREAD_GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated", UNREAD_GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupNotifiesUnreadCountUpdated",
GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupSingleScreenNotifies", GROUP_NOTIFY: "nodeIKernelGroupListener/onGroupSingleScreenNotifies",
FRIEND_REQUEST: "nodeIKernelBuddyListener/onBuddyReqChange", FRIEND_REQUEST: "nodeIKernelBuddyListener/onBuddyReqChange",
SELF_STATUS: 'nodeIKernelProfileListener/onSelfStatusChanged', SELF_STATUS: 'nodeIKernelProfileListener/onSelfStatusChanged',
CACHE_SCAN_FINISH: "nodeIKernelStorageCleanListener/onFinishScan", CACHE_SCAN_FINISH: "nodeIKernelStorageCleanListener/onFinishScan",
MEDIA_UPLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaUploadComplete", MEDIA_UPLOAD_COMPLETE: "nodeIKernelMsgListener/onRichMediaUploadComplete",
SKEY_UPDATE: "onSkeyUpdate" SKEY_UPDATE: "onSkeyUpdate"
} }
export type ReceiveCmd = typeof ReceiveCmdS[keyof typeof ReceiveCmdS] export type ReceiveCmd = typeof ReceiveCmdS[keyof typeof ReceiveCmdS]
interface NTQQApiReturnData<PayloadType = unknown> extends Array<any> { interface NTQQApiReturnData<PayloadType = unknown> extends Array<any> {
0: { 0: {
"type": "request", "type": "request",
"eventName": NTQQApiClass, "eventName": NTQQApiClass,
"callbackId"?: string "callbackId"?: string
}, },
1: 1:
{ {
cmdName: ReceiveCmd, cmdName: ReceiveCmd,
cmdType: "event", cmdType: "event",
payload: PayloadType payload: PayloadType
}[] }[]
} }
let receiveHooks: Array<{ let receiveHooks: Array<{
method: ReceiveCmd[], method: ReceiveCmd[],
hookFunc: ((payload: any) => void | Promise<void>) hookFunc: ((payload: any) => void | Promise<void>)
id: string id: string
}> = [] }> = []
export function hookNTQQApiReceive(window: BrowserWindow) { let callHooks: Array<{
const originalSend = window.webContents.send; method: NTQQApiMethod[],
const patchSend = (channel: string, ...args: NTQQApiReturnData) => { hookFunc: ((callParams: unknown[]) => void | Promise<void>)
// console.log("hookNTQQApiReceive", channel, args) }> = []
let isLogger = false
try {
isLogger = args[0]?.eventName?.startsWith("ns-LoggerApi") export function hookNTQQApiReceive(window: BrowserWindow) {
} catch (e) { 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<void>).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<void>).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) { export function hookNTQQApiCall(window: BrowserWindow) {
// 监听调用NTQQApi // 监听调用NTQQApi
let webContents = window.webContents as any; let webContents = window.webContents as any;
const ipc_message_proxy = webContents._events["-ipc-message"]?.[0] || webContents._events["-ipc-message"]; const ipc_message_proxy = webContents._events["-ipc-message"]?.[0] || webContents._events["-ipc-message"];
const proxyIpcMsg = new Proxy(ipc_message_proxy, { const proxyIpcMsg = new Proxy(ipc_message_proxy, {
apply(target, thisArg, args) { apply(target, thisArg, args) {
// console.log(thisArg, args); // console.log(thisArg, args);
let isLogger = false let isLogger = false
try { try {
isLogger = args[3][0].eventName.startsWith("ns-LoggerApi") isLogger = args[3][0].eventName.startsWith("ns-LoggerApi")
} catch (e) { } catch (e) {
} }
if (!isLogger) { if (!isLogger) {
try { try {
HOOK_LOG && log("call NTQQ api", thisArg, args); HOOK_LOG && log("call NTQQ api", thisArg, args);
} catch (e) { } 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;
} }
}); try {
if (webContents._events["-ipc-invoke"]?.[0]) { const _args: unknown[] = args[3][1];
webContents._events["-ipc-invoke"][0] = proxyIpcInvoke; const cmdName: NTQQApiMethod = _args[0] as NTQQApiMethod;
} else { const callParams = _args.slice(1);
webContents._events["-ipc-invoke"] = proxyIpcInvoke; 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<void>).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<PayloadType>(method: ReceiveCmd | ReceiveCmd[], hookFunc: (payload: PayloadType) => void): string { export function registerReceiveHook<PayloadType>(method: ReceiveCmd | ReceiveCmd[], hookFunc: (payload: PayloadType) => void): string {
const id = uuidv4() const id = uuidv4()
if (!Array.isArray(method)) { if (!Array.isArray(method)) {
method = [method] method = [method]
} }
receiveHooks.push({ receiveHooks.push({
method, method,
hookFunc, hookFunc,
id id
}) })
return id; return id;
}
export function registerCallHook(method: NTQQApiMethod | NTQQApiMethod[], hookFunc: (callParams: unknown[]) => void | Promise<void>): void {
if (!Array.isArray(method)) {
method = [method]
}
callHooks.push({
method,
hookFunc
})
} }
export function removeReceiveHook(id: string) { export function removeReceiveHook(id: string) {
const index = receiveHooks.findIndex(h => h.id === id) const index = receiveHooks.findIndex(h => h.id === id)
receiveHooks.splice(index, 1); receiveHooks.splice(index, 1);
} }
let activatedGroups: string[] = []; let activatedGroups: string[] = [];
async function updateGroups(_groups: Group[], needUpdate: boolean = true) { async function updateGroups(_groups: Group[], needUpdate: boolean = true) {
for (let group of _groups) { for (let group of _groups) {
log("update group", group) log("update group", group)
// if (!activatedGroups.includes(group.groupCode)) { // if (!activatedGroups.includes(group.groupCode)) {
NTQQMsgApi.activateChat({peerUid: group.groupCode, chatType: ChatType.group}).then((r) => { NTQQMsgApi.activateChat({peerUid: group.groupCode, chatType: ChatType.group}).then((r) => {
// activatedGroups.push(group.groupCode); // activatedGroups.push(group.groupCode);
// log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r) // log(`激活群聊天窗口${group.groupName}(${group.groupCode})`, r)
// if (r.result !== 0) { // if (r.result !== 0) {
// setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500); // setTimeout(() => NTQQMsgApi.activateGroupChat(group.groupCode).then(r => log(`再次激活群聊天窗口${group.groupName}(${group.groupCode})`, r)), 500);
// }else { // }else {
// } // }
}).catch(log) }).catch(log)
// } // }
let existGroup = groups.find(g => g.groupCode == group.groupCode); let existGroup = groups.find(g => g.groupCode == group.groupCode);
if (existGroup) { if (existGroup) {
Object.assign(existGroup, group); Object.assign(existGroup, group);
} else { } else {
groups.push(group); groups.push(group);
existGroup = group; existGroup = group;
}
if (needUpdate) {
const members = await NTQQGroupApi.getGroupMembers(group.groupCode);
if (members) {
existGroup.members = members;
}
}
} }
if (needUpdate) {
const members = await NTQQGroupApi.getGroupMembers(group.groupCode);
if (members) {
existGroup.members = members;
}
}
}
} }
async function processGroupEvent(payload: { groupList: Group[] }) { async function processGroupEvent(payload: { groupList: Group[] }) {
try { try {
const newGroupList = payload.groupList; const newGroupList = payload.groupList;
for (const group of newGroupList) { for (const group of newGroupList) {
let existGroup = groups.find(g => g.groupCode == group.groupCode); let existGroup = groups.find(g => g.groupCode == group.groupCode);
if (existGroup) { if (existGroup) {
if (existGroup.memberCount > group.memberCount) { if (existGroup.memberCount > group.memberCount) {
log(`群(${group.groupCode})成员数量减少${existGroup.memberCount} -> ${group.memberCount}`); log(`群(${group.groupCode})成员数量减少${existGroup.memberCount} -> ${group.memberCount}`);
const oldMembers = existGroup.members; const oldMembers = existGroup.members;
await sleep(200); // 如果请求QQ API的速度过快通常无法正确拉取到最新的群信息因此这里人为引入一个延时 await sleep(200); // 如果请求QQ API的速度过快通常无法正确拉取到最新的群信息因此这里人为引入一个延时
const newMembers = await NTQQGroupApi.getGroupMembers(group.groupCode); const newMembers = await NTQQGroupApi.getGroupMembers(group.groupCode);
group.members = newMembers; group.members = newMembers;
const newMembersSet = new Set<string>(); // 建立索引降低时间复杂度 const newMembersSet = new Set<string>(); // 建立索引降低时间复杂度
for (const member of newMembers) { for (const member of newMembers) {
newMembersSet.add(member.uin); 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;
}
}
}
// 判断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) => { registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS, (payload) => {
// updateType 3是群列表变动2是群成员变动 // updateType 3是群列表变动2是群成员变动
// log("群列表变动", payload.updateType, payload.groupList) // log("群列表变动", payload.updateType, payload.groupList)
if (payload.updateType != 2) { if (payload.updateType != 2) {
updateGroups(payload.groupList).then(); updateGroups(payload.groupList).then();
} else { } else {
if (process.platform == "win32") { if (process.platform == "win32") {
processGroupEvent(payload).then(); processGroupEvent(payload).then();
}
} }
}
}) })
registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS_STORE, (payload) => { registerReceiveHook<{ groupList: Group[], updateType: number }>(ReceiveCmdS.GROUPS_STORE, (payload) => {
// updateType 3是群列表变动2是群成员变动 // updateType 3是群列表变动2是群成员变动
// log("群列表变动", payload.updateType, payload.groupList) // log("群列表变动", payload.updateType, payload.groupList)
if (payload.updateType != 2) { if (payload.updateType != 2) {
updateGroups(payload.groupList).then(); updateGroups(payload.groupList).then();
} else { } else {
if (process.platform != "win32") { if (process.platform != "win32") {
processGroupEvent(payload).then(); processGroupEvent(payload).then();
}
} }
}
}) })
registerReceiveHook<{ registerReceiveHook<{
groupCode: string, groupCode: string,
dataSource: number, dataSource: number,
members: Set<GroupMember> members: Set<GroupMember>
}>(ReceiveCmdS.GROUP_MEMBER_INFO_UPDATE, async (payload) => { }>(ReceiveCmdS.GROUP_MEMBER_INFO_UPDATE, async (payload) => {
const groupCode = payload.groupCode; const groupCode = payload.groupCode;
const members = Array.from(payload.members.values()); const members = Array.from(payload.members.values());
// log("群成员信息变动", groupCode, members) // log("群成员信息变动", groupCode, members)
for (const member of members) { for (const member of members) {
const existMember = await getGroupMember(groupCode, member.uin); const existMember = await getGroupMember(groupCode, member.uin);
if (existMember) { if (existMember) {
Object.assign(existMember, member); Object.assign(existMember, member);
}
} }
// const existGroup = groups.find(g => g.groupCode == groupCode); }
// if (existGroup) { // const existGroup = groups.find(g => g.groupCode == groupCode);
// log("对比群成员", existGroup.members, members) // if (existGroup) {
// for (const member of members) { // log("对比群成员", existGroup.members, members)
// const existMember = existGroup.members.find(m => m.uin == member.uin); // for (const member of members) {
// if (existMember) { // const existMember = existGroup.members.find(m => m.uin == member.uin);
// log("对比群名片", existMember.cardName, member.cardName) // if (existMember) {
// if (existMember.cardName != member.cardName) { // log("对比群名片", existMember.cardName, member.cardName)
// postOB11Event(new OB11GroupCardEvent(parseInt(existGroup.groupCode), parseInt(member.uin), member.cardName, existMember.cardName)); // if (existMember.cardName != member.cardName) {
// } // postOB11Event(new OB11GroupCardEvent(parseInt(existGroup.groupCode), parseInt(member.uin), member.cardName, existMember.cardName));
// Object.assign(existMember, member); // }
// } // Object.assign(existMember, member);
// } // }
// } // }
// }
}) })
// 好友列表变动 // 好友列表变动
registerReceiveHook<{ registerReceiveHook<{
data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[] data: { categoryId: number, categroyName: string, categroyMbCount: number, buddyList: User[] }[]
}>(ReceiveCmdS.FRIENDS, payload => { }>(ReceiveCmdS.FRIENDS, payload => {
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 { } else {
Object.assign(existFriend, friend) 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; 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;
// 自动清理新消息文件
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)
} }
// 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}) => { registerReceiveHook<{ msgRecord: RawMessage }>(ReceiveCmdS.SELF_SEND_MSG, ({msgRecord}) => {
const message = msgRecord; const message = msgRecord;
const peerUid = message.peerUid; const peerUid = message.peerUid;
// log("收到自己发送成功的消息", Object.keys(sendMessagePool), message); // log("收到自己发送成功的消息", Object.keys(sendMessagePool), message);
// log("收到自己发送成功的消息", message.msgId, message.msgSeq); // log("收到自己发送成功的消息", message.msgId, message.msgSeq);
dbUtil.addMsg(message).then() dbUtil.addMsg(message).then()
const sendCallback = sendMessagePool[peerUid] const sendCallback = sendMessagePool[peerUid]
if (sendCallback) { if (sendCallback) {
try { try {
sendCallback(message); sendCallback(message);
} catch (e) { } catch (e) {
log("receive self msg error", e.stack) log("receive self msg error", e.stack)
}
} }
}
}) })
registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => { registerReceiveHook<{ info: { status: number } }>(ReceiveCmdS.SELF_STATUS, (info) => {
selfInfo.online = info.info.status !== 20 selfInfo.online = info.info.status !== 20
}) })
let activatedPeerUids: string[] = [] let activatedPeerUids: string[] = []
registerReceiveHook<{ registerReceiveHook<{
changedRecentContactLists: { changedRecentContactLists: {
listType: number, sortedContactList: string[], listType: number, sortedContactList: string[],
changedList: { changedList: {
id: string, // peerUid id: string, // peerUid
chatType: ChatType chatType: ChatType
}[]
}[] }[]
}[]
}>(ReceiveCmdS.RECENT_CONTACT, async (payload) => { }>(ReceiveCmdS.RECENT_CONTACT, async (payload) => {
for (const recentContact of payload.changedRecentContactLists) { for (const recentContact of payload.changedRecentContactLists) {
for (const changedContact of recentContact.changedList) { for (const changedContact of recentContact.changedList) {
if (activatedPeerUids.includes(changedContact.id)) continue; if (activatedPeerUids.includes(changedContact.id)) continue;
activatedPeerUids.push(changedContact.id) activatedPeerUids.push(changedContact.id)
const peer = {peerUid: changedContact.id, chatType: changedContact.chatType} const peer = {peerUid: changedContact.id, chatType: changedContact.chatType}
if (changedContact.chatType === ChatType.temp) { if (changedContact.chatType === ChatType.temp) {
log("收到临时会话消息", peer) log("收到临时会话消息", peer)
NTQQMsgApi.activateChatAndGetHistory(peer).then( NTQQMsgApi.activateChatAndGetHistory(peer).then(
() => { () => {
NTQQMsgApi.getMsgHistory(peer, "", 20).then(({msgList}) => { NTQQMsgApi.getMsgHistory(peer, "", 20).then(({msgList}) => {
let lastTempMsg = msgList.pop() let lastTempMsg = msgList.pop()
log("激活窗口之前的第一条临时会话消息:", lastTempMsg) log("激活窗口之前的第一条临时会话消息:", lastTempMsg)
if ((Date.now() / 1000) - parseInt(lastTempMsg.msgTime) < 5) { if ((Date.now() / 1000) - parseInt(lastTempMsg.msgTime) < 5) {
OB11Constructor.message(lastTempMsg).then(r => postOB11Event(r)) OB11Constructor.message(lastTempMsg).then(r => postOB11Event(r))
} }
}) })
} }
) )
} else { } else {
NTQQMsgApi.activateChat(peer).then() 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

@ -26,6 +26,7 @@ export enum NTQQApiMethod {
ACTIVE_CHAT_HISTORY = "nodeIKernelMsgService/getMsgsIncludeSelfAndAddActiveChat", // 激活聊天窗口,有时候必须这样才能收到消息, 并返回历史消息 ACTIVE_CHAT_HISTORY = "nodeIKernelMsgService/getMsgsIncludeSelfAndAddActiveChat", // 激活聊天窗口,有时候必须这样才能收到消息, 并返回历史消息
HISTORY_MSG = "nodeIKernelMsgService/getMsgsIncludeSelf", HISTORY_MSG = "nodeIKernelMsgService/getMsgsIncludeSelf",
GET_MULTI_MSG = "nodeIKernelMsgService/getMultiMsg", GET_MULTI_MSG = "nodeIKernelMsgService/getMultiMsg",
DELETE_ACTIVE_CHAT = "nodeIKernelMsgService/deleteActiveChatByUid",
LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike", LIKE_FRIEND = "nodeIKernelProfileLikeService/setBuddyProfileLike",
SELF_INFO = "fetchAuthData", SELF_INFO = "fetchAuthData",