Commit fad0e3a3 authored by nanahira's avatar nanahira

add ProvideContextService

parent 94092e42
Pipeline #6338 passed with stages
in 6 minutes and 1 second
...@@ -201,45 +201,6 @@ export class AppModule {} ...@@ -201,45 +201,6 @@ export class AppModule {}
* `values` 作用域值。例如 `getContextProvideToken('platform', ['onebot'])` 等价于 `ctx.platform('onebot')` . * `values` 作用域值。例如 `getContextProvideToken('platform', ['onebot'])` 等价于 `ctx.platform('onebot')` .
### 注入上下文 Service
您可以使用装饰器与 Koishi 的 Service 系统进行交互。
```ts
import { Injectable, OnApplicationBootstrap } from "@nestjs/common";
import { WireContextService, UseEvent } from 'koishi-nestjs';
import { Cache } from 'koishi';
@Injectable()
export class AppService implements OnApplicationBootstrap {
constructor(@InjectContextGuild('1111111111') private ctx: Context) {
}
// 注入 Service
@WireContextService('cache')
private cache2: Cache;
// 成员变量名与 Service 名称一致时 name 可省略。
@WireContextService()
private cache: Cache;
async onApplicationBootstrap() {
// 可以在 onApplicationBootstrap 访问上下文 Service
const user = this.cache.get('user', '111111111');
}
@UseEvent('service/cache')
async onCacheAvailable() {
// 建议监听 Service 事件
const user = this.cache.get('user', '111111112');
}
}
```
#### 定义
* `@WireContextService(name?: string)` 在提供者类某一属性注入特定上下文 Service 。 `name` 默认为类方法名。
## 使用装饰器注册 Koishi 指令 ## 使用装饰器注册 Koishi 指令
您也可以在完全不注入任何 Koishi 上下文的情况下注册 Koishi 指令,只需要在提供者类中使用装饰器即可。下面是一个例子。 您也可以在完全不注入任何 Koishi 上下文的情况下注册 Koishi 指令,只需要在提供者类中使用装饰器即可。下面是一个例子。
...@@ -341,6 +302,77 @@ Koishi-Nest 使用一组装饰器进行描述指令的行为。这些装饰器 ...@@ -341,6 +302,77 @@ Koishi-Nest 使用一组装饰器进行描述指令的行为。这些装饰器
* `@CommandDef((cmd: Command) => Command)` 手动定义指令信息,用于 Koishi-Nest 不支持的指令类型。 * `@CommandDef((cmd: Command) => Command)` 手动定义指令信息,用于 Koishi-Nest 不支持的指令类型。
## 上下文 Service 交互
您可以使用装饰器与 Koishi 的 Service 系统进行交互。
### 注入上下文 Service
注入的 Service 通常来自 Koishi 插件,或是自行提供的 Service 。
```ts
import { Injectable, OnApplicationBootstrap } from "@nestjs/common";
import { WireContextService, UseEvent } from 'koishi-nestjs';
import { Cache } from 'koishi';
@Injectable()
export class AppService implements OnApplicationBootstrap {
constructor(@InjectContextGuild('1111111111') private ctx: Context) {
}
// 注入 Service
@WireContextService('cache')
private cache2: Cache;
// 成员变量名与 Service 名称一致时 name 可省略。
@WireContextService()
private cache: Cache;
async onApplicationBootstrap() {
// 可以在 onApplicationBootstrap 访问上下文 Service
const user = this.cache.get('user', '111111111');
}
@UseEvent('service/cache')
async onCacheAvailable() {
// 建议监听 Service 事件
const user = this.cache.get('user', '111111112');
}
}
```
### 提供上下文 Service
您也可以使用 Nest 提供者提供 Koishi 需要的 Service 的实现。
```ts
import { Injectable } from '@nestjs/common';
import { ProvideContextService } from 'koishi-nestjs';
declare module 'koishi' {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Context {
interface Services {
testProvide: TestProvideService;
}
}
}
// `@ProvideContextService(name)` 装饰器会自动完成 `Context.service(name)` 的声明操作
@Injectable()
@ProvideContextService('testProvide')
export class TestProvideService {
// 该类会作为 Koishi 的 Service 供其他 Koishi 插件进行引用
}
```
### 定义
* `@WireContextService(name?: string)` 在 Nest 提供者类某一属性注入特定上下文 Service 。 `name` 默认为类方法名。
* `@ProvideContextService(name: string)` 使用某一 Nest 提供者类提供 Koishi 上下文 Service 。会自动完成 Koishi 的 Service 声明操作。
## 复用性 ## 复用性
Nest 提供了模块系统,我们可以编写功能模块,并在功能模块进行 Koishi 指令注册操作,从而进行代码的有效复用。 Nest 提供了模块系统,我们可以编写功能模块,并在功能模块进行 Koishi 指令注册操作,从而进行代码的有效复用。
......
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiDoRegister, KoishiDoRegister,
KoishiOnContextScope, KoishiOnContextScope,
KoishiServiceProvideSym,
KoishiServiceWireKeys, KoishiServiceWireKeys,
KoishiServiceWireProperty, KoishiServiceWireProperty,
} from './utility/koishi.constants'; } from './utility/koishi.constants';
...@@ -16,7 +17,7 @@ import { ...@@ -16,7 +17,7 @@ import {
OnContextFunction, OnContextFunction,
Selection, Selection,
} from './koishi.interfaces'; } from './koishi.interfaces';
import { Argv, Command } from 'koishi'; import { Argv, Command, Context } from 'koishi';
import { import {
ContextScopeTypes, ContextScopeTypes,
getContextProvideToken, getContextProvideToken,
...@@ -170,3 +171,8 @@ export function WireContextService(name?: string): PropertyDecorator { ...@@ -170,3 +171,8 @@ export function WireContextService(name?: string): PropertyDecorator {
); );
}; };
} }
export function ProvideContextService(name: string): ClassDecorator {
Context.service(name as keyof Context.Services);
return SetMetadata(KoishiServiceProvideSym, name);
}
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiDoRegister, KoishiDoRegister,
KoishiOnContextScope, KoishiOnContextScope,
KoishiServiceProvideSym,
KoishiServiceWireKeys, KoishiServiceWireKeys,
KoishiServiceWireProperty, KoishiServiceWireProperty,
} from '../utility/koishi.constants'; } from '../utility/koishi.constants';
...@@ -156,7 +157,17 @@ export class KoishiMetascanService { ...@@ -156,7 +157,17 @@ export class KoishiMetascanService {
); );
} }
private scanInstanceForService(ctx: Context, instance: any) { private scanInstanceForProvidingContextService(ctx: Context, instance: any) {
const providingServiceName = this.reflector.get(
KoishiServiceProvideSym,
instance.constructor,
);
if (providingServiceName) {
ctx[providingServiceName] = instance;
}
}
private scanInstanceForWireContextService(ctx: Context, instance: any) {
const instanceClass = instance.constructor; const instanceClass = instance.constructor;
const properties: string[] = Reflect.getMetadata( const properties: string[] = Reflect.getMetadata(
KoishiServiceWireKeys, KoishiServiceWireKeys,
...@@ -189,7 +200,9 @@ export class KoishiMetascanService { ...@@ -189,7 +200,9 @@ export class KoishiMetascanService {
const moduleCtx = this.ctxService.getModuleCtx(ctx, module); const moduleCtx = this.ctxService.getModuleCtx(ctx, module);
const allProviders = this.getAllActiveProvidersFromModule(module); const allProviders = this.getAllActiveProvidersFromModule(module);
for (const provider of allProviders) { for (const provider of allProviders) {
return this.scanInstanceForService(moduleCtx, provider.instance); const instance = provider.instance;
this.scanInstanceForWireContextService(moduleCtx, instance);
this.scanInstanceForProvidingContextService(moduleCtx, instance);
} }
} }
} }
...@@ -203,7 +216,6 @@ export class KoishiMetascanService { ...@@ -203,7 +216,6 @@ export class KoishiMetascanService {
const allProviders = this.getAllActiveProvidersFromModule(module); const allProviders = this.getAllActiveProvidersFromModule(module);
return allProviders.map((wrapper: InstanceWrapper) => { return allProviders.map((wrapper: InstanceWrapper) => {
const { instance } = wrapper; const { instance } = wrapper;
this.scanInstanceForService(moduleCtx, instance);
const prototype = Object.getPrototypeOf(instance); const prototype = Object.getPrototypeOf(instance);
return this.metadataScanner.scanFromPrototype( return this.metadataScanner.scanFromPrototype(
instance, instance,
......
...@@ -9,3 +9,4 @@ export const KoishiCommandDefinition = 'KoishiCommandDefinition'; ...@@ -9,3 +9,4 @@ export const KoishiCommandDefinition = 'KoishiCommandDefinition';
export const KoishiServiceWireProperty = Symbol('KoishiServiceWireProperty'); export const KoishiServiceWireProperty = Symbol('KoishiServiceWireProperty');
export const KoishiServiceWireKeys = Symbol('KoishiServiceWireKeys'); export const KoishiServiceWireKeys = Symbol('KoishiServiceWireKeys');
export const KoishiServiceProvideSym = 'KoishiServiceProvideSym';
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