mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
feat: test-rpc-service
This commit is contained in:
@@ -10,9 +10,9 @@ interface InternalMapKey {
|
||||
checker: ((...args: any[]) => boolean) | undefined;
|
||||
}
|
||||
|
||||
type EnsureFunc<T> = T extends (...args: any) => any ? T : never;
|
||||
export type EnsureFunc<T> = T extends (...args: any) => any ? T : never;
|
||||
|
||||
type FuncKeys<T> = Extract<
|
||||
export type FuncKeys<T> = Extract<
|
||||
{
|
||||
[K in keyof T]: EnsureFunc<T[K]> extends never ? never : K;
|
||||
}[keyof T],
|
||||
|
@@ -20,3 +20,23 @@ export function proxyHandlerOf(logger: LogWrapper) {
|
||||
export function proxiedListenerOf<T extends object>(listener: T, logger: LogWrapper) {
|
||||
return new Proxy<T>(listener, proxyHandlerOf(logger));
|
||||
}
|
||||
|
||||
export function proxyHandlerOfWithoutLogger() {
|
||||
return {
|
||||
get(target: any, prop: any, receiver: any) {
|
||||
if (typeof target[prop] === 'undefined') {
|
||||
// 如果方法不存在,返回一个函数,这个函数调用existentMethod
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return (..._args: unknown[]) => {
|
||||
console.log(`${target.constructor.name} has no method ${prop}`);
|
||||
};
|
||||
}
|
||||
// 如果方法存在,正常返回
|
||||
return Reflect.get(target, prop, receiver);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function proxiedListenerOfWithoutLogger<T extends object>(listener: T) {
|
||||
return new Proxy<T>(listener, proxyHandlerOfWithoutLogger());
|
||||
}
|
@@ -30,6 +30,7 @@ import os from 'node:os';
|
||||
import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
|
||||
import { proxiedListenerOf } from '@/common/proxy-handler';
|
||||
import { NTQQPacketApi } from './apis/packet';
|
||||
import { createVirtualServiceClient, handleServiceServerOnce } from '@/framework/proxy/service';
|
||||
export * from './wrapper';
|
||||
export * from './types';
|
||||
export * from './services';
|
||||
@@ -99,7 +100,7 @@ export class NapCatCore {
|
||||
this.context = context;
|
||||
this.util = this.context.wrapper.NodeQQNTWrapperUtil;
|
||||
this.eventWrapper = new NTEventWrapper(context.session);
|
||||
this.configLoader = new NapCatConfigLoader(this, this.context.pathWrapper.configPath,NapcatConfigSchema);
|
||||
this.configLoader = new NapCatConfigLoader(this, this.context.pathWrapper.configPath, NapcatConfigSchema);
|
||||
this.apis = {
|
||||
FileApi: new NTQQFileApi(this.context, this),
|
||||
SystemApi: new NTQQSystemApi(this.context, this),
|
||||
@@ -168,6 +169,13 @@ export class NapCatCore {
|
||||
proxiedListenerOf(msgListener, this.context.logger),
|
||||
);
|
||||
|
||||
let msgServiceClient = createVirtualServiceClient('NodeIKernelMsgService', async (ServiceCommand, ...args) => {
|
||||
this.context.logger.log(`Client Outing->[${ServiceCommand}]`, ...args);
|
||||
return handleServiceServerOnce(ServiceCommand, async (listenerCommand: string, ...args: any[]) => {
|
||||
msgServiceClient.receiverListener(listenerCommand, ...args);
|
||||
}, this.eventWrapper, ...args);
|
||||
});
|
||||
console.log('msgServiceClient', await msgServiceClient.object.fetchFavEmojiList('', 50, true, true));
|
||||
const profileListener = new NodeIKernelProfileListener();
|
||||
profileListener.onProfileDetailInfoChanged = (profile) => {
|
||||
if (profile.uid === this.selfInfo.uid) {
|
||||
|
@@ -1,71 +1,71 @@
|
||||
import { User, UserDetailInfoListenerArg } from '@/core/types';
|
||||
import { SelfStatusInfo, User, UserDetailInfoListenerArg } from '@/core/types';
|
||||
|
||||
export class NodeIKernelProfileListener {
|
||||
onUserDetailInfoChanged(arg: UserDetailInfoListenerArg): void {
|
||||
onUserDetailInfoChanged(_arg: UserDetailInfoListenerArg): void {
|
||||
|
||||
}
|
||||
|
||||
onProfileSimpleChanged(...args: unknown[]): any {
|
||||
onProfileSimpleChanged(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onProfileDetailInfoChanged(profile: User): any {
|
||||
onProfileDetailInfoChanged(_profile: User): any {
|
||||
|
||||
}
|
||||
|
||||
onStatusUpdate(...args: unknown[]): any {
|
||||
onStatusUpdate(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onSelfStatusChanged(...args: unknown[]): any {
|
||||
onSelfStatusChanged(_info: SelfStatusInfo): any {
|
||||
|
||||
}
|
||||
|
||||
onStrangerRemarkChanged(...args: unknown[]): any {
|
||||
onStrangerRemarkChanged(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onMemberListChange(...args: unknown[]): any {
|
||||
onMemberListChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onMemberInfoChange(...args: unknown[]): any {
|
||||
onMemberInfoChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupListUpdate(...args: unknown[]): any {
|
||||
onGroupListUpdate(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupAllInfoChange(...args: unknown[]): any {
|
||||
onGroupAllInfoChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupDetailInfoChange(...args: unknown[]): any {
|
||||
onGroupDetailInfoChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupConfMemberChange(...args: unknown[]): any {
|
||||
onGroupConfMemberChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupExtListUpdate(...args: unknown[]): any {
|
||||
onGroupExtListUpdate(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupNotifiesUpdated(...args: unknown[]): any {
|
||||
onGroupNotifiesUpdated(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupNotifiesUnreadCountUpdated(...args: unknown[]): any {
|
||||
onGroupNotifiesUnreadCountUpdated(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupMemberLevelInfoChange(...args: unknown[]): any {
|
||||
onGroupMemberLevelInfoChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
|
||||
onGroupBulletinChange(...args: unknown[]): any {
|
||||
onGroupBulletinChange(..._args: unknown[]): any {
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -36,8 +36,10 @@ import type {
|
||||
NodeIKernelTicketService,
|
||||
NodeIKernelTipOffService,
|
||||
} from '.';
|
||||
import { NodeIKernelAlbumService } from './NodeIKernelAlbumService';
|
||||
|
||||
export type ServiceNamingMapping = {
|
||||
NodeIKernelAlbumService: NodeIKernelAlbumService;
|
||||
NodeIKernelAvatarService: NodeIKernelAvatarService;
|
||||
NodeIKernelBuddyService: NodeIKernelBuddyService;
|
||||
NodeIKernelFileAssistantService: NodeIKernelFileAssistantService;
|
||||
|
106
src/framework/proxy/service.ts
Normal file
106
src/framework/proxy/service.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { FuncKeys, NTEventWrapper } from "@/common/event";
|
||||
import { ServiceNamingMapping } from "@/core";
|
||||
|
||||
type ServiceMethodCommand = {
|
||||
[Service in keyof ServiceNamingMapping]: `${Service}/${FuncKeys<ServiceNamingMapping[Service]>}`
|
||||
}[keyof ServiceNamingMapping];
|
||||
|
||||
export const RegisterListenerCmd: Array<ServiceMethodCommand> = [
|
||||
'NodeIKernelMsgService/addKernelMsgListener',
|
||||
'NodeIKernelGroupService/addKernelGroupListener',
|
||||
'NodeIKernelProfileLikeService/addKernelProfileLikeListener',
|
||||
'NodeIKernelProfileService/addKernelProfileListener',
|
||||
'NodeIKernelBuddyService/addKernelBuddyListener',
|
||||
];
|
||||
|
||||
export function createVirtualServiceServer<T extends keyof ServiceNamingMapping>(
|
||||
serviceName: T,
|
||||
ntevent: NTEventWrapper,
|
||||
callback: (command: ServiceMethodCommand, ...args: any[]) => Promise<any>
|
||||
): ServiceNamingMapping[T] {
|
||||
return new Proxy(() => { }, {
|
||||
get: (_target: any, functionName: string) => {
|
||||
const command = `${serviceName}/${functionName}` as ServiceMethodCommand;
|
||||
if (RegisterListenerCmd.includes(command as ServiceMethodCommand)) {
|
||||
return async (..._args: any[]) => {
|
||||
const listener = new Proxy(new class { }(), {
|
||||
apply: (_target, _thisArg, _arguments) => {
|
||||
return callback(command, ..._arguments);
|
||||
}
|
||||
});
|
||||
return await (ntevent.callNoListenerEvent as any)(command, listener);
|
||||
};
|
||||
}
|
||||
return async (...args: any[]) => {
|
||||
return await (ntevent.callNoListenerEvent as any)(command, ...args);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 问题2: 全局状态管理可能导致内存泄漏和状态污染
|
||||
export const listenerCmdRegisted = new Map<ServiceMethodCommand, boolean>();
|
||||
export const clientCallback = new Map<string, (command: string, ...args: any[]) => Promise<any>>();
|
||||
export async function handleServiceServerOnce(
|
||||
command: ServiceMethodCommand,// 服务注册命令
|
||||
recvListener: (command: string, ...args: any[]) => Promise<any>,//listener监听器
|
||||
ntevent: NTEventWrapper,// 事件处理器
|
||||
...args: any[]//实际参数
|
||||
) {
|
||||
if (RegisterListenerCmd.includes(command)) {
|
||||
if (!listenerCmdRegisted.has(command)) {
|
||||
listenerCmdRegisted.set(command, true);
|
||||
return (ntevent.callNoListenerEvent as any)(command, new Proxy(new class { }(), {
|
||||
get: (_target: any, prop: string) => {
|
||||
return async (..._args: any[]) => {
|
||||
let listenerCmd = `${command.split('/')[0]}/${prop}`;
|
||||
recvListener(listenerCmd, ..._args);
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
console.log('handleServiceServerOnce', command, 'args', args);
|
||||
console.log('params', args);
|
||||
return await (ntevent.callNoListenerEvent as (command: ServiceMethodCommand, ...args: any[]) => Promise<any>)(command, ...args);
|
||||
}
|
||||
|
||||
export function createVirtualServiceClient<T extends keyof ServiceNamingMapping>(
|
||||
serviceName: T,
|
||||
receiverEvent: (command: ServiceMethodCommand, ...args: any[]) => Promise<any>
|
||||
) {
|
||||
const object = new Proxy(() => { }, {
|
||||
get: (_target: any, functionName: string) => {
|
||||
const command = `${serviceName}/${functionName}` as ServiceMethodCommand;
|
||||
if (RegisterListenerCmd.includes(command as ServiceMethodCommand)) {
|
||||
if (!clientCallback.has(command)) {
|
||||
return async (listener: Record<string, any>) => {
|
||||
// 遍历 listener
|
||||
for (const key in listener) {
|
||||
if (typeof listener[key] === 'function') {
|
||||
const listenerCmd = `${command.split('/')[0]}/${key}`;
|
||||
clientCallback.set(listenerCmd, listener[key].bind(listener));
|
||||
}
|
||||
}
|
||||
return await receiverEvent(command);
|
||||
};
|
||||
}
|
||||
}
|
||||
return async (...args: any[]) => {
|
||||
return await receiverEvent(command, ...args);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const receiverListener = function (command: string, ...args: any[]) {
|
||||
return clientCallback.get(command)?.(command, ...args);
|
||||
};
|
||||
return { receiverListener: receiverListener, object: object as ServiceNamingMapping[T] };
|
||||
}
|
||||
|
||||
// 建议添加清理函数
|
||||
export function clearServiceState() {
|
||||
listenerCmdRegisted.clear();
|
||||
clientCallback.clear();
|
||||
}
|
0
src/framework/proxy/session.ts
Normal file
0
src/framework/proxy/session.ts
Normal file
Reference in New Issue
Block a user