Commit 0aefcf6e authored by nanahira's avatar nanahira

move

parent c7aec622
...@@ -85,7 +85,7 @@ Koishi-Nest 的配置项和 Koishi 配置项一致,参照 [Koishi 文档](http ...@@ -85,7 +85,7 @@ Koishi-Nest 的配置项和 Koishi 配置项一致,参照 [Koishi 文档](http
* `options` Koishi 插件配置。等同于 `ctx.plugin(plugin, options)` * `options` Koishi 插件配置。等同于 `ctx.plugin(plugin, options)`
* `select` 插件选择器,定义插件的作用上下文。定义参照 [Koishi 文档](https://koishi.js.org/v4/guide/plugin/context.html#%E5%9C%A8%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%AD%E4%BD%BF%E7%94%A8%E9%80%89%E6%8B%A9%E5%99%A8) 的写法。 * `select` 插件选择器,定义插件的作用上下文。定义参照 [Koishi 文档](https://koishi.js.org/v4/guide/plugin/context.html#%E5%9C%A8%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%AD%E4%BD%BF%E7%94%A8%E9%80%89%E6%8B%A9%E5%99%A8) 的写法。
* `isGlobal`: `boolean` 默认 `false` 。指示 Koishi-Nest 模块是否应被注册为全局模块。 **异步配置该项应写入异步配置项中。** 关于全局模块请参考 [Nest.js 文档](https://docs.nestjs.cn/8/modules?id=%e5%85%a8%e5%b1%80%e6%a8%a1%e5%9d%97) * `isGlobal`: `boolean` 默认 `false` 。指示 Koishi-Nest 模块是否应被注册为全局模块。当 Koishi-Nest 需要被其他模块引用的情况下,需要使用该选项。 **异步配置该项应写入异步配置项中。** 关于全局模块请参考 [Nest.js 文档](https://docs.nestjs.cn/8/modules?id=%e5%85%a8%e5%b1%80%e6%a8%a1%e5%9d%97)
插件的使用可以参考 [Koishi 文档](https://koishi.js.org/v4/guide/plugin/plugin.html) 插件的使用可以参考 [Koishi 文档](https://koishi.js.org/v4/guide/plugin/plugin.html)
...@@ -301,6 +301,7 @@ Koishi-Nest 使用一组装饰器进行描述指令的行为。这些装饰器 ...@@ -301,6 +301,7 @@ Koishi-Nest 使用一组装饰器进行描述指令的行为。这些装饰器
* `PluginDef(plugin: Plugin, options?: PluginConfig, select?: Selection)` 生成指令注册定义。用于 Koishi-Nest 启动参数和 `@UsePlugin()` 返回值。指令注册定义成员参数如下。 * `PluginDef(plugin: Plugin, options?: PluginConfig, select?: Selection)` 生成指令注册定义。用于 Koishi-Nest 启动参数和 `@UsePlugin()` 返回值。指令注册定义成员参数如下。
* `plugin` Koishi 插件。 * `plugin`: Koishi 插件。
* `options` Koishi 插件配置。等同于 `ctx.plugin(plugin, options)` * `options`: Koishi 插件配置。等同于 `ctx.plugin(plugin, options)`
* `select` 插件选择器,定义插件的作用上下文。定义参照 [Koishi 文档](https://koishi.js.org/v4/guide/plugin/context.html#%E5%9C%A8%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%AD%E4%BD%BF%E7%94%A8%E9%80%89%E6%8B%A9%E5%99%A8) 的写法。 * `select`: 插件选择器,定义插件的作用上下文。定义参照 [Koishi 文档](https://koishi.js.org/v4/guide/plugin/context.html#%E5%9C%A8%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%AD%E4%BD%BF%E7%94%A8%E9%80%89%E6%8B%A9%E5%99%A8) 的写法。
* `useSelector`: `(ctx: Context) => Context` 使用函数进行选择。该函数接受1个 Context 参数,同时也需要返回1个 Context 对象。
import { Context } from 'koishi'; import { Context } from 'koishi';
import { Provider, Scope } from '@nestjs/common'; import { Provider, Scope } from '@nestjs/common';
import { KOISHI_CONTEXT } from './koishi.constants'; import { KOISHI_CONTEXT } from './utility/koishi.constants';
export type ContextScopeTypes = export type ContextScopeTypes =
| 'guild' | 'guild'
...@@ -26,7 +26,7 @@ function createContextProvider( ...@@ -26,7 +26,7 @@ function createContextProvider(
return { return {
provide: constructProvideToken(scopeType, values), provide: constructProvideToken(scopeType, values),
inject: [KOISHI_CONTEXT], inject: [KOISHI_CONTEXT],
// scope: Scope.TRANSIENT, scope: Scope.TRANSIENT,
useFactory: (ctx: Context) => ctx[scopeType](...values), useFactory: (ctx: Context) => ctx[scopeType](...values),
}; };
} }
......
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiDoRegister, KoishiDoRegister,
KoishiOnContextScope, KoishiOnContextScope,
} from './koishi.constants'; } from './utility/koishi.constants';
import { import {
CommandDefinitionFun, CommandDefinitionFun,
ContextFunction, ContextFunction,
......
...@@ -20,10 +20,14 @@ export interface Selection extends BaseSelection { ...@@ -20,10 +20,14 @@ export interface Selection extends BaseSelection {
$not?: Selection; $not?: Selection;
} }
export interface KoishiModulePlugin<T extends Plugin> { export interface ContextSelector {
select?: Selection;
useSelector?: OnContextFunction;
}
export interface KoishiModulePlugin<T extends Plugin> extends ContextSelector {
plugin: T; plugin: T;
options?: boolean | Plugin.Config<T>; options?: boolean | Plugin.Config<T>;
select?: Selection;
} }
export function PluginDef<T extends Plugin>( export function PluginDef<T extends Plugin>(
...@@ -34,6 +38,10 @@ export function PluginDef<T extends Plugin>( ...@@ -34,6 +38,10 @@ export function PluginDef<T extends Plugin>(
return { plugin, options, select }; return { plugin, options, select };
} }
export interface KoishiModuleSelection extends ContextSelector {
module: Type<any>;
}
export interface WhetherGlobalOption { export interface WhetherGlobalOption {
isGlobal?: boolean; isGlobal?: boolean;
} }
...@@ -42,6 +50,7 @@ export interface KoishiModuleOptions extends App.Config, WhetherGlobalOption { ...@@ -42,6 +50,7 @@ export interface KoishiModuleOptions extends App.Config, WhetherGlobalOption {
usePlugins?: KoishiModulePlugin<Plugin>[]; usePlugins?: KoishiModulePlugin<Plugin>[];
loggerPrefix?: string; loggerPrefix?: string;
loggerColor?: number; loggerColor?: number;
moduleSelection?: KoishiModuleSelection[];
} }
export interface KoishiModuleOptionsFactory { export interface KoishiModuleOptionsFactory {
......
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
Module, Module,
NestModule, NestModule,
Provider, Provider,
Scope,
} from '@nestjs/common'; } from '@nestjs/common';
import { import {
KoishiModuleAsyncOptions, KoishiModuleAsyncOptions,
...@@ -11,20 +12,27 @@ import { ...@@ -11,20 +12,27 @@ import {
KoishiModuleOptionsFactory, KoishiModuleOptionsFactory,
} from './koishi.interfaces'; } from './koishi.interfaces';
import { KoishiService } from './koishi.service'; import { KoishiService } from './koishi.service';
import { KOISHI_CONTEXT, KOISHI_MODULE_OPTIONS } from './koishi.constants'; import {
import { KoishiMiddleware } from './koishi.middleware'; KOISHI_CONTEXT,
KOISHI_MODULE_OPTIONS,
} from './utility/koishi.constants';
import { KoishiMiddleware } from './providers/koishi.middleware';
import { createServer } from 'http'; import { createServer } from 'http';
import { AddressInfo } from 'net'; import { AddressInfo } from 'net';
import { KoishiLoggerService } from './koishi-logger.service'; import { KoishiLoggerService } from './providers/koishi-logger.service';
import { KoishiMetascanService } from './koishi-metascan.service'; import { KoishiMetascanService } from './providers/koishi-metascan.service';
import { DiscoveryModule } from '@nestjs/core'; import { DiscoveryModule, INQUIRER } from '@nestjs/core';
import { Context } from 'koishi'; import { Context } from 'koishi';
import { contextsToProvide } from './koishi-context.factory'; import { contextsToProvide } from './koishi-context.factory';
import { KoishiInjectionService } from './providers/koishi-injection.service';
import { KoishiContextService } from './providers/koishi-context.service';
const koishiContextProvider: Provider<Context> = { const koishiContextProvider: Provider<Context> = {
provide: KOISHI_CONTEXT, provide: KOISHI_CONTEXT,
inject: [KoishiService], inject: [KoishiInjectionService, INQUIRER],
useFactory: (koishiApp: KoishiService) => koishiApp.any(), scope: Scope.TRANSIENT,
useFactory: (injectionService: KoishiInjectionService, inquirer: any) =>
injectionService.getInjectContext(inquirer),
}; };
@Module({ @Module({
...@@ -55,6 +63,8 @@ const koishiContextProvider: Provider<Context> = { ...@@ -55,6 +63,8 @@ const koishiContextProvider: Provider<Context> = {
KoishiLoggerService, KoishiLoggerService,
KoishiMetascanService, KoishiMetascanService,
koishiContextProvider, koishiContextProvider,
KoishiContextService,
KoishiInjectionService,
KoishiMiddleware, KoishiMiddleware,
], ],
exports: [KoishiService, koishiContextProvider], exports: [KoishiService, koishiContextProvider],
......
...@@ -10,7 +10,8 @@ import { Server } from 'http'; ...@@ -10,7 +10,8 @@ import { Server } from 'http';
import Koa from 'koa'; import Koa from 'koa';
import KoaRouter from '@koa/router'; import KoaRouter from '@koa/router';
import KoaBodyParser from 'koa-bodyparser'; import KoaBodyParser from 'koa-bodyparser';
import { KoishiMetascanService } from './koishi-metascan.service'; import { KoishiMetascanService } from './providers/koishi-metascan.service';
import { applySelector } from './utility/koishi.utility';
@Injectable() @Injectable()
export class KoishiService export class KoishiService
...@@ -51,9 +52,7 @@ export class KoishiService ...@@ -51,9 +52,7 @@ export class KoishiService
await this.setHttpServer(); await this.setHttpServer();
if (this.koishiModuleOptions.usePlugins) { if (this.koishiModuleOptions.usePlugins) {
for (const pluginDesc of this.koishiModuleOptions.usePlugins) { for (const pluginDesc of this.koishiModuleOptions.usePlugins) {
const ctx = pluginDesc.select const ctx = applySelector(this, pluginDesc);
? this.select(pluginDesc.select)
: this.any();
ctx.plugin(pluginDesc.plugin, pluginDesc.options); ctx.plugin(pluginDesc.plugin, pluginDesc.options);
} }
} }
......
import { Inject, Injectable, Type } from '@nestjs/common';
import { KOISHI_MODULE_OPTIONS } from '../utility/koishi.constants';
import {
KoishiModuleOptions,
KoishiModuleSelection,
} from '../koishi.interfaces';
import { applySelector } from '../utility/koishi.utility';
import { Context } from 'koishi';
import { Module } from '@nestjs/core/injector/module';
@Injectable()
export class KoishiContextService {
moduleSelections = new Map<Type<any>, KoishiModuleSelection>();
constructor(@Inject(KOISHI_MODULE_OPTIONS) options: KoishiModuleOptions) {
if (options.moduleSelection) {
for (const selection of options.moduleSelection) {
this.moduleSelections.set(selection.module, selection);
}
}
}
getModuleCtx(ctx: Context, module: Module) {
const moduleSelection = this.moduleSelections.get(module.metatype);
if (moduleSelection) {
return applySelector(ctx, moduleSelection);
} else {
return ctx;
}
}
}
import { Injectable } from '@nestjs/common';
import { KoishiService } from '../koishi.service';
import { KoishiContextService } from './koishi-context.service';
import { ModulesContainer } from '@nestjs/core';
@Injectable()
export class KoishiInjectionService {
constructor(
private readonly koishi: KoishiService,
private readonly ctxService: KoishiContextService,
private moduleContainer: ModulesContainer,
) {}
getInjectContext(inquerier: string | any) {
let ctx = this.koishi.any();
const token =
typeof inquerier === 'string' ? inquerier : inquerier.constructor;
for (const module of this.moduleContainer.values()) {
const targetProvider = module.getProviderByKey(token);
if (targetProvider) {
ctx = this.ctxService.getModuleCtx(ctx, module);
}
}
return ctx;
}
}
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 './koishi.constants'; import { KOISHI_MODULE_OPTIONS } from '../utility/koishi.constants';
import { KoishiModuleOptions } from './koishi.interfaces'; import { KoishiModuleOptions } from '../koishi.interfaces';
@Injectable() @Injectable()
export class KoishiLoggerService extends ConsoleLogger { export class KoishiLoggerService extends ConsoleLogger {
......
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { import {
AbstractHttpAdapter, AbstractHttpAdapter,
DiscoveryService,
HttpAdapterHost, HttpAdapterHost,
MetadataScanner, MetadataScanner,
ModuleRef, ModuleRef,
ModulesContainer,
Reflector, Reflector,
} from '@nestjs/core'; } from '@nestjs/core';
import { Argv, Command, Context } from 'koishi'; import { Argv, Command, Context } from 'koishi';
...@@ -13,7 +13,7 @@ import { ...@@ -13,7 +13,7 @@ import {
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiDoRegister, KoishiDoRegister,
KoishiOnContextScope, KoishiOnContextScope,
} from './koishi.constants'; } from '../utility/koishi.constants';
import { import {
CommandDefinitionFun, CommandDefinitionFun,
ContextFunction, ContextFunction,
...@@ -21,15 +21,19 @@ import { ...@@ -21,15 +21,19 @@ import {
EventNameAndPrepend, EventNameAndPrepend,
KoishiModulePlugin, KoishiModulePlugin,
OnContextFunction, OnContextFunction,
} from './koishi.interfaces'; } from '../koishi.interfaces';
import { applySelector } from '../utility/koishi.utility';
import _ from 'lodash';
import { KoishiContextService } from './koishi-context.service';
@Injectable() @Injectable()
export class KoishiMetascanService { export class KoishiMetascanService {
constructor( constructor(
private readonly discoveryService: DiscoveryService,
private readonly metadataScanner: MetadataScanner, private readonly metadataScanner: MetadataScanner,
private readonly reflector: Reflector, private readonly reflector: Reflector,
private readonly moduleRef: ModuleRef, private readonly moduleRef: ModuleRef,
private readonly moduleContainer: ModulesContainer,
private readonly ctxService: KoishiContextService,
) {} ) {}
getHttpAdapter(): AbstractHttpAdapter { getHttpAdapter(): AbstractHttpAdapter {
...@@ -95,9 +99,7 @@ export class KoishiMetascanService { ...@@ -95,9 +99,7 @@ export class KoishiMetascanService {
if (!pluginDesc || !pluginDesc.plugin) { if (!pluginDesc || !pluginDesc.plugin) {
throw new Error(`Invalid plugin from method ${methodKey}.`); throw new Error(`Invalid plugin from method ${methodKey}.`);
} }
const pluginCtx = pluginDesc.select const pluginCtx = applySelector(baseContext, pluginDesc);
? baseContext.select(pluginDesc.select)
: baseContext.any();
pluginCtx.plugin(pluginDesc.plugin, pluginDesc.options); pluginCtx.plugin(pluginDesc.plugin, pluginDesc.options);
break; break;
case 'command': case 'command':
...@@ -124,22 +126,29 @@ export class KoishiMetascanService { ...@@ -124,22 +126,29 @@ export class KoishiMetascanService {
} }
async registerContext(ctx: Context) { async registerContext(ctx: Context) {
const providers = this.discoveryService.getProviders(); const modules = Array.from(this.moduleContainer.values());
const controllers = this.discoveryService.getControllers();
await Promise.all( await Promise.all(
[...providers, ...controllers] _.flatten(
.filter((wrapper) => wrapper.isDependencyTreeStatic()) modules.map((module) => {
.filter((wrapper) => wrapper.instance) const moduleCtx = this.ctxService.getModuleCtx(ctx, module);
.map((wrapper: InstanceWrapper) => { return [
const { instance } = wrapper; ...Array.from(module.routes.values()),
const prototype = Object.getPrototypeOf(instance); ...Array.from(module.providers.values()),
return this.metadataScanner.scanFromPrototype( ]
instance, .filter((wrapper) => wrapper.isDependencyTreeStatic())
prototype, .filter((wrapper) => wrapper.instance)
(methodKey: string) => .map((wrapper: InstanceWrapper) => {
this.handleInstance(ctx, instance, methodKey), const { instance } = wrapper;
); const prototype = Object.getPrototypeOf(instance);
return this.metadataScanner.scanFromPrototype(
instance,
prototype,
(methodKey: string) =>
this.handleInstance(moduleCtx, instance, methodKey),
);
});
}), }),
),
); );
} }
} }
import { Injectable, NestMiddleware, OnModuleInit } from '@nestjs/common'; import { Injectable, NestMiddleware, OnModuleInit } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express';
import { KoishiService } from './koishi.service'; import { KoishiService } from '../koishi.service';
import { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware'; import { createProxyMiddleware, RequestHandler } from 'http-proxy-middleware';
@Injectable() @Injectable()
......
import { Context } from 'koishi';
import { ContextSelector } from '../koishi.interfaces';
export function applySelector(
ctx: Context,
selector: ContextSelector,
): Context {
if (!selector) {
return ctx;
}
let targetCtx = ctx;
if (selector.select) {
targetCtx = targetCtx.select(selector.select);
}
if (selector.useSelector) {
targetCtx = selector.useSelector(targetCtx) || targetCtx;
}
return targetCtx;
}
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