Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
K
Koishi Nestjs
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
3rdeye
Koishi Nestjs
Commits
7af29461
"tests/media/svn:/svn.code.sf.net/p/irrlicht/code/trunk@3204" did not exist on "303e5c16dcd1acb8c20d9ee1709028da3984edca"
Commit
7af29461
authored
Oct 31, 2021
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
interceptor in context
parent
489d798f
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
246 additions
and
23 deletions
+246
-23
README.md
README.md
+55
-0
src/koishi-interceptor-manager/koishi-interceptor-manager.service.ts
...interceptor-manager/koishi-interceptor-manager.service.ts
+35
-0
src/koishi.module.ts
src/koishi.module.ts
+2
-0
src/koishi.service.ts
src/koishi.service.ts
+50
-2
src/providers/koishi-injection.service.ts
src/providers/koishi-injection.service.ts
+16
-2
src/providers/koishi-metascan.service.ts
src/providers/koishi-metascan.service.ts
+17
-19
src/utility/koishi.interfaces.ts
src/utility/koishi.interfaces.ts
+1
-0
src/utility/replaced-context.ts
src/utility/replaced-context.ts
+70
-0
No files found.
README.md
View file @
7af29461
...
...
@@ -89,6 +89,8 @@ Koishi-Nest 的配置项和 Koishi 配置项一致,参照 [Koishi 文档](http
*
`module`
Nest 模块名。
*
上下文选择器见本文
**上下文选择器**
部分。
*
`globalInterceptors`
全局命令拦截器,详见
**命令拦截器**
部分。
插件的使用可以参考
[
Koishi 文档
](
https://koishi.js.org/v4/guide/plugin/plugin.html
)
。
`moduleSelection`
的使用见本文
**复用性**
部分。
...
...
@@ -333,6 +335,59 @@ Koishi-Nest 使用一组装饰器进行描述指令的行为。这些装饰器
*
`@PutUserName(useDatabase: boolean = true)`
注入当前用户的用户名。
*
`useDatabase`
是否尝试从数据库获取用户名。
### 指令拦截器
和 Koishi 中的
[
`command.before`
](
https://koishi.js.org/v4/guide/message/command.html#%E4%BD%BF%E7%94%A8%E6%A3%80%E6%9F%A5%E5%99%A8
)
对应,Koishi-Nest 提供了
**指令拦截器**
,便于在指令运行之前进行一些操作,如参数检查,记录日志等。
#### 定义
指令拦截器需要实现
`KoishiCommandInterceptor`
接口,提供
`intercept`
方法。该方法的参数与
`command.before`
的回调函数一致。
> 不要将指令拦截器与 Nest.js 的拦截器混淆。
下面是一个指令拦截器的例子。
```
ts
import
{
KoishiCommandInterceptor
}
from
"
koishi-nestjs
"
;
export
class
MyCommandInterceptor
implements
KoishiCommandInterceptor
{
intercept
(
argv
:
Argv
,
arg1
:
string
)
{
if
(
arg1
.
startsWith
(
'
foo
'
))
{
return
'
Intercepted!
'
;
}
}
}
```
#### 注册
要注册拦截器,只需要在指令对应的提供者方法或提供者本人使用
`@CommandInterceptors`
装饰器即可。也可以指定多个拦截器。
其中,在注册过拦截器的提供者类中,使用
`@InjectContext()`
或类似方法注入的上下文对象,也会应用拦截器。
> 这些上下文内安装的 Koishi 插件不会应用拦截器。
```
ts
import
{
InjectContext
}
from
'
koishi-nestjs
'
;
import
{
Context
}
from
'
koishi
'
;
@
Injectable
()
// 可以在提供者类中指定上下文选择器,等价于 `ctx.guild('111111111')`
@
CommandInterceptors
(
MyGlobalInterceptor
)
export
class
AppService
{
// 这里的 Koishi 上下文注册的任何指令也会应用拦截器
constructor
(@
InjectContext
()
private
ctx
:
Context
)
{}
@
UseCommand
(
'
my-echo <content:string>
'
)
@
CommandInterceptors
(
MyInterceptor1
,
MyInterceptor2
)
// 可以指定多个拦截器。
testEchoCommand
(@
PutArgv
()
argv
:
Argv
,
@
PutArg
(
0
)
content
:
string
)
{
return
content
;
}
}
```
也可以在 Koishi-Nest 启动配置中,使用
`globalInterceptors`
方法注册拦截器。
## 上下文 Service 交互
...
...
src/koishi-interceptor-manager/koishi-interceptor-manager.service.ts
0 → 100644
View file @
7af29461
import
{
Injectable
}
from
'
@nestjs/common
'
;
import
{
ModuleRef
}
from
'
@nestjs/core
'
;
import
{
KoishiCommandInterceptorRegistration
}
from
'
../utility/koishi.interfaces
'
;
import
{
Command
}
from
'
koishi
'
;
@
Injectable
()
export
class
KoishiInterceptorManagerService
{
constructor
(
private
readonly
moduleRef
:
ModuleRef
)
{}
getInterceptor
(
interceptorDef
:
KoishiCommandInterceptorRegistration
)
{
if
(
typeof
interceptorDef
!==
'
object
'
)
{
return
this
.
moduleRef
.
get
(
interceptorDef
,
{
strict
:
false
});
}
return
interceptorDef
;
}
addInterceptor
(
command
:
Command
,
interceptorDef
:
KoishiCommandInterceptorRegistration
,
)
{
const
interceptor
=
this
.
getInterceptor
(
interceptorDef
);
command
.
before
((...
params
)
=>
interceptor
.
intercept
(...
params
));
}
addInterceptors
(
command
:
Command
,
interceptorDefs
:
KoishiCommandInterceptorRegistration
[],
)
{
if
(
!
interceptorDefs
)
{
return
;
}
interceptorDefs
.
forEach
((
interceptorDef
)
=>
this
.
addInterceptor
(
command
,
interceptorDef
),
);
}
}
src/koishi.module.ts
View file @
7af29461
...
...
@@ -27,6 +27,7 @@ import { KoishiContextService } from './providers/koishi-context.service';
import
{
KoishiHttpDiscoveryService
}
from
'
./koishi-http-discovery/koishi-http-discovery.service
'
;
import
{
KoishiWebsocketGateway
}
from
'
./providers/koishi-websocket.gateway
'
;
import
{
KoishiMetadataFetcherService
}
from
'
./koishi-metadata-fetcher/koishi-metadata-fetcher.service
'
;
import
{
KoishiInterceptorManagerService
}
from
'
./koishi-interceptor-manager/koishi-interceptor-manager.service
'
;
const
koishiContextProvider
:
Provider
<
Context
>
=
{
provide
:
KOISHI_CONTEXT
,
...
...
@@ -47,6 +48,7 @@ const koishiContextProvider: Provider<Context> = {
KoishiInjectionService
,
KoishiHttpDiscoveryService
,
KoishiMetadataFetcherService
,
KoishiInterceptorManagerService
,
],
exports
:
[
KoishiService
,
koishiContextProvider
],
})
...
...
src/koishi.service.ts
View file @
7af29461
import
{
App
,
Router
}
from
'
koishi
'
;
import
{
App
,
Command
,
Context
,
Router
}
from
'
koishi
'
;
import
{
Inject
,
Injectable
,
...
...
@@ -6,7 +6,10 @@ import {
OnModuleDestroy
,
OnModuleInit
,
}
from
'
@nestjs/common
'
;
import
{
KoishiModuleOptions
}
from
'
./utility/koishi.interfaces
'
;
import
{
KoishiCommandInterceptorRegistration
,
KoishiModuleOptions
,
}
from
'
./utility/koishi.interfaces
'
;
import
{
Server
}
from
'
http
'
;
import
Koa
from
'
koa
'
;
import
KoaBodyParser
from
'
koa-bodyparser
'
;
...
...
@@ -15,6 +18,7 @@ import { applySelector } from './utility/koishi.utility';
import
{
KOISHI_MODULE_OPTIONS
}
from
'
./utility/koishi.constants
'
;
import
{
KoishiLoggerService
}
from
'
./providers/koishi-logger.service
'
;
import
{
KoishiHttpDiscoveryService
}
from
'
./koishi-http-discovery/koishi-http-discovery.service
'
;
import
{
Filter
,
ReplacedContext
}
from
'
./utility/replaced-context
'
;
@
Injectable
()
export
class
KoishiService
...
...
@@ -71,4 +75,48 @@ export class KoishiService
async
onModuleDestroy
()
{
await
this
.
stop
();
}
addInterceptors
(
command
:
Command
,
interceptorDefs
:
KoishiCommandInterceptorRegistration
[],
)
{
return
this
.
metascan
.
addInterceptors
(
command
,
interceptorDefs
);
}
private
cloneContext
(
filter
:
Filter
,
interceptors
:
KoishiCommandInterceptorRegistration
[]
=
[],
):
Context
{
return
new
ReplacedContext
(
filter
,
this
,
null
,
[
...(
this
.
koishiModuleOptions
.
globalInterceptors
||
[]),
...
interceptors
,
]);
}
withInterceptors
(
interceptors
:
KoishiCommandInterceptorRegistration
[])
{
return
this
.
cloneContext
(()
=>
true
,
interceptors
);
}
any
()
{
return
this
.
cloneContext
(()
=>
true
);
}
never
()
{
return
this
.
cloneContext
(()
=>
false
);
}
union
(
arg
:
Filter
|
Context
)
{
const
filter
=
typeof
arg
===
'
function
'
?
arg
:
arg
.
filter
;
return
this
.
cloneContext
((
s
)
=>
this
.
filter
(
s
)
||
filter
(
s
));
}
intersect
(
arg
:
Filter
|
Context
)
{
const
filter
=
typeof
arg
===
'
function
'
?
arg
:
arg
.
filter
;
return
this
.
cloneContext
((
s
)
=>
this
.
filter
(
s
)
&&
filter
(
s
));
}
except
(
arg
:
Filter
|
Context
)
{
const
filter
=
typeof
arg
===
'
function
'
?
arg
:
arg
.
filter
;
return
this
.
cloneContext
((
s
)
=>
this
.
filter
(
s
)
&&
!
filter
(
s
));
}
}
src/providers/koishi-injection.service.ts
View file @
7af29461
import
{
Injectable
}
from
'
@nestjs/common
'
;
import
{
Inject
,
Inject
able
}
from
'
@nestjs/common
'
;
import
{
KoishiService
}
from
'
../koishi.service
'
;
import
{
KoishiContextService
}
from
'
./koishi-context.service
'
;
import
{
ModulesContainer
}
from
'
@nestjs/core
'
;
import
{
KoishiMetadataFetcherService
}
from
'
../koishi-metadata-fetcher/koishi-metadata-fetcher.service
'
;
import
{
KOISHI_MODULE_OPTIONS
,
KoishiCommandInterceptorDef
,
}
from
'
../utility/koishi.constants
'
;
import
{
KoishiModuleOptions
}
from
'
../utility/koishi.interfaces
'
;
import
{
Context
}
from
'
koishi
'
;
@
Injectable
()
export
class
KoishiInjectionService
{
constructor
(
private
readonly
koishi
:
KoishiService
,
private
readonly
ctxService
:
KoishiContextService
,
private
readonly
metaFetcher
:
KoishiMetadataFetcherService
,
private
moduleContainer
:
ModulesContainer
,
@
Inject
(
KOISHI_MODULE_OPTIONS
)
private
readonly
koishiModuleOptions
:
KoishiModuleOptions
,
)
{}
getInjectContext
(
inquerier
:
string
|
any
)
{
let
ctx
=
this
.
koishi
.
any
();
const
token
=
typeof
inquerier
===
'
string
'
?
inquerier
:
inquerier
.
constructor
;
const
interceptors
=
this
.
metaFetcher
.
getMetadataArray
(
KoishiCommandInterceptorDef
,
token
,
);
let
ctx
:
Context
=
this
.
koishi
.
withInterceptors
(
interceptors
);
for
(
const
module
of
this
.
moduleContainer
.
values
())
{
if
(
module
.
hasProvider
(
token
)
||
module
.
controllers
.
has
(
token
))
{
ctx
=
this
.
ctxService
.
getModuleCtx
(
ctx
,
module
);
...
...
src/providers/koishi-metascan.service.ts
View file @
7af29461
import
{
Injectable
}
from
'
@nestjs/common
'
;
import
{
Inject
,
Inject
able
}
from
'
@nestjs/common
'
;
import
{
MetadataScanner
,
ModuleRef
,
ModulesContainer
}
from
'
@nestjs/core
'
;
import
{
Argv
,
Command
,
Context
,
User
}
from
'
koishi
'
;
import
{
InstanceWrapper
}
from
'
@nestjs/core/injector/instance-wrapper
'
;
import
{
KOISHI_MODULE_OPTIONS
,
KoishiCommandDefinition
,
KoishiCommandInterceptorDef
,
KoishiDoRegister
,
...
...
@@ -16,6 +17,7 @@ import {
DoRegisterConfig
,
EventName
,
KoishiCommandInterceptorRegistration
,
KoishiModuleOptions
,
KoishiModulePlugin
,
}
from
'
../utility/koishi.interfaces
'
;
import
{
applySelector
}
from
'
../utility/koishi.utility
'
;
...
...
@@ -23,6 +25,7 @@ import _ from 'lodash';
import
{
KoishiContextService
}
from
'
./koishi-context.service
'
;
import
{
Module
}
from
'
@nestjs/core/injector/module
'
;
import
{
KoishiMetadataFetcherService
}
from
'
../koishi-metadata-fetcher/koishi-metadata-fetcher.service
'
;
import
{
KoishiInterceptorManagerService
}
from
'
../koishi-interceptor-manager/koishi-interceptor-manager.service
'
;
@
Injectable
()
export
class
KoishiMetascanService
{
...
...
@@ -32,6 +35,9 @@ export class KoishiMetascanService {
private
readonly
moduleContainer
:
ModulesContainer
,
private
readonly
ctxService
:
KoishiContextService
,
private
readonly
metaFetcher
:
KoishiMetadataFetcherService
,
@
Inject
(
KOISHI_MODULE_OPTIONS
)
private
readonly
koishiModuleOptions
:
KoishiModuleOptions
,
private
readonly
intercepterManager
:
KoishiInterceptorManagerService
,
)
{}
private
preRegisterCommandActionArg
(
config
:
CommandPutConfig
,
cmd
:
Command
)
{
...
...
@@ -107,19 +113,11 @@ export class KoishiMetascanService {
}
}
private
getInterceptor
(
interceptorDef
:
KoishiCommandInterceptorRegistration
)
{
if
(
typeof
interceptorDef
!==
'
object
'
)
{
return
this
.
moduleRef
.
get
(
interceptorDef
,
{
strict
:
false
});
}
return
interceptorDef
;
}
private
addInterceptor
(
addInterceptors
(
command
:
Command
,
interceptorDef
:
KoishiCommandInterceptorRegistration
,
interceptorDef
s
:
KoishiCommandInterceptorRegistration
[]
,
)
{
const
interceptor
=
this
.
getInterceptor
(
interceptorDef
);
command
.
before
((...
params
)
=>
interceptor
.
intercept
(...
params
));
return
this
.
intercepterManager
.
addInterceptors
(
command
,
interceptorDefs
);
}
private
async
handleInstanceRegistration
(
...
...
@@ -187,14 +185,14 @@ export class KoishiMetascanService {
for
(
const
commandDef
of
commandDefs
)
{
command
=
commandDef
(
command
)
||
command
;
}
const
interceptorDefs
=
this
.
metaFetcher
.
getPropertyMetadataArray
(
KoishiCommandInterceptorDef
,
instance
,
methodKey
,
const
interceptorDefs
=
_
.
uniq
(
this
.
metaFetcher
.
getPropertyMetadataArray
(
KoishiCommandInterceptorDef
,
instance
,
methodKey
,
),
);
for
(
const
interceptorDef
of
interceptorDefs
)
{
this
.
addInterceptor
(
command
,
interceptorDef
);
}
this
.
addInterceptors
(
command
,
interceptorDefs
);
if
(
!
commandData
.
putOptions
)
{
command
.
action
((
argv
:
Argv
,
...
args
:
any
[])
=>
methodFun
.
call
(
instance
,
argv
,
...
args
),
...
...
src/utility/koishi.interfaces.ts
View file @
7af29461
...
...
@@ -67,6 +67,7 @@ export interface KoishiModuleOptions
loggerPrefix
?:
string
;
loggerColor
?:
number
;
moduleSelection
?:
KoishiModuleSelection
[];
globalInterceptors
?:
KoishiCommandInterceptorRegistration
[];
}
export
interface
KoishiModuleOptionsFactory
{
...
...
src/utility/replaced-context.ts
0 → 100644
View file @
7af29461
import
{
Argv
,
Command
,
Context
,
Plugin
,
Session
}
from
'
koishi
'
;
import
{
KoishiService
}
from
'
../koishi.service
'
;
import
{
KoishiCommandInterceptorRegistration
}
from
'
./koishi.interfaces
'
;
export
type
Filter
=
(
session
:
Session
)
=>
boolean
;
export
class
ReplacedContext
extends
Context
{
constructor
(
private
_filter
:
Filter
,
private
_app
:
KoishiService
,
private
__plugin
:
Plugin
=
null
,
public
_interceptors
:
KoishiCommandInterceptorRegistration
[]
=
[],
)
{
super
(
_filter
,
_app
,
__plugin
);
}
private
cloneContext
(
filter
:
Filter
,
interceptors
:
KoishiCommandInterceptorRegistration
[]
=
[],
):
Context
{
return
new
ReplacedContext
(
filter
,
this
.
_app
,
this
.
__plugin
,
[
...
this
.
_interceptors
,
...
interceptors
,
]);
}
withInterceptors
(
interceptors
:
KoishiCommandInterceptorRegistration
[])
{
return
this
.
cloneContext
(
this
.
filter
,
interceptors
);
}
any
()
{
return
this
.
cloneContext
(()
=>
true
);
}
never
()
{
return
this
.
cloneContext
(()
=>
false
);
}
union
(
arg
:
Filter
|
Context
)
{
const
filter
=
typeof
arg
===
'
function
'
?
arg
:
arg
.
filter
;
return
this
.
cloneContext
((
s
)
=>
this
.
filter
(
s
)
||
filter
(
s
));
}
intersect
(
arg
:
Filter
|
Context
)
{
const
filter
=
typeof
arg
===
'
function
'
?
arg
:
arg
.
filter
;
return
this
.
cloneContext
((
s
)
=>
this
.
filter
(
s
)
&&
filter
(
s
));
}
except
(
arg
:
Filter
|
Context
)
{
const
filter
=
typeof
arg
===
'
function
'
?
arg
:
arg
.
filter
;
return
this
.
cloneContext
((
s
)
=>
this
.
filter
(
s
)
&&
!
filter
(
s
));
}
command
<
D
extends
string
>
(
def
:
D
,
config
?:
Command
.
Config
,
):
Command
<
never
,
never
,
Argv
.
ArgumentType
<
D
>>
;
command
<
D
extends
string
>
(
def
:
D
,
desc
:
string
,
config
?:
Command
.
Config
,
):
Command
<
never
,
never
,
Argv
.
ArgumentType
<
D
>>
;
command
(
def
:
string
,
...
args
:
[
Command
.
Config
?]
|
[
string
,
Command
.
Config
?])
{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const
cmd
=
super
.
command
(
def
,
...
args
);
this
.
_app
.
addInterceptors
(
cmd
,
this
.
_interceptors
);
return
cmd
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment