Commit 67f7abde authored by nanahira's avatar nanahira

interceptor

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