Commit 67f7abde authored by nanahira's avatar nanahira

interceptor

parent 116e50a9
export * from './src/koishi.decorators';
export * from './src/koishi.interfaces';
export * from './src/utility/koishi.decorators';
export * from './src/utility/koishi.interfaces';
export * from './src/utility/koishi.constants';
export * from './src/koishi.service';
export * from './src/koishi.module';
export * from './src/koishi-context.factory';
export * from './src/utility/koishi-context.factory';
export * from './src/koishi.ws-adapter';
import { Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { MetadataArrayMap } from '../utility/koishi.constants';
import {
MetadataArrayMap,
MetadataArrayValue,
MetadataGenericMap,
} from '../utility/koishi.constants';
MetadataMapValue,
} from '../utility/koishi.interfaces';
@Injectable()
export class KoishiMetadataFetcherService {
......@@ -25,7 +27,7 @@ export class KoishiMetadataFetcherService {
instance: any,
) {
return (
this.reflector.get<MetadataArrayMap[K][]>(metadataKey, instance) || []
this.reflector.get<MetadataArrayValue<K>>(metadataKey, instance) || []
);
}
......@@ -33,6 +35,6 @@ export class KoishiMetadataFetcherService {
metadataKey: K,
instance: any,
) {
return this.reflector.get<MetadataGenericMap[K]>(metadataKey, instance);
return this.reflector.get<MetadataMapValue<K>>(metadataKey, instance);
}
}
......@@ -10,7 +10,7 @@ import {
KoishiModuleAsyncOptions,
KoishiModuleOptions,
KoishiModuleOptionsFactory,
} from './koishi.interfaces';
} from './utility/koishi.interfaces';
import { KoishiService } from './koishi.service';
import {
KOISHI_CONTEXT,
......@@ -21,7 +21,7 @@ import { KoishiLoggerService } from './providers/koishi-logger.service';
import { KoishiMetascanService } from './providers/koishi-metascan.service';
import { DiscoveryModule, INQUIRER } from '@nestjs/core';
import { Context } from 'koishi';
import { defaultContextContainer } from './koishi-context.factory';
import { defaultContextContainer } from './utility/koishi-context.factory';
import { KoishiInjectionService } from './providers/koishi-injection.service';
import { KoishiContextService } from './providers/koishi-context.service';
import { KoishiHttpDiscoveryService } from './koishi-http-discovery/koishi-http-discovery.service';
......
......@@ -6,7 +6,7 @@ import {
OnModuleDestroy,
OnModuleInit,
} from '@nestjs/common';
import { KoishiModuleOptions } from './koishi.interfaces';
import { KoishiModuleOptions } from './utility/koishi.interfaces';
import { Server } from 'http';
import Koa from 'koa';
import KoaBodyParser from 'koa-bodyparser';
......@@ -19,8 +19,7 @@ import { KoishiHttpDiscoveryService } from './koishi-http-discovery/koishi-http-
@Injectable()
export class KoishiService
extends App
implements OnModuleInit, OnApplicationBootstrap, OnModuleDestroy
{
implements OnModuleInit, OnApplicationBootstrap, OnModuleDestroy {
constructor(
@Inject(KOISHI_MODULE_OPTIONS)
private readonly koishiModuleOptions: KoishiModuleOptions,
......
......@@ -3,7 +3,7 @@ import { KOISHI_MODULE_OPTIONS } from '../utility/koishi.constants';
import {
KoishiModuleOptions,
KoishiModuleSelection,
} from '../koishi.interfaces';
} from '../utility/koishi.interfaces';
import { applySelector } from '../utility/koishi.utility';
import { Context } from 'koishi';
import { Module } from '@nestjs/core/injector/module';
......
import { ConsoleLogger, Inject, Injectable } from '@nestjs/common';
import { Logger } from 'koishi';
import { KOISHI_MODULE_OPTIONS } from '../utility/koishi.constants';
import { KoishiModuleOptions } from '../koishi.interfaces';
import { KoishiModuleOptions } from '../utility/koishi.interfaces';
@Injectable()
export class KoishiLoggerService extends ConsoleLogger {
......
......@@ -4,6 +4,7 @@ import { Argv, Command, Context, User } from 'koishi';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import {
KoishiCommandDefinition,
KoishiCommandInterceptorDef,
KoishiDoRegister,
KoishiOnContextScope,
KoishiServiceProvideSym,
......@@ -14,8 +15,10 @@ import {
CommandPutConfig,
DoRegisterConfig,
EventName,
KoishiCommandInterceptor,
KoishiCommandInterceptorRegistration,
KoishiModulePlugin,
} from '../koishi.interfaces';
} from '../utility/koishi.interfaces';
import { applySelector } from '../utility/koishi.utility';
import _ from 'lodash';
import { KoishiContextService } from './koishi-context.service';
......@@ -105,6 +108,21 @@ export class KoishiMetascanService {
}
}
private getInterceptor(interceptorDef: KoishiCommandInterceptorRegistration) {
if (typeof interceptorDef !== 'object') {
return this.moduleRef.get(interceptorDef, { strict: false });
}
return interceptorDef;
}
private addInterceptor(
command: Command,
interceptorDef: KoishiCommandInterceptorRegistration,
) {
const interceptor = this.getInterceptor(interceptorDef);
command.before((...params) => interceptor.intercept(...params));
}
private async handleInstanceRegistration(
ctx: Context,
instance: Record<string, any>,
......@@ -165,12 +183,18 @@ export class KoishiMetascanService {
);
const commandDefs = this.metaFetcher.getMetadataArray(
KoishiCommandDefinition,
methodFun(),
methodFun,
);
if (commandDefs) {
for (const commandDef of commandDefs) {
command = commandDef(command) || command;
}
for (const commandDef of commandDefs) {
command = commandDef(command) || command;
}
const interceptorDefs = this.metaFetcher.getPropertyMetadataArray(
KoishiCommandInterceptorDef,
instance,
methodKey,
);
for (const interceptorDef of interceptorDefs) {
this.addInterceptor(command, interceptorDef);
}
if (!commandData.putOptions) {
command.action((argv: Argv, ...args: any[]) =>
......
import { Context } from 'koishi';
import { Provider, Scope } from '@nestjs/common';
import { KOISHI_CONTEXT } from './utility/koishi.constants';
import { KOISHI_CONTEXT } from './koishi.constants';
export type ContextScopeTypes =
| 'guild'
......
......@@ -2,9 +2,12 @@
import {
CommandDefinitionFun,
DoRegisterConfig,
KoishiCommandInterceptor,
KoishiCommandInterceptorRegistration,
OnContextFunction,
} from '../koishi.interfaces';
} from './koishi.interfaces';
import { Context } from 'koishi';
import { Type } from '@nestjs/common';
export const KOISHI_MODULE_OPTIONS = 'KOISHI_MODULE_OPTIONS';
export const KOISHI_CONTEXT = 'KOISHI_CONTEXT';
......@@ -14,6 +17,7 @@ export const KoishiOnContextScope = 'KoishiOnContextScope';
export const KoishiDoRegister = 'KoishiDoRegister';
export const KoishiCommandDefinition = 'KoishiCommandDefinition';
export const KoishiCommandPutDef = 'KoishiCommandPutDef';
export const KoishiCommandInterceptorDef = 'KoishiCommandInterceptorDef';
export const KoishiServiceWireProperty = 'KoishiServiceWireProperty';
export const KoishiServiceWireKeys = 'KoishiServiceWireKeys';
......@@ -25,13 +29,9 @@ export interface MetadataArrayMap {
KoishiOnContextScope: OnContextFunction;
KoishiCommandDefinition: CommandDefinitionFun;
KoishiServiceProvideSym: keyof Context.Services;
KoishiCommandInterceptorDef: KoishiCommandInterceptorRegistration;
}
export interface MetadataMap {
KoishiDoRegister: DoRegisterConfig;
}
export type MetadataGenericMap = {
[K in keyof MetadataArrayMap]: MetadataArrayMap[K][];
} &
MetadataMap;
import { CustomDecorator, Inject, SetMetadata } from '@nestjs/common';
import { CustomDecorator, Inject, Type } from '@nestjs/common';
import {
KOISHI_CONTEXT,
KoishiCommandDefinition,
KoishiCommandInterceptorDef,
KoishiCommandPutDef,
KoishiDoRegister,
KoishiOnContextScope,
......@@ -9,7 +10,8 @@ import {
KoishiServiceWireKeys,
KoishiServiceWireProperty,
MetadataArrayMap,
} from './utility/koishi.constants';
MetadataMap,
} from './koishi.constants';
import {
CommandDefinitionFun,
CommandPutConfig,
......@@ -17,6 +19,13 @@ import {
DoRegisterConfig,
EventName,
GenerateMappingStruct,
KoishiCommandInterceptor,
KoishiCommandInterceptorDeclaration,
MetadataArrayValue,
MetadataArrayValueMap,
MetadataGenericMap,
MetadataKey,
MetadataMapValue,
OnContextFunction,
Selection,
} from './koishi.interfaces';
......@@ -45,44 +54,66 @@ export const InjectContextPlatform = (...values: string[]) =>
export const InjectContextUser = (...values: string[]) =>
InjectContextSpecific('user', values);
export const SetExtraMetadata = <K extends keyof MetadataArrayMap>(
// metadata extended
export function TransformMetadata<
K extends MetadataKey,
VM extends Partial<MetadataGenericMap> = MetadataGenericMap
>(
metadataKey: K,
metadataValue: MetadataArrayMap[K],
): CustomDecorator<K> => {
metadataValueFun: (oldValue: VM[K]) => VM[K],
): CustomDecorator<K> {
const decoratorFactory = (target: any, key?: any, descriptor?: any) => {
const currentMetadata: any[] =
const oldValue: VM[K] =
Reflect.getMetadata(
metadataKey,
descriptor ? descriptor.value : target,
) || [];
currentMetadata.push(metadataValue);
const newValue = metadataValueFun(oldValue);
if (descriptor) {
Reflect.defineMetadata(metadataKey, currentMetadata, descriptor.value);
Reflect.defineMetadata(metadataKey, newValue, descriptor.value);
return descriptor;
}
Reflect.defineMetadata(metadataKey, currentMetadata, target);
Reflect.defineMetadata(metadataKey, newValue, target);
return target;
};
decoratorFactory.KEY = metadataKey;
return decoratorFactory;
};
}
export const SetMetadata = <K extends keyof MetadataGenericMap>(
metadataKey: K,
metadataValue: MetadataGenericMap[K],
): CustomDecorator<K> => TransformMetadata<K>(metadataKey, () => metadataValue);
export const AppendMetadata = <K extends keyof MetadataArrayMap>(
metadataKey: K,
metadataValue: MetadataArrayMap[K],
): CustomDecorator<K> =>
TransformMetadata<K, MetadataArrayValueMap>(metadataKey, (arr) => {
const newArr = arr || [];
newArr.push(metadataValue);
return newArr;
});
export const ConcatMetadata = <K extends keyof MetadataArrayValueMap>(
metadataKey: K,
metadataValue: MetadataArrayValue<K>,
): CustomDecorator<K> =>
TransformMetadata<K, MetadataArrayValueMap>(metadataKey, (arr) =>
((arr || []) as any[]).concat(metadataValue),
);
// Register methods
export const UseMiddleware = (prepend?: boolean): MethodDecorator =>
SetMetadata<string, DoRegisterConfig<'middleware'>>(
KoishiDoRegister,
GenerateMappingStruct('middleware', prepend),
);
SetMetadata(KoishiDoRegister, GenerateMappingStruct('middleware', prepend));
export const UseEvent = (name: EventName, prepend?: boolean): MethodDecorator =>
SetMetadata<string, DoRegisterConfig<'onevent'>>(
SetMetadata(
KoishiDoRegister,
GenerateMappingStruct('onevent', { name, prepend }),
);
export const UsePlugin = (): MethodDecorator =>
SetMetadata<string, DoRegisterConfig<'plugin'>>(
KoishiDoRegister,
GenerateMappingStruct('plugin'),
);
SetMetadata(KoishiDoRegister, GenerateMappingStruct('plugin'));
export function UseCommand<D extends string>(
def: D,
......@@ -104,18 +135,15 @@ export function UseCommand(
Reflect.getMetadata(KoishiCommandPutDef, obj.constructor, key) ||
undefined;
// console.log(Reflect.getMetadata('design:paramtypes', obj, key));
const metadataDec = SetMetadata<string, DoRegisterConfig<'command'>>(
KoishiDoRegister,
{
type: 'command',
data: {
def,
desc,
config,
putOptions,
},
const metadataDec = SetMetadata(KoishiDoRegister, {
type: 'command',
data: {
def,
desc,
config,
putOptions,
},
);
});
return metadataDec(obj, key, des);
};
}
......@@ -125,7 +153,7 @@ export function UseCommand(
export const OnContext = (
ctxFun: OnContextFunction,
): MethodDecorator & ClassDecorator =>
SetExtraMetadata(KoishiOnContextScope, ctxFun);
AppendMetadata(KoishiOnContextScope, ctxFun);
export const OnUser = (...values: string[]) =>
OnContext((ctx) => ctx.user(...values));
......@@ -151,7 +179,7 @@ export const OnSelection = (selection: Selection) =>
// Command definition
export const CommandDef = (def: CommandDefinitionFun): MethodDecorator =>
SetExtraMetadata(KoishiCommandDefinition, def);
AppendMetadata(KoishiCommandDefinition, def);
export const CommandDescription = (desc: string) =>
CommandDef((cmd) => {
......@@ -249,5 +277,12 @@ export function ProvideContextService(
name: keyof Context.Services,
): ClassDecorator {
Context.service(name);
return SetExtraMetadata(KoishiServiceProvideSym, name);
return AppendMetadata(KoishiServiceProvideSym, name);
}
// Command interceptor
export const CommandInterceptors = (
...interceptors: KoishiCommandInterceptorDeclaration[]
): MethodDecorator & ClassDecorator =>
ConcatMetadata(KoishiCommandInterceptorDef, interceptors);
import { ModuleMetadata, Provider, Type } from '@nestjs/common';
import { Channel, User } from 'koishi';
import {
App,
Argv,
......@@ -10,6 +11,7 @@ import {
Plugin,
Session,
} from 'koishi';
import { MetadataArrayMap, MetadataMap } from './koishi.constants';
const selectors = [
'user',
......@@ -150,3 +152,46 @@ export type CommandPutConfig<
> = MappingStruct<CommandPutConfigMap, K>;
export type CommandDefinitionFun = (cmd: Command) => Command;
// metadata map
export type MetadataArrayValueMap = {
[K in keyof MetadataArrayMap]: MetadataArrayMap[K][];
};
export type MetadataGenericMap = MetadataArrayValueMap & MetadataMap;
export type MetadataArrayValue<
K extends keyof MetadataArrayValueMap
> = MetadataArrayValueMap[K];
export type MetadataKey = keyof MetadataArrayMap | keyof MetadataMap;
export type MetadataMapValue<
K extends MetadataKey
> = K extends keyof MetadataArrayValueMap
? MetadataArrayValue<K>
: K extends keyof MetadataMap
? MetadataMap[K]
: never;
// command interceptor
export interface KoishiCommandInterceptor<
U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
O extends {} = {}
> {
intercept: Command.Action<U, G, A, O>;
}
export type KoishiCommandInterceptorRegistration<
U extends User.Field = never,
G extends Channel.Field = never,
A extends any[] = any[],
O extends {} = {}
> =
| KoishiCommandInterceptor<U, G, A, O>
| Type<KoishiCommandInterceptor<U, G, A, O>>
| string
| symbol;
import { Context } from 'koishi';
import { ContextSelector } from '../koishi.interfaces';
import { ContextSelector } from './koishi.interfaces';
export function applySelector(
ctx: Context,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment