Add service handler registration and DI support

Introduces dependency injection via Inversify and reflect-metadata, adds a service handler registry for packet handling, and updates core initialization to auto-register and bind service handlers. Also updates Vite configs and auto-include logic to support protocol service files.
This commit is contained in:
手瓜一十雪 2025-11-14 22:20:33 +08:00
parent a2a73ce2dd
commit f04ffa5dc6
8 changed files with 62 additions and 13 deletions

View File

@ -14,6 +14,8 @@
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.3",
"@vitejs/plugin-react-swc": "^4.2.2",
"inversify": "^7.10.4",
"reflect-metadata": "^0.2.2",
"typescript": "^5.3.0",
"vite": "^6.4.1",
"vite-plugin-cp": "^6.0.3"

View File

@ -25,18 +25,18 @@ export class NTEventWrapper {
private readonly listenerManager: Map<string, ListenerClassBase> = new Map<string, ListenerClassBase>(); // ListenerName-Unique -> Listener实例
private readonly EventTask = new Map<string, Map<string, Map<string, InternalMapKey>>>(); // tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func}
constructor (
constructor(
wrapperSession: NodeIQQNTWrapperSession
) {
this.WrapperSession = wrapperSession;
}
createProxyDispatch (ListenerMainName: string) {
createProxyDispatch(ListenerMainName: string) {
const dispatcherListenerFunc = this.dispatcherListener.bind(this);
return new Proxy(
{},
{
get (target: any, prop: any, receiver: any) {
get(target: any, prop: any, receiver: any) {
if (typeof target[prop] === 'undefined') {
// 如果方法不存在返回一个函数这个函数调用existentMethod
return (...args: any[]) => {
@ -54,7 +54,7 @@ export class NTEventWrapper {
Service extends keyof ServiceNamingMapping,
ServiceMethod extends FuncKeys<ServiceNamingMapping[Service]>,
T extends (...args: any) => any = EnsureFunc<ServiceNamingMapping[Service][ServiceMethod]>
> (eventName: `${Service}/${ServiceMethod}`): T | undefined {
>(eventName: `${Service}/${ServiceMethod}`): T | undefined {
const eventNameArr = eventName.split('/');
type eventType = {
[key: string]: () => { [key: string]: (...params: Parameters<T>) => Promise<ReturnType<T>>; };
@ -78,8 +78,8 @@ export class NTEventWrapper {
return undefined;
}
createListenerFunction<T, Listener extends keyof ListenerNamingMapping,
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>> (listenerMainName: string, uniqueCode: string = ''): T {
createListenerFunction<T, Listener extends keyof ListenerNamingMapping,
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>>(listenerMainName: string, uniqueCode: string = ''): T {
const existListener = this.listenerManager.get(listenerMainName + uniqueCode);
if (!existListener) {
const Listener = this.createProxyDispatch(listenerMainName);
@ -97,7 +97,7 @@ export class NTEventWrapper {
}
// 统一回调清理事件
async dispatcherListener (ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) {
this.EventTask.get(ListenerMainName)
?.get(ListenerSubName)
?.forEach((task, uuid) => {
@ -115,7 +115,7 @@ export class NTEventWrapper {
Service extends keyof ServiceNamingMapping,
ServiceMethod extends FuncKeys<ServiceNamingMapping[Service]>,
EventType extends (...args: any) => any = EnsureFunc<ServiceNamingMapping[Service][ServiceMethod]>
> (
>(
serviceAndMethod: `${Service}/${ServiceMethod}`,
...args: Parameters<EventType>
): Promise<Awaited<ReturnType<EventType>>> {
@ -126,7 +126,7 @@ export class NTEventWrapper {
Listener extends keyof ListenerNamingMapping,
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>,
ListenerType extends (...args: any) => any = EnsureFunc<ListenerNamingMapping[Listener][ListenerMethod]>
> (
>(
listenerAndMethod: `${Listener}/${ListenerMethod}`,
checker: (...args: Parameters<ListenerType>) => boolean,
waitTimes = 1,
@ -140,7 +140,7 @@ export class NTEventWrapper {
let complete = 0;
let retData: Parameters<ListenerType> | undefined;
function sendDataCallback () {
function sendDataCallback() {
if (complete === 0) {
reject(new Error(' ListenerName:' + listenerAndMethod + ' timeout'));
} else {
@ -180,7 +180,7 @@ export class NTEventWrapper {
ListenerMethod extends FuncKeys<ListenerNamingMapping[Listener]>,
EventType extends (...args: any) => any = EnsureFunc<ServiceNamingMapping[Service][ServiceMethod]>,
ListenerType extends (...args: any) => any = EnsureFunc<ListenerNamingMapping[Listener][ListenerMethod]>
> (
>(
serviceAndMethod: `${Service}/${ServiceMethod}`,
listenerAndMethod: `${Listener}/${ListenerMethod}`,
args: Parameters<EventType>,
@ -194,7 +194,7 @@ export class NTEventWrapper {
let retData: Parameters<ListenerType> | undefined;
let retEvent: any = {};
function sendDataCallback (resolve: any, reject: any) {
function sendDataCallback(resolve: any, reject: any) {
if (complete === 0) {
reject(
new Error(

View File

@ -31,6 +31,7 @@ import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/napcat-cor
import { proxiedListenerOf } from 'napcat-common/src/proxy-handler';
import { NTQQPacketApi } from './apis/packet';
import { NativePacketHandler } from './packet/handler/client';
import { container, ReceiverServiceRegistry } from './packet/handler/serviceRegister';
export * from './wrapper';
export * from './types/index';
export * from './services/index';
@ -118,6 +119,15 @@ export class NapCatCore {
UserApi: new NTQQUserApi(this.context, this),
GroupApi: new NTQQGroupApi(this.context, this),
};
container.bind(NapCatCore).toConstantValue(this);
ReceiverServiceRegistry.forEach((ServiceClass, serviceName) => {
container.bind(ServiceClass).toSelf();
console.log(`Registering service handler for: ${serviceName}`);
this.context.packetHandler.onCmd(serviceName, ({ seq, hex_data }) => {
const serviceInstance = container.get(ServiceClass);
return serviceInstance.handler(seq, hex_data);
});
});
}
async initCore () {

View File

@ -0,0 +1,24 @@
import "reflect-metadata";
import { Container, injectable } from "inversify";
import { NapCatCore } from "../..";
export const container = new Container();
export const ReceiverServiceRegistry = new Map<string, new (...args: any[]) => ServiceBase>();
export abstract class ServiceBase {
get core(): NapCatCore {
return container.get(NapCatCore);
}
abstract handler(seq: number, hex_data: string): Promise<void> | void;
}
export function ReceiveService(serviceName: string) {
return function <T extends new (...args: any[]) => ServiceBase>(constructor: T) {
injectable()(constructor);
ReceiverServiceRegistry.set(serviceName, constructor);
return constructor;
};
}

View File

@ -0,0 +1,8 @@
import { ReceiveService, ServiceBase } from "../packet/handler/serviceRegister";
// @ReceiveService('trpc.msg.olpush.OlPushService.MsgPush')
// export class OlPushService extends ServiceBase {
// async handler(seq: number, hex_data: string) {
// console.log(`OlPushService handler called with seq: ${seq} and data: ${hex_data}`);
// }
// }

View File

@ -16,6 +16,7 @@ const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].
const FrameworkBaseConfigPlugin: PluginOption[] = [
autoIncludeTSPlugin({
entries: [
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-core/protocol') },
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') }
]
}),

View File

@ -19,6 +19,7 @@ const ShellBaseConfigPlugin: PluginOption[] = [
react({ tsDecorators: true }),
autoIncludeTSPlugin({
entries: [
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-core/protocol') },
{ entry: 'napcat.ts', dir: path.resolve(__dirname, '../napcat-onebot/action/test') }
]
}),

View File

@ -12,6 +12,9 @@ export function autoIncludeTSPlugin(options) {
async buildStart() {
tsFilesMap = {};
for (const { entry, dir } of entries) {
if (!tsFilesMap[entry]) {
tsFilesMap[entry] = [];
}
const fullDir = path.resolve(dir);
const allTsFiles = await findTSFiles(fullDir);
@ -29,7 +32,7 @@ export function autoIncludeTSPlugin(options) {
}
});
tsFilesMap[entry] = validFiles;
tsFilesMap[entry].push(...validFiles);
}
},