fix: adaptation 27187

This commit is contained in:
idranme 2024-08-21 22:14:52 +08:00
parent f8bf60a3a0
commit 111bb4dd88
No known key found for this signature in database
GPG Key ID: 926F7B5B668E495F
10 changed files with 140 additions and 141 deletions

View File

@ -20,6 +20,7 @@
"compressing": "^1.10.1", "compressing": "^1.10.1",
"cordis": "^3.18.0", "cordis": "^3.18.0",
"cors": "^2.8.5", "cors": "^2.8.5",
"cosmokit": "^1.6.2",
"express": "^4.19.2", "express": "^4.19.2",
"fast-xml-parser": "^4.4.1", "fast-xml-parser": "^4.4.1",
"file-type": "^19.4.1", "file-type": "^19.4.1",

View File

@ -22,6 +22,7 @@ export class NTEventWrapper {
private WrapperSession: NodeIQQNTWrapperSession | undefined//WrapperSession private WrapperSession: NodeIQQNTWrapperSession | undefined//WrapperSession
private ListenerManger: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>() //ListenerName-Unique -> Listener实例 private ListenerManger: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>() //ListenerName-Unique -> Listener实例
private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>()//tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func} private EventTask = new Map<string, Map<string, Map<string, Internal_MapKey>>>()//tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
public initialised = false
constructor() { constructor() {
} }
@ -46,6 +47,7 @@ export class NTEventWrapper {
init({ ListenerMap, WrapperSession }: { ListenerMap: { [key: string]: typeof ListenerClassBase }, WrapperSession: NodeIQQNTWrapperSession }) { init({ ListenerMap, WrapperSession }: { ListenerMap: { [key: string]: typeof ListenerClassBase }, WrapperSession: NodeIQQNTWrapperSession }) {
this.ListenerMap = ListenerMap this.ListenerMap = ListenerMap
this.WrapperSession = WrapperSession this.WrapperSession = WrapperSession
this.initialised = true
} }
createEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined { createEventFunction<T extends (...args: any) => any>(eventName: string): T | undefined {

View File

@ -14,7 +14,7 @@ import {
CHANNEL_UPDATE, CHANNEL_UPDATE,
} from '../common/channels' } from '../common/channels'
import { ob11WebsocketServer } from '../onebot11/server/ws/WebsocketServer' 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 { import {
llonebotError, llonebotError,
setSelfInfo, setSelfInfo,
@ -341,7 +341,7 @@ function onLoad() {
} }
}) })
registerReceiveHook<FriendRequestNotify>(ReceiveCmdS.FRIEND_REQUEST, async (payload) => { /*registerReceiveHook<FriendRequestNotify>(ReceiveCmdS.FRIEND_REQUEST, async (payload) => {
for (const req of payload.data.buddyReqs) { for (const req of payload.data.buddyReqs) {
if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) { if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) {
continue continue
@ -362,7 +362,7 @@ function onLoad() {
) )
postOb11Event(friendRequestEvent) postOb11Event(friendRequestEvent)
} }
}) })*/
} }
let startTime = 0 // 毫秒 let startTime = 0 // 毫秒
@ -380,7 +380,10 @@ function onLoad() {
} }
llonebotError.otherError = '' llonebotError.otherError = ''
startTime = Date.now() startTime = Date.now()
NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession: getSession()! }) const WrapperSession = getSession()
if (WrapperSession) {
NTEventDispatch.init({ ListenerMap: wrapperConstructor, WrapperSession })
}
MessageUnique.init(uin) MessageUnique.init(uin)
//log('start activate group member info') //log('start activate group member info')
@ -405,6 +408,8 @@ function onLoad() {
log('LLOneBot start') log('LLOneBot start')
} }
const buildVersion = getBuildVersion()
const intervalId = setInterval(() => { const intervalId = setInterval(() => {
const current = getSelfInfo() const current = getSelfInfo()
if (!current.uin) { if (!current.uin) {
@ -414,7 +419,7 @@ function onLoad() {
nick: current.uin, nick: current.uin,
}) })
} }
if (current.uin && getSession()) { if (current.uin && (buildVersion >= 27187 || getSession())) {
clearInterval(intervalId) clearInterval(intervalId)
start(current.uid, current.uin) start(current.uid, current.uin)
} }

View File

@ -25,6 +25,7 @@ import fsPromise from 'node:fs/promises'
import { NTEventDispatch } from '@/common/utils/EventTask' import { NTEventDispatch } from '@/common/utils/EventTask'
import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners' import { OnRichMediaDownloadCompleteParams } from '@/ntqqapi/listeners'
import { NodeIKernelSearchService } from '@/ntqqapi/services' import { NodeIKernelSearchService } from '@/ntqqapi/services'
import { Time } from 'cosmokit'
export class NTQQFileApi { export class NTQQFileApi {
static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise<string> { static async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise<string> {
@ -379,7 +380,7 @@ export class NTQQFileCacheApi {
return callNTQQApi<CacheScanResult>({ return callNTQQApi<CacheScanResult>({
methodName: NTQQApiMethod.CACHE_SCAN, methodName: NTQQApiMethod.CACHE_SCAN,
args: [null, null], args: [null, null],
timeoutSecond: 300, timeout: 300 * Time.second,
}) })
} }

View File

@ -121,6 +121,16 @@ export class NTQQFriendApi {
static async isBuddy(uid: string): Promise<boolean> { static async isBuddy(uid: string): Promise<boolean> {
const session = getSession() const session = getSession()
return session?.getBuddyService().isBuddy(uid)! if (session) {
return session.getBuddyService().isBuddy(uid)
} else {
return await callNTQQApi<boolean>({
methodName: 'nodeIKernelBuddyService/isBuddy',
args: [
{ uid },
null,
],
})
}
} }
} }

View File

@ -71,11 +71,36 @@ export class NTQQGroupApi {
static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> { static async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const session = getSession() const session = getSession()
const groupService = session?.getGroupService() let result: Awaited<ReturnType<NodeIKernelGroupService['getNextMemberList']>>
const sceneId = groupService?.createMemberListScene(groupQQ, 'groupMemberList_MainWindow') if (session) {
const result = await groupService?.getNextMemberList(sceneId!, undefined, num) const groupService = session.getGroupService()
if (result?.errCode !== 0) { const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow')
throw ('获取群成员列表出错,' + result?.errMsg) result = await groupService.getNextMemberList(sceneId, undefined, num)
} else {
const sceneId = await callNTQQApi<string>({
methodName: NTQQApiMethod.GROUP_MEMBER_SCENE,
args: [
{
groupCode: groupQQ,
scene: 'groupMemberList_MainWindow',
},
],
})
result = await callNTQQApi<
ReturnType<NodeIKernelGroupService['getNextMemberList']>
>({
methodName: NTQQApiMethod.GROUP_MEMBERS,
args: [
{
sceneId,
num,
},
null,
],
})
}
if (result.errCode !== 0) {
throw ('获取群成员列表出错,' + result.errMsg)
} }
return result.result.infos return result.result.infos
} }

View File

@ -139,56 +139,6 @@ export class NTQQMsgApi {
return retMsg! 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<any, any>) => Promise<unknown>,
(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) { static async getMsgUnique(chatType: number, time: string) {
const session = getSession() const session = getSession()
if (getBuildVersion() >= 26702) { if (getBuildVersion() >= 26702) {

View File

@ -1,5 +1,5 @@
import { callNTQQApi, GeneralCallResult, NTQQApiClass, NTQQApiMethod } from '../ntcall' 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 { ReceiveCmdS } from '../hook'
import { friends, groupMembers, getSelfUin } from '@/common/data' import { friends, groupMembers, getSelfUin } from '@/common/data'
import { CacheClassFuncAsync, log, getBuildVersion } from '@/common/utils' import { CacheClassFuncAsync, log, getBuildVersion } from '@/common/utils'
@ -9,6 +9,7 @@ import { NodeIKernelProfileService, UserDetailSource, ProfileBizType } from '../
import { NodeIKernelProfileListener } from '../listeners' import { NodeIKernelProfileListener } from '../listeners'
import { NTEventDispatch } from '@/common/utils/EventTask' import { NTEventDispatch } from '@/common/utils/EventTask'
import { NTQQFriendApi } from './friend' import { NTQQFriendApi } from './friend'
import { Time } from 'cosmokit'
export class NTQQUserApi { export class NTQQUserApi {
static async setQQAvatar(filePath: string) { static async setQQAvatar(filePath: string) {
@ -20,7 +21,7 @@ export class NTQQUserApi {
}, },
null, null,
], ],
timeoutSecond: 10, // 10秒不一定够 timeout: 10 * Time.second, // 10秒不一定够
}) })
} }
@ -28,7 +29,7 @@ export class NTQQUserApi {
return await callNTQQApi<SelfInfo>({ return await callNTQQApi<SelfInfo>({
className: NTQQApiClass.GLOBAL_DATA, className: NTQQApiClass.GLOBAL_DATA,
methodName: NTQQApiMethod.SELF_INFO, methodName: NTQQApiMethod.SELF_INFO,
timeoutSecond: 2, timeout: 2 * Time.second,
}) })
} }
@ -41,33 +42,53 @@ export class NTQQUserApi {
return result.profiles.get(uid) return result.profiles.get(uid)
} }
/** 26702 */
static async fetchUserDetailInfo(uid: string) { static async fetchUserDetailInfo(uid: string) {
type EventService = NodeIKernelProfileService['fetchUserDetailInfo'] let info: UserDetailInfoListenerArg
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'] if (NTEventDispatch.initialised) {
const [_retData, profile] = await NTEventDispatch.CallNormalEvent type EventService = NodeIKernelProfileService['fetchUserDetailInfo']
<EventService, EventListener> type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged']
( const [_retData, profile] = await NTEventDispatch.CallNormalEvent
'NodeIKernelProfileService/fetchUserDetailInfo', <EventService, EventListener>
'NodeIKernelProfileListener/onUserDetailInfoChanged', (
1, 'NodeIKernelProfileService/fetchUserDetailInfo',
5000, 'NodeIKernelProfileListener/onUserDetailInfoChanged',
(profile) => profile.uid === uid, 1,
'BuddyProfileStore', 5000,
[uid], (profile) => profile.uid === uid,
UserDetailSource.KSERVER, 'BuddyProfileStore',
[ProfileBizType.KALL] [uid],
) UserDetailSource.KSERVER,
const RetUser: User = { [ProfileBizType.KALL]
...profile.simpleInfo.coreInfo, )
...profile.simpleInfo.status, info = profile
...profile.simpleInfo.vasInfo, } else {
...profile.commonExt, const result = await callNTQQApi<{ info: UserDetailInfoListenerArg }>({
...profile.simpleInfo.baseInfo, methodName: 'nodeIKernelProfileService/fetchUserDetailInfo',
qqLevel: profile.commonExt.qqLevel, 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: '' pendantId: ''
} }
return RetUser return ret
} }
static async getUserDetailInfo(uid: string, getLevel = false, withBizInfo = true) { static async getUserDetailInfo(uid: string, getLevel = false, withBizInfo = true) {
@ -115,7 +136,7 @@ export class NTQQUserApi {
} }
return cookies return cookies
} }
static async getSkey(): Promise<string> { static async getSkey(): Promise<string> {
const clientKeyData = await NTQQUserApi.getClientKey() const clientKeyData = await NTQQUserApi.getClientKey()
if (clientKeyData.result !== 0) { if (clientKeyData.result !== 0) {

View File

@ -77,15 +77,17 @@ let callHooks: Array<{
hookFunc: (callParams: unknown[]) => void | Promise<void> hookFunc: (callParams: unknown[]) => void | Promise<void>
}> = [] }> = []
const logHook = false
export function hookNTQQApiReceive(window: BrowserWindow) { export function hookNTQQApiReceive(window: BrowserWindow) {
const originalSend = window.webContents.send const originalSend = window.webContents.send
const patchSend = (channel: string, ...args: NTQQApiReturnData) => { const patchSend = (channel: string, ...args: NTQQApiReturnData) => {
/*try { try {
const isLogger = args[0]?.eventName?.startsWith('ns-LoggerApi') const isLogger = args[0]?.eventName?.startsWith('ns-LoggerApi')
if (!isLogger) { if (logHook && !isLogger) {
log(`received ntqq api message: ${channel}`, args) log(`received ntqq api message: ${channel}`, args)
} }
} catch { }*/ } catch { }
if (args?.[1] instanceof Array) { if (args?.[1] instanceof Array) {
for (const receiveData of args?.[1]) { for (const receiveData of args?.[1]) {
const ntQQApiMethodName = receiveData.cmdName const ntQQApiMethodName = receiveData.cmdName
@ -134,9 +136,9 @@ export function hookNTQQApiCall(window: BrowserWindow) {
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) logHook && log('call NTQQ api', thisArg, args)
} catch (e) { }*/ } catch (e) { }
try { try {
const _args: unknown[] = args[3][1] const _args: unknown[] = args[3][1]
const cmdName: NTQQApiMethod = _args[0] as NTQQApiMethod const cmdName: NTQQApiMethod = _args[0] as NTQQApiMethod
@ -145,13 +147,11 @@ export function hookNTQQApiCall(window: BrowserWindow) {
if (hook.method.includes(cmdName)) { if (hook.method.includes(cmdName)) {
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
try { try {
let _ = hook.hookFunc(callParams) hook.hookFunc(callParams)
if (hook.hookFunc.constructor.name === 'AsyncFunction') { } catch (e: any) {
(_ as Promise<void>).then()
}
} catch (e) {
log('hook call error', e, _args) log('hook call error', e, _args)
} }
resolve(undefined)
}).then() }).then()
} }
}) })
@ -399,7 +399,7 @@ export async function startHook() {
friendList.push(...fData.buddyList) friendList.push(...fData.buddyList)
} }
} }
log('好友列表变动', friendList) log('好友列表变动', friendList.length)
for (let friend of friendList) { for (let friend of friendList) {
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)

View File

@ -1,7 +1,8 @@
import { ipcMain } from 'electron' 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 { log } from '../common/utils/log'
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import { GeneralCallResult } from './services/common'
export enum NTQQApiClass { export enum NTQQApiClass {
NT_API = 'ns-ntApi', NT_API = 'ns-ntApi',
@ -108,38 +109,24 @@ interface NTQQApiParams {
cbCmd?: ReceiveCmd | ReceiveCmd[] | null cbCmd?: ReceiveCmd | ReceiveCmd[] | null
cmdCB?: (payload: any) => boolean cmdCB?: (payload: any) => boolean
afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd afterFirstCmd?: boolean // 是否在methodName调用完之后再去hook cbCmd
timeoutSecond?: number timeout?: number
} }
export function callNTQQApi<ReturnType>(params: NTQQApiParams) { export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
let { const className = params.className ?? NTQQApiClass.NT_API
className, const channel = params.channel ?? NTQQApiChannel.IPC_UP_2
methodName, const timeout = params.timeout ?? 5000
channel, const afterFirstCmd = params.afterFirstCmd ?? true
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 uuid = randomUUID() 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) => { return new Promise((resolve: (data: ReturnType) => void, reject) => {
// log("callNTQQApiPromise", channel, className, methodName, args, uuid)
const _timeout = timeout * 1000
let success = false let success = false
let eventName = className + '-' + channel[channel.length - 1] if (!params.cbCmd) {
if (classNameIsRegister) {
eventName += '-register'
}
const apiArgs = [methodName, ...args]
if (!cbCmd) {
// QQ后端会返回结果并且可以根据uuid识别 // QQ后端会返回结果并且可以根据uuid识别
hookApiCallbacks[uuid] = (r: ReturnType) => { hookApiCallbacks[uuid] = (r: ReturnType) => {
success = true success = true
@ -149,10 +136,10 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
else { else {
// 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据 // 这里的callback比较特殊QQ后端先返回是否调用成功再返回一条结果数据
const secondCallback = () => { const secondCallback = () => {
const hookId = registerReceiveHook<ReturnType>(cbCmd, (payload) => { const hookId = registerReceiveHook<ReturnType>(params.cbCmd!, (payload) => {
// log(methodName, "second callback", cbCmd, payload, cmdCB); // log(methodName, "second callback", cbCmd, payload, cmdCB);
if (!!cmdCB) { if (!!params.cmdCB) {
if (cmdCB(payload)) { if (params.cmdCB(payload)) {
removeReceiveHook(hookId) removeReceiveHook(hookId)
success = true success = true
resolve(payload) resolve(payload)
@ -167,8 +154,8 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
} }
!afterFirstCmd && secondCallback() !afterFirstCmd && secondCallback()
hookApiCallbacks[uuid] = (result: GeneralCallResult) => { hookApiCallbacks[uuid] = (result: GeneralCallResult) => {
log(`${methodName} callback`, result) log(`${params.methodName} callback`, result)
if (result?.result == 0 || result === undefined) { if (result?.result === 0 || result === undefined) {
afterFirstCmd && secondCallback() afterFirstCmd && secondCallback()
} }
else { else {
@ -180,10 +167,10 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
setTimeout(() => { setTimeout(() => {
// log("ntqq api timeout", success, channel, className, methodName) // log("ntqq api timeout", success, channel, className, methodName)
if (!success) { if (!success) {
log(`ntqq api timeout ${channel}, ${eventName}, ${methodName}`, apiArgs) log(`ntqq api timeout ${channel}, ${eventName}, ${params.methodName}`, apiArgs)
reject(`ntqq api timeout ${channel}, ${eventName}, ${methodName}, ${apiArgs}`) reject(`ntqq api timeout ${channel}, ${eventName}, ${params.methodName}, ${apiArgs}`)
} }
}, _timeout) }, timeout)
ipcMain.emit( ipcMain.emit(
channel, channel,
@ -199,7 +186,4 @@ export function callNTQQApi<ReturnType>(params: NTQQApiParams) {
}) })
} }
export interface GeneralCallResult { export { GeneralCallResult }
result: number // 0: success
errMsg: string
}