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

View File

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

View File

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

View File

@ -43,15 +43,14 @@ export class NTQQGroupApi {
async getGroups(forced = false) {
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',
'NodeIKernelGroupListener/onGroupListUpdate',
1,
5000,
() => true,
forced,
);
);
return groupList;
}
@ -255,8 +254,7 @@ export class NTQQGroupApi {
}
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',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
1,
@ -265,7 +263,7 @@ export class NTQQGroupApi {
false,
'',
num,
);
);
return notifies;
}

View File

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

View File

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

View File

@ -10,3 +10,28 @@ export * from './NodeIKernelTicketListener';
export * from './NodeIKernelStorageCleanListener';
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 './NodeIKernelDbToolsService';
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;
};