From 111bb4dd88c62bf33d9702321c3c924ae8188b9c Mon Sep 17 00:00:00 2001 From: idranme Date: Wed, 21 Aug 2024 22:14:52 +0800 Subject: [PATCH] fix: adaptation 27187 --- package.json | 1 + src/common/utils/EventTask.ts | 2 + src/main/main.ts | 15 ++++--- src/ntqqapi/api/file.ts | 3 +- src/ntqqapi/api/friend.ts | 12 +++++- src/ntqqapi/api/group.ts | 35 +++++++++++++--- src/ntqqapi/api/msg.ts | 50 ----------------------- src/ntqqapi/api/user.ts | 77 ++++++++++++++++++++++------------- src/ntqqapi/hook.ts | 24 +++++------ src/ntqqapi/ntcall.ts | 62 +++++++++++----------------- 10 files changed, 140 insertions(+), 141 deletions(-) diff --git a/package.json b/package.json index 8e3ac6c..7e73a1d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "compressing": "^1.10.1", "cordis": "^3.18.0", "cors": "^2.8.5", + "cosmokit": "^1.6.2", "express": "^4.19.2", "fast-xml-parser": "^4.4.1", "file-type": "^19.4.1", diff --git a/src/common/utils/EventTask.ts b/src/common/utils/EventTask.ts index bc32199..a7279fe 100644 --- a/src/common/utils/EventTask.ts +++ b/src/common/utils/EventTask.ts @@ -22,6 +22,7 @@ export class NTEventWrapper { private WrapperSession: NodeIQQNTWrapperSession | undefined//WrapperSession private ListenerManger: Map = new Map() //ListenerName-Unique -> Listener实例 private EventTask = new Map>>()//tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func} + public initialised = false constructor() { } @@ -46,6 +47,7 @@ export class NTEventWrapper { init({ ListenerMap, WrapperSession }: { ListenerMap: { [key: string]: typeof ListenerClassBase }, WrapperSession: NodeIQQNTWrapperSession }) { this.ListenerMap = ListenerMap this.WrapperSession = WrapperSession + this.initialised = true } createEventFunction any>(eventName: string): T | undefined { diff --git a/src/main/main.ts b/src/main/main.ts index 6d1aea1..72cfde0 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -14,7 +14,7 @@ import { CHANNEL_UPDATE, } from '../common/channels' import { ob11WebsocketServer } from '../onebot11/server/ws/WebsocketServer' -import { DATA_DIR, TEMP_DIR } from '../common/utils' +import { DATA_DIR, getBuildVersion, TEMP_DIR } from '../common/utils' import { llonebotError, setSelfInfo, @@ -341,7 +341,7 @@ function onLoad() { } }) - registerReceiveHook(ReceiveCmdS.FRIEND_REQUEST, async (payload) => { + /*registerReceiveHook(ReceiveCmdS.FRIEND_REQUEST, async (payload) => { for (const req of payload.data.buddyReqs) { if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) { continue @@ -362,7 +362,7 @@ function onLoad() { ) postOb11Event(friendRequestEvent) } - }) + })*/ } let startTime = 0 // 毫秒 @@ -380,7 +380,10 @@ function onLoad() { } llonebotError.otherError = '' startTime = Date.now() - NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession: getSession()! }) + const WrapperSession = getSession() + if (WrapperSession) { + NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession }) + } MessageUnique.init(uin) //log('start activate group member info') @@ -405,6 +408,8 @@ function onLoad() { log('LLOneBot start') } + const buildVersion = getBuildVersion() + const intervalId = setInterval(() => { const current = getSelfInfo() if (!current.uin) { @@ -414,7 +419,7 @@ function onLoad() { nick: current.uin, }) } - if (current.uin && getSession()) { + if (current.uin && (buildVersion >= 27187 || getSession())) { clearInterval(intervalId) start(current.uid, current.uin) } diff --git a/src/ntqqapi/api/file.ts b/src/ntqqapi/api/file.ts index e4d51c6..f1f50fc 100644 --- a/src/ntqqapi/api/file.ts +++ b/src/ntqqapi/api/file.ts @@ -25,6 +25,7 @@ import fsPromise from 'node:fs/promises' import { NTEventDispatch } from '@/common/utils/EventTask' import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners' import { NodeIKernelSearchService } from '@/ntqqapi/services' +import { Time } from 'cosmokit' export class NTQQFileApi { static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise { @@ -379,7 +380,7 @@ export class NTQQFileCacheApi { return callNTQQApi({ methodName: NTQQApiMethod.CACHE_SCAN, args: [null, null], - timeoutSecond: 300, + timeout: 300 * Time.second, }) } diff --git a/src/ntqqapi/api/friend.ts b/src/ntqqapi/api/friend.ts index fcab865..717c5c0 100644 --- a/src/ntqqapi/api/friend.ts +++ b/src/ntqqapi/api/friend.ts @@ -121,6 +121,16 @@ export class NTQQFriendApi { static async isBuddy(uid: string): Promise { const session = getSession() - return session?.getBuddyService().isBuddy(uid)! + if (session) { + return session.getBuddyService().isBuddy(uid) + } else { + return await callNTQQApi({ + methodName: 'nodeIKernelBuddyService/isBuddy', + args: [ + { uid }, + null, + ], + }) + } } } diff --git a/src/ntqqapi/api/group.ts b/src/ntqqapi/api/group.ts index 2e39155..866033a 100644 --- a/src/ntqqapi/api/group.ts +++ b/src/ntqqapi/api/group.ts @@ -71,11 +71,36 @@ export class NTQQGroupApi { static async getGroupMembers(groupQQ: string, num = 3000): Promise> { const session = getSession() - const groupService = session?.getGroupService() - const sceneId = groupService?.createMemberListScene(groupQQ, 'groupMemberList_MainWindow') - const result = await groupService?.getNextMemberList(sceneId!, undefined, num) - if (result?.errCode !== 0) { - throw ('获取群成员列表出错,' + result?.errMsg) + let result: Awaited> + if (session) { + const groupService = session.getGroupService() + const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow') + result = await groupService.getNextMemberList(sceneId, undefined, num) + } else { + const sceneId = await callNTQQApi({ + methodName: NTQQApiMethod.GROUP_MEMBER_SCENE, + args: [ + { + groupCode: groupQQ, + scene: 'groupMemberList_MainWindow', + }, + ], + }) + result = await callNTQQApi< + ReturnType + >({ + methodName: NTQQApiMethod.GROUP_MEMBERS, + args: [ + { + sceneId, + num, + }, + null, + ], + }) + } + if (result.errCode !== 0) { + throw ('获取群成员列表出错,' + result.errMsg) } return result.result.infos } diff --git a/src/ntqqapi/api/msg.ts b/src/ntqqapi/api/msg.ts index c581e50..9c8a185 100644 --- a/src/ntqqapi/api/msg.ts +++ b/src/ntqqapi/api/msg.ts @@ -139,56 +139,6 @@ export class NTQQMsgApi { return retMsg! } - static async sendMsgV2(peer: Peer, msgElements: SendMessageElement[], waitComplete = true, timeout = 10000) { - function generateMsgId() { - const timestamp = Math.floor(Date.now() / 1000) - const random = Math.floor(Math.random() * Math.pow(2, 32)) - const buffer = Buffer.alloc(8) - buffer.writeUInt32BE(timestamp, 0) - buffer.writeUInt32BE(random, 4) - const msgId = BigInt('0x' + buffer.toString('hex')).toString() - return msgId - } - // 此处有采用Hack方法 利用数据返回正确得到对应消息 - // 与之前 Peer队列 MsgSeq队列 真正的MsgId并发不同 - // 谨慎采用 目前测试暂无问题 Developer.Mlikiowa - let msgId: string - try { - msgId = await NTQQMsgApi.getMsgUnique(peer.chatType, await NTQQMsgApi.getServerTime()) - } catch (error) { - //if (!napCatCore.session.getMsgService()['generateMsgUniqueId']) - //兜底识别策略V2 - msgId = generateMsgId().toString() - } - let data = await NTEventDispatch.CallNormalEvent< - (msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map) => Promise, - (msgList: RawMessage[]) => void - >( - 'NodeIKernelMsgService/sendMsg', - 'NodeIKernelMsgListener/onMsgInfoListUpdate', - 1, - timeout, - (msgRecords: RawMessage[]) => { - for (let msgRecord of msgRecords) { - if (msgRecord.msgId === msgId && msgRecord.sendStatus === 2) { - return true - } - } - return false - }, - msgId, - peer, - msgElements, - new Map() - ) - const retMsg = data[1].find(msgRecord => { - if (msgRecord.msgId === msgId) { - return true - } - }) - return retMsg! - } - static async getMsgUnique(chatType: number, time: string) { const session = getSession() if (getBuildVersion() >= 26702) { diff --git a/src/ntqqapi/api/user.ts b/src/ntqqapi/api/user.ts index 1d6c6b5..8cb4ce4 100644 --- a/src/ntqqapi/api/user.ts +++ b/src/ntqqapi/api/user.ts @@ -1,5 +1,5 @@ import { callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod } from '../ntcall' -import { SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2 } from '../types' +import { SelfInfo, User, UserDetailInfoByUin, UserDetailInfoByUinV2, UserDetailInfoListenerArg } from '../types' import { ReceiveCmdS } from '../hook' import { friends, groupMembers, getSelfUin } from '@/common/data' import { CacheClassFuncAsync, log, getBuildVersion } from '@/common/utils' @@ -9,6 +9,7 @@ import { NodeIKernelProfileService, UserDetailSource, ProfileBizType } from '../ import { NodeIKernelProfileListener } from '../listeners' import { NTEventDispatch } from '@/common/utils/EventTask' import { NTQQFriendApi } from './friend' +import { Time } from 'cosmokit' export class NTQQUserApi { static async setQQAvatar(filePath: string) { @@ -20,7 +21,7 @@ export class NTQQUserApi { }, null, ], - timeoutSecond: 10, // 10秒不一定够 + timeout: 10 * Time.second, // 10秒不一定够 }) } @@ -28,7 +29,7 @@ export class NTQQUserApi { return await callNTQQApi({ className: NTQQApiClass.GLOBAL_DATA, methodName: NTQQApiMethod.SELF_INFO, - timeoutSecond: 2, + timeout: 2 * Time.second, }) } @@ -41,33 +42,53 @@ export class NTQQUserApi { return result.profiles.get(uid) } - /** 26702 */ static async fetchUserDetailInfo(uid: string) { - type EventService = NodeIKernelProfileService['fetchUserDetailInfo'] - type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'] - const [_retData, profile] = await NTEventDispatch.CallNormalEvent - - ( - 'NodeIKernelProfileService/fetchUserDetailInfo', - 'NodeIKernelProfileListener/onUserDetailInfoChanged', - 1, - 5000, - (profile) => profile.uid === uid, - 'BuddyProfileStore', - [uid], - UserDetailSource.KSERVER, - [ProfileBizType.KALL] - ) - const RetUser: User = { - ...profile.simpleInfo.coreInfo, - ...profile.simpleInfo.status, - ...profile.simpleInfo.vasInfo, - ...profile.commonExt, - ...profile.simpleInfo.baseInfo, - qqLevel: profile.commonExt.qqLevel, + let info: UserDetailInfoListenerArg + if (NTEventDispatch.initialised) { + type EventService = NodeIKernelProfileService['fetchUserDetailInfo'] + type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'] + const [_retData, profile] = await NTEventDispatch.CallNormalEvent + + ( + 'NodeIKernelProfileService/fetchUserDetailInfo', + 'NodeIKernelProfileListener/onUserDetailInfoChanged', + 1, + 5000, + (profile) => profile.uid === uid, + 'BuddyProfileStore', + [uid], + UserDetailSource.KSERVER, + [ProfileBizType.KALL] + ) + info = profile + } else { + const result = await callNTQQApi<{ info: UserDetailInfoListenerArg }>({ + methodName: 'nodeIKernelProfileService/fetchUserDetailInfo', + cbCmd: 'nodeIKernelProfileListener/onUserDetailInfoChanged', + afterFirstCmd: false, + cmdCB: payload => payload.info.uid === uid, + args: [ + { + callFrom: 'BuddyProfileStore', + uid: [uid], + source: UserDetailSource.KSERVER, + bizList: [ProfileBizType.KALL] + }, + null + ], + }) + info = result.info + } + const ret: User = { + ...info.simpleInfo.coreInfo, + ...info.simpleInfo.status, + ...info.simpleInfo.vasInfo, + ...info.commonExt, + ...info.simpleInfo.baseInfo, + qqLevel: info.commonExt?.qqLevel, pendantId: '' } - return RetUser + return ret } static async getUserDetailInfo(uid: string, getLevel = false, withBizInfo = true) { @@ -115,7 +136,7 @@ export class NTQQUserApi { } return cookies } - + static async getSkey(): Promise { const clientKeyData = await NTQQUserApi.getClientKey() if (clientKeyData.result !== 0) { diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index cef1672..494b3db 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -77,15 +77,17 @@ let callHooks: Array<{ hookFunc: (callParams: unknown[]) => void | Promise }> = [] +const logHook = false + export function hookNTQQApiReceive(window: BrowserWindow) { const originalSend = window.webContents.send const patchSend = (channel: string, ...args: NTQQApiReturnData) => { - /*try { + try { const isLogger = args[0]?.eventName?.startsWith('ns-LoggerApi') - if (!isLogger) { + if (logHook && !isLogger) { log(`received ntqq api message: ${channel}`, args) } - } catch { }*/ + } catch { } if (args?.[1] instanceof Array) { for (const receiveData of args?.[1]) { const ntQQApiMethodName = receiveData.cmdName @@ -134,9 +136,9 @@ export function hookNTQQApiCall(window: BrowserWindow) { isLogger = args[3][0].eventName.startsWith('ns-LoggerApi') } catch (e) { } if (!isLogger) { - /*try { - HOOK_LOG && log('call NTQQ api', thisArg, args) - } catch (e) { }*/ + try { + logHook && log('call NTQQ api', thisArg, args) + } catch (e) { } try { const _args: unknown[] = args[3][1] const cmdName: NTQQApiMethod = _args[0] as NTQQApiMethod @@ -145,13 +147,11 @@ export function hookNTQQApiCall(window: BrowserWindow) { 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) { + hook.hookFunc(callParams) + } catch (e: any) { log('hook call error', e, _args) } + resolve(undefined) }).then() } }) @@ -399,7 +399,7 @@ export async function startHook() { friendList.push(...fData.buddyList) } } - log('好友列表变动', friendList) + log('好友列表变动', friendList.length) for (let friend of friendList) { NTQQMsgApi.activateChat({ peerUid: friend.uid, chatType: ChatType.friend }).then() let existFriend = friends.find((f) => f.uin == friend.uin) diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts index 9fec4d2..e172c57 100644 --- a/src/ntqqapi/ntcall.ts +++ b/src/ntqqapi/ntcall.ts @@ -1,7 +1,8 @@ import { ipcMain } from 'electron' -import { hookApiCallbacks, ReceiveCmd, ReceiveCmdS, registerReceiveHook, removeReceiveHook } from './hook' +import { hookApiCallbacks, ReceiveCmd, registerReceiveHook, removeReceiveHook } from './hook' import { log } from '../common/utils/log' import { randomUUID } from 'node:crypto' +import { GeneralCallResult } from './services/common' export enum NTQQApiClass { NT_API = 'ns-ntApi', @@ -108,38 +109,24 @@ interface NTQQApiParams { cbCmd?: ReceiveCmd | ReceiveCmd[] | null cmdCB?: (payload: any) => boolean afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd - timeoutSecond?: number + timeout?: number } export function callNTQQApi(params: NTQQApiParams) { - let { - className, - methodName, - channel, - args, - cbCmd, - timeoutSecond: timeout, - classNameIsRegister, - cmdCB, - afterFirstCmd, - } = params - className = className ?? NTQQApiClass.NT_API - channel = channel ?? NTQQApiChannel.IPC_UP_2 - args = args ?? [] - timeout = timeout ?? 5 - afterFirstCmd = afterFirstCmd ?? true + const className = params.className ?? NTQQApiClass.NT_API + const channel = params.channel ?? NTQQApiChannel.IPC_UP_2 + const timeout = params.timeout ?? 5000 + const afterFirstCmd = params.afterFirstCmd ?? true const uuid = randomUUID() - //HOOK_LOG && log('callNTQQApi', channel, className, methodName, args, uuid) + let eventName = className + '-' + channel[channel.length - 1] + if (params.classNameIsRegister) { + eventName += '-register' + } + const apiArgs = [params.methodName, ...(params.args ?? [])] + //log('callNTQQApi', channel, eventName, apiArgs, uuid) return new Promise((resolve: (data: ReturnType) => void, reject) => { - // log("callNTQQApiPromise", channel, className, methodName, args, uuid) - const _timeout = timeout * 1000 let success = false - let eventName = className + '-' + channel[channel.length - 1] - if (classNameIsRegister) { - eventName += '-register' - } - const apiArgs = [methodName, ...args] - if (!cbCmd) { + if (!params.cbCmd) { // QQ后端会返回结果,并且可以根据uuid识别 hookApiCallbacks[uuid] = (r: ReturnType) => { success = true @@ -149,10 +136,10 @@ export function callNTQQApi(params: NTQQApiParams) { else { // 这里的callback比较特殊,QQ后端先返回是否调用成功,再返回一条结果数据 const secondCallback = () => { - const hookId = registerReceiveHook(cbCmd, (payload) => { + const hookId = registerReceiveHook(params.cbCmd!, (payload) => { // log(methodName, "second callback", cbCmd, payload, cmdCB); - if (!!cmdCB) { - if (cmdCB(payload)) { + if (!!params.cmdCB) { + if (params.cmdCB(payload)) { removeReceiveHook(hookId) success = true resolve(payload) @@ -167,8 +154,8 @@ export function callNTQQApi(params: NTQQApiParams) { } !afterFirstCmd && secondCallback() hookApiCallbacks[uuid] = (result: GeneralCallResult) => { - log(`${methodName} callback`, result) - if (result?.result == 0 || result === undefined) { + log(`${params.methodName} callback`, result) + if (result?.result === 0 || result === undefined) { afterFirstCmd && secondCallback() } else { @@ -180,10 +167,10 @@ export function callNTQQApi(params: NTQQApiParams) { setTimeout(() => { // log("ntqq api timeout", success, channel, className, methodName) if (!success) { - log(`ntqq api timeout ${channel}, ${eventName}, ${methodName}`, apiArgs) - reject(`ntqq api timeout ${channel}, ${eventName}, ${methodName}, ${apiArgs}`) + log(`ntqq api timeout ${channel}, ${eventName}, ${params.methodName}`, apiArgs) + reject(`ntqq api timeout ${channel}, ${eventName}, ${params.methodName}, ${apiArgs}`) } - }, _timeout) + }, timeout) ipcMain.emit( channel, @@ -199,7 +186,4 @@ export function callNTQQApi(params: NTQQApiParams) { }) } -export interface GeneralCallResult { - result: number // 0: success - errMsg: string -} \ No newline at end of file +export { GeneralCallResult } \ No newline at end of file