refactor: enhanced type definition for callNormalEvent

类型体操,伟大,无需多言!
This commit is contained in:
Wesley F. Young
2024-08-26 12:02:41 +08:00
parent a71eddbed2
commit e21c779d06
8 changed files with 193 additions and 150 deletions

View File

@@ -1,5 +1,6 @@
import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper'; import { NodeIQQNTWrapperSession } from '@/core/wrapper/wrapper';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { ListenerNamingMapping, ServiceNamingMapping } from '@/core';
interface InternalMapKey { interface InternalMapKey {
timeout: number; timeout: number;
@@ -145,12 +146,21 @@ export class LegacyNTEventWrapper {
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
}); });
} }
async CallNormalEventV2<
EventType extends (...args: any[]) => Promise<any>, async callNormalEventV2<
ListenerType extends (...args: any[]) => void Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends Exclude<keyof ListenerNamingMapping[Listener], symbol>,
// eslint-disable-next-line
// @ts-ignore
EventType extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
// eslint-disable-next-line
// @ts-ignore
ListenerType extends (...args: any) => any = ListenerNamingMapping[Listener][ListenerMethod]
>( >(
EventName = '', serviceAndMethod: `${Service}/${ServiceMethod}`,
ListenerName = '', listenerAndMethod: `${Listener}/${ListenerMethod}`,
waitTimes = 1, waitTimes = 1,
timeout: number = 3000, timeout: number = 3000,
checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true, checkerEvent: (ret: Awaited<ReturnType<EventType>>) => boolean = () => true,
@@ -163,14 +173,14 @@ export class LegacyNTEventWrapper {
let complete = 0; let complete = 0;
let retData: Parameters<ListenerType> | undefined = undefined; let retData: Parameters<ListenerType> | undefined = undefined;
let retEvent: any = {}; let retEvent: any = {};
const databack = () => { function sendDataCallback() {
if (complete == 0) { if (complete == 0) {
reject( reject(
new Error( new Error(
'Timeout: NTEvent EventName:' + 'Timeout: NTEvent serviceAndMethod:' +
EventName + serviceAndMethod +
' ListenerName:' + ' ListenerName:' +
ListenerName + listenerAndMethod +
' EventRet:\n' + ' EventRet:\n' +
JSON.stringify(retEvent, null, 4) + JSON.stringify(retEvent, null, 4) +
'\n', '\n',
@@ -179,15 +189,15 @@ export class LegacyNTEventWrapper {
} else { } else {
resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]); resolve([retEvent as Awaited<ReturnType<EventType>>, ...retData!]);
} }
}; }
const ListenerNameList = ListenerName.split('/'); const ListenerNameList = listenerAndMethod.split('/');
const ListenerMainName = ListenerNameList[0]; const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1]; const ListenerSubName = ListenerNameList[1];
const Timeouter = setTimeout(databack, timeout); const timeoutRef = setTimeout(sendDataCallback, timeout);
const eventCallbak = { const eventCallback = {
timeout: timeout, timeout: timeout,
createtime: Date.now(), createtime: Date.now(),
checker: checkerListener, checker: checkerListener,
@@ -195,8 +205,8 @@ export class LegacyNTEventWrapper {
complete++; complete++;
retData = args as Parameters<ListenerType>; retData = args as Parameters<ListenerType>;
if (complete >= waitTimes) { if (complete >= waitTimes) {
clearTimeout(Timeouter); clearTimeout(timeoutRef);
databack(); sendDataCallback();
} }
}, },
}; };
@@ -206,18 +216,18 @@ export class LegacyNTEventWrapper {
if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) { if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) {
this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map()); this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map());
} }
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak); this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback);
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName); const EventFunc = this.createEventFunction<EventType>(serviceAndMethod);
retEvent = await EventFunc!(...(args as any[])); retEvent = await EventFunc!(...(args as any[]));
if (!checkerEvent(retEvent)) { if (!checkerEvent(retEvent)) {
clearTimeout(Timeouter); clearTimeout(timeoutRef);
reject( reject(
new Error( new Error(
'EventChecker Failed: NTEvent EventName:' + 'EventChecker Failed: NTEvent serviceAndMethod:' +
EventName + serviceAndMethod +
' ListenerName:' + ' ListenerName:' +
ListenerName + listenerAndMethod +
' EventRet:\n' + ' EventRet:\n' +
JSON.stringify(retEvent, null, 4) + JSON.stringify(retEvent, null, 4) +
'\n', '\n',
@@ -227,12 +237,21 @@ export class LegacyNTEventWrapper {
}, },
); );
} }
async CallNormalEvent<
EventType extends (...args: any[]) => Promise<any>, async callNormalEvent<
ListenerType extends (...args: any[]) => void Service extends keyof ServiceNamingMapping,
ServiceMethod extends Exclude<keyof ServiceNamingMapping[Service], symbol>,
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends Exclude<keyof ListenerNamingMapping[Listener], symbol>,
// eslint-disable-next-line
// @ts-ignore
EventType extends (...args: any) => any = ServiceNamingMapping[Service][ServiceMethod],
// eslint-disable-next-line
// @ts-ignore
ListenerType extends (...args: any) => any = ListenerNamingMapping[Listener][ListenerMethod]
>( >(
EventName = '', serviceAndMethod: `${Service}/${ServiceMethod}`,
ListenerName = '', listenerAndMethod: `${Listener}/${ListenerMethod}`,
waitTimes = 1, waitTimes = 1,
timeout: number = 3000, timeout: number = 3000,
checker: (...args: Parameters<ListenerType>) => boolean, checker: (...args: Parameters<ListenerType>) => boolean,
@@ -249,9 +268,9 @@ export class LegacyNTEventWrapper {
reject( reject(
new Error( new Error(
'Timeout: NTEvent EventName:' + 'Timeout: NTEvent EventName:' +
EventName + serviceAndMethod +
' ListenerName:' + ' ListenerName:' +
ListenerName + listenerAndMethod +
' EventRet:\n' + ' EventRet:\n' +
JSON.stringify(retEvent, null, 4) + JSON.stringify(retEvent, null, 4) +
'\n', '\n',
@@ -262,7 +281,7 @@ export class LegacyNTEventWrapper {
} }
}; };
const ListenerNameList = ListenerName.split('/'); const ListenerNameList = listenerAndMethod.split('/');
const ListenerMainName = ListenerNameList[0]; const ListenerMainName = ListenerNameList[0];
const ListenerSubName = ListenerNameList[1]; const ListenerSubName = ListenerNameList[1];
@@ -290,7 +309,7 @@ export class LegacyNTEventWrapper {
} }
this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak); this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallbak);
this.createListenerFunction(ListenerMainName); this.createListenerFunction(ListenerMainName);
const EventFunc = this.createEventFunction<EventType>(EventName); const EventFunc = this.createEventFunction<EventType>(serviceAndMethod);
retEvent = await EventFunc!(...(args as any[])); retEvent = await EventFunc!(...(args as any[]));
}, },
); );

View File

@@ -304,66 +304,49 @@ export class NTQQFileApi {
return sourcePath; return sourcePath;
} }
} }
const data = await this.core.eventWrapper.CallNormalEvent< const [, fileTransNotifyInfo] = await this.core.eventWrapper.callNormalEvent(
( 'NodeIKernelMsgService/downloadRichMedia',
params: { 'NodeIKernelMsgListener/onRichMediaDownloadComplete',
fileModelId: string, 1,
downloadSourceType: number, timeout,
triggerType: number, (arg: OnRichMediaDownloadCompleteParams) => {
msgId: string, if (arg.msgId === msgId) {
chatType: ChatType, return true;
peerUid: string, }
elementId: string, return false;
thumbSize: number, },
downloadType: number, {
filePath: string fileModelId: '0',
}) => Promise<unknown>, downloadSourceType: 0,
(fileTransNotifyInfo: OnRichMediaDownloadCompleteParams) => void triggerType: 1,
>( msgId: msgId,
'NodeIKernelMsgService/downloadRichMedia', chatType: chatType,
'NodeIKernelMsgListener/onRichMediaDownloadComplete', peerUid: peerUid,
1, elementId: elementId,
timeout, thumbSize: 0,
(arg: OnRichMediaDownloadCompleteParams) => { downloadType: 1,
if (arg.msgId === msgId) { filePath: thumbPath,
return true; },
} );
return false;
},
{
fileModelId: '0',
downloadSourceType: 0,
triggerType: 1,
msgId: msgId,
chatType: chatType,
peerUid: peerUid,
elementId: elementId,
thumbSize: 0,
downloadType: 1,
filePath: thumbPath,
},
);
const msg = await this.core.apis.MsgApi.getMsgsByMsgId({ const msg = await this.core.apis.MsgApi.getMsgsByMsgId({
guildId: '', guildId: '',
chatType: chatType, chatType: chatType,
peerUid: peerUid, peerUid: peerUid,
}, [msgId]); }, [msgId]);
if (msg.msgList.length === 0) { if (msg.msgList.length === 0) {
return data[1].filePath; return fileTransNotifyInfo.filePath;
} }
//获取原始消息 //获取原始消息
const FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId); const FileElements = msg?.msgList[0]?.elements?.find(e => e.elementId === elementId);
if (!FileElements) { if (!FileElements) {
//失败则就乱来 Todo //失败则就乱来 Todo
return data[1].filePath; return fileTransNotifyInfo.filePath;
} }
//从原始消息获取文件路径 //从原始消息获取文件路径
const filePath = return FileElements?.fileElement?.filePath ??
FileElements?.fileElement?.filePath ??
FileElements?.pttElement?.filePath ?? FileElements?.pttElement?.filePath ??
FileElements?.videoElement?.filePath ?? FileElements?.videoElement?.filePath ??
FileElements?.picElement?.sourcePath; FileElements?.picElement?.sourcePath;
return filePath;
} }
async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> { async getImageSize(filePath: string): Promise<ISizeCalculationResult | undefined> {

View File

@@ -74,10 +74,7 @@ export class NTQQFriendApi {
return this.context.session.getBuddyService().clearBuddyReqUnreadCnt(); return this.context.session.getBuddyService().clearBuddyReqUnreadCnt();
} }
async getBuddyReq() { async getBuddyReq() {
const [, ret] = await this.core.eventWrapper.CallNormalEventV2< const [, ret] = await this.core.eventWrapper.callNormalEventV2(
NodeIKernelBuddyService['getBuddyReq'],
NodeIKernelBuddyListener['onBuddyReqChange']
>(
'NodeIKernelBuddyService/getBuddyReq', 'NodeIKernelBuddyService/getBuddyReq',
'NodeIKernelBuddyListener/onBuddyReqChange', 'NodeIKernelBuddyListener/onBuddyReqChange',
1, 1,

View File

@@ -43,15 +43,14 @@ export class NTQQGroupApi {
async getGroups(forced = false) { async getGroups(forced = false) {
type ListenerType = NodeIKernelGroupListener['onGroupListUpdate']; type ListenerType = NodeIKernelGroupListener['onGroupListUpdate'];
const [_retData, _updateType, groupList] = await this.core.eventWrapper.CallNormalEvent<(force: boolean) => Promise<any>, ListenerType> const [,, groupList] = await this.core.eventWrapper.callNormalEvent(
(
'NodeIKernelGroupService/getGroupList', 'NodeIKernelGroupService/getGroupList',
'NodeIKernelGroupListener/onGroupListUpdate', 'NodeIKernelGroupListener/onGroupListUpdate',
1, 1,
5000, 5000,
() => true, () => true,
forced, forced,
); );
return groupList; return groupList;
} }
@@ -255,8 +254,7 @@ export class NTQQGroupApi {
} }
async getSingleScreenNotifies(num: number) { async getSingleScreenNotifies(num: number) {
const [_retData, _doubt, _seq, notifies] = await this.core.eventWrapper.CallNormalEvent<(arg1: boolean, arg2: string, arg3: number) => Promise<any>, (doubt: boolean, seq: string, notifies: GroupNotify[]) => void> const [,,, notifies] = await this.core.eventWrapper.callNormalEvent(
(
'NodeIKernelGroupService/getSingleScreenNotifies', 'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies', 'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
1, 1,
@@ -265,7 +263,7 @@ export class NTQQGroupApi {
false, false,
'', '',
num, num,
); );
return notifies; return notifies;
} }

View File

@@ -1,6 +1,5 @@
import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/entities'; import { ChatType, GetFileListParam, Peer, RawMessage, SendMessageElement, SendStatusType } from '@/core/entities';
import { InstanceContext, NapCatCore } from '@/core'; import { InstanceContext, NapCatCore } from '@/core';
import { GroupFileInfoUpdateParamType } from '@/core/listeners';
import { GeneralCallResult } from '@/core/services/common'; import { GeneralCallResult } from '@/core/services/common';
export class NTQQMsgApi { export class NTQQMsgApi {
@@ -117,22 +116,19 @@ export class NTQQMsgApi {
} }
async getGroupFileList(GroupCode: string, params: GetFileListParam) { async getGroupFileList(GroupCode: string, params: GetFileListParam) {
const data = await this.core.eventWrapper.CallNormalEvent< const [, groupFileListResult] = await this.core.eventWrapper.callNormalEvent(
(GroupCode: string, params: GetFileListParam) => Promise<unknown>, 'NodeIKernelRichMediaService/getGroupFileList',
(groupFileListResult: GroupFileInfoUpdateParamType) => void 'NodeIKernelMsgListener/onGroupFileInfoUpdate',
>( 1,
'NodeIKernelRichMediaService/getGroupFileList', 5000,
'NodeIKernelMsgListener/onGroupFileInfoUpdate', ( /* groupFileListResult: GroupFileInfoUpdateParamType */) => {
1,
5000,
(groupFileListResult: GroupFileInfoUpdateParamType) => {
//Developer Mlikiowa Todo: 此处有问题 无法判断是否成功 //Developer Mlikiowa Todo: 此处有问题 无法判断是否成功
return true; return true;
}, },
GroupCode, GroupCode,
params, params,
); );
return data[1].item; return groupFileListResult.item;
} }
async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) { async getMsgHistory(peer: Peer, msgId: string, count: number, isReverseOrder: boolean = false) {
@@ -179,33 +175,29 @@ export class NTQQMsgApi {
} }
const msgId = await this.generateMsgUniqueId(peer.chatType, await this.getServerTime()); const msgId = await this.generateMsgUniqueId(peer.chatType, await this.getServerTime());
peer.guildId = msgId; peer.guildId = msgId;
const data = await this.core.eventWrapper.CallNormalEvent< const [, msgList] = await this.core.eventWrapper.callNormalEvent(
(msgId: string, peer: Peer, msgElements: SendMessageElement[], map: Map<any, any>) => Promise<unknown>, 'NodeIKernelMsgService/sendMsg',
(msgList: RawMessage[]) => void 'NodeIKernelMsgListener/onMsgInfoListUpdate',
>( 1,
'NodeIKernelMsgService/sendMsg', timeout,
'NodeIKernelMsgListener/onMsgInfoListUpdate', (msgRecords: RawMessage[]) => {
1, for (const msgRecord of msgRecords) {
timeout, if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
(msgRecords: RawMessage[]) => { return true;
for (const msgRecord of msgRecords) {
if (msgRecord.guildId === msgId && msgRecord.sendStatus === SendStatusType.KSEND_STATUS_SUCCESS) {
return true;
}
} }
return false; }
}, return false;
'0', },
peer, '0',
msgElements, peer,
new Map(), msgElements,
); new Map(),
const retMsg = data[1].find(msgRecord => { );
return msgList.find(msgRecord => {
if (msgRecord.guildId === msgId) { if (msgRecord.guildId === msgId) {
return true; return true;
} }
}); });
return retMsg;
} }
async generateMsgUniqueId(chatType: number, time: string) { async generateMsgUniqueId(chatType: number, time: string) {
@@ -224,29 +216,26 @@ export class NTQQMsgApi {
const msgInfos = msgIds.map(id => { const msgInfos = msgIds.map(id => {
return { msgId: id, senderShowName: this.core.selfInfo.nick }; return { msgId: id, senderShowName: this.core.selfInfo.nick };
}); });
const data = await this.core.eventWrapper.CallNormalEvent< const [, msgList] = await this.core.eventWrapper.callNormalEvent(
(msgInfo: typeof msgInfos, srcPeer: Peer, destPeer: Peer, comment: Array<any>, attr: Map<any, any>) => Promise<unknown>, 'NodeIKernelMsgService/multiForwardMsgWithComment',
(msgList: RawMessage[]) => void 'NodeIKernelMsgListener/onMsgInfoListUpdate',
>( 1,
'NodeIKernelMsgService/multiForwardMsgWithComment', 5000,
'NodeIKernelMsgListener/onMsgInfoListUpdate', (msgRecords: RawMessage[]) => {
1, for (const msgRecord of msgRecords) {
5000, if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
(msgRecords: RawMessage[]) => { return true;
for (const msgRecord of msgRecords) {
if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == this.core.selfInfo.uid) {
return true;
}
} }
return false; }
}, return false;
msgInfos, },
srcPeer, msgInfos,
destPeer, srcPeer,
[], destPeer,
new Map(), [],
); new Map(),
for (const msg of data[1]) { );
for (const msg of msgList) {
const arkElement = msg.elements.find(ele => ele.arkElement); const arkElement = msg.elements.find(ele => ele.arkElement);
if (!arkElement) { if (!arkElement) {
continue; continue;

View File

@@ -69,9 +69,7 @@ export class NTQQUserApi {
type EventService = NodeIKernelProfileService['fetchUserDetailInfo']; type EventService = NodeIKernelProfileService['fetchUserDetailInfo'];
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged']; type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
const retData: User[] = []; const retData: User[] = [];
const [_retData, _retListener] = await this.core.eventWrapper.CallNormalEvent< const [_retData, _retListener] = await this.core.eventWrapper.callNormalEvent(
EventService, EventListener
>(
'NodeIKernelProfileService/fetchUserDetailInfo', 'NodeIKernelProfileService/fetchUserDetailInfo',
'NodeIKernelProfileListener/onUserDetailInfoChanged', 'NodeIKernelProfileListener/onUserDetailInfoChanged',
uids.length, uids.length,
@@ -102,9 +100,7 @@ export class NTQQUserApi {
} }
async fetchUserDetailInfo(uid: string, mode: UserDetailSource = UserDetailSource.KDB) { async fetchUserDetailInfo(uid: string, mode: UserDetailSource = UserDetailSource.KDB) {
type EventService = NodeIKernelProfileService['fetchUserDetailInfo']; const [_retData, profile] = await this.core.eventWrapper.callNormalEvent(
type EventListener = NodeIKernelProfileListener['onUserDetailInfoChanged'];
const [_retData, profile] = await this.core.eventWrapper.CallNormalEvent<EventService, EventListener>(
'NodeIKernelProfileService/fetchUserDetailInfo', 'NodeIKernelProfileService/fetchUserDetailInfo',
'NodeIKernelProfileListener/onUserDetailInfoChanged', 'NodeIKernelProfileListener/onUserDetailInfoChanged',
1, 1,

View File

@@ -10,3 +10,28 @@ export * from './NodeIKernelTicketListener';
export * from './NodeIKernelStorageCleanListener'; export * from './NodeIKernelStorageCleanListener';
export * from './NodeIKernelFileAssistantListener'; export * from './NodeIKernelFileAssistantListener';
import type {
NodeIKernelSessionListener,
NodeIKernelLoginListener,
NodeIKernelMsgListener,
NodeIKernelGroupListener,
NodeIKernelBuddyListener,
NodeIKernelProfileListener,
NodeIKernelRobotListener,
NodeIKernelTicketListener,
NodeIKernelStorageCleanListener,
NodeIKernelFileAssistantListener,
} from '.';
export type ListenerNamingMapping = {
NodeIKernelSessionListener: NodeIKernelSessionListener;
NodeIKernelLoginListener: NodeIKernelLoginListener;
NodeIKernelMsgListener: NodeIKernelMsgListener;
NodeIKernelGroupListener: NodeIKernelGroupListener;
NodeIKernelBuddyListener: NodeIKernelBuddyListener;
NodeIKernelProfileListener: NodeIKernelProfileListener;
NodeIKernelRobotListener: NodeIKernelRobotListener;
NodeIKernelTicketListener: NodeIKernelTicketListener;
NodeIKernelStorageCleanListener: NodeIKernelStorageCleanListener;
NodeIKernelFileAssistantListener: NodeIKernelFileAssistantListener;
};

View File

@@ -14,3 +14,39 @@ export * from './NodeIKernelRobotService';
export * from './NodeIKernelRichMediaService'; export * from './NodeIKernelRichMediaService';
export * from './NodeIKernelDbToolsService'; export * from './NodeIKernelDbToolsService';
export * from './NodeIKernelTipOffService'; export * from './NodeIKernelTipOffService';
import type {
NodeIKernelAvatarService,
NodeIKernelBuddyService,
NodeIKernelDbToolsService,
NodeIKernelFileAssistantService,
NodeIKernelGroupService,
NodeIKernelLoginService,
NodeIKernelMsgService,
NodeIKernelOnlineStatusService,
NodeIKernelProfileLikeService,
NodeIKernelProfileService,
NodeIKernelRichMediaService,
NodeIKernelRobotService,
NodeIKernelStorageCleanService,
NodeIKernelTicketService,
NodeIKernelTipOffService,
} from '.';
export type ServiceNamingMapping = {
NodeIKernelAvatarService: NodeIKernelAvatarService;
NodeIKernelBuddyService: NodeIKernelBuddyService;
NodeIKernelFileAssistantService: NodeIKernelFileAssistantService;
NodeIKernelGroupService: NodeIKernelGroupService;
NodeIKernelLoginService: NodeIKernelLoginService;
NodeIKernelMsgService: NodeIKernelMsgService;
NodeIKernelOnlineStatusService: NodeIKernelOnlineStatusService;
NodeIKernelProfileLikeService: NodeIKernelProfileLikeService;
NodeIKernelProfileService: NodeIKernelProfileService;
NodeIKernelTicketService: NodeIKernelTicketService;
NodeIKernelStorageCleanService: NodeIKernelStorageCleanService;
NodeIKernelRobotService: NodeIKernelRobotService;
NodeIKernelRichMediaService: NodeIKernelRichMediaService;
NodeIKernelDbToolsService: NodeIKernelDbToolsService;
NodeIKernelTipOffService: NodeIKernelTipOffService;
};