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
f6ba250a
Commit
f6ba250a
authored
Oct 24, 2021
by
nanahira
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'command-option'
parents
908560ea
fbc1def7
Pipeline
#6411
passed with stages
in 32 seconds
Changes
7
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
296 additions
and
51 deletions
+296
-51
README.md
README.md
+33
-4
package-lock.json
package-lock.json
+2
-2
package.json
package.json
+1
-1
src/koishi.decorators.ts
src/koishi.decorators.ts
+95
-23
src/koishi.interfaces.ts
src/koishi.interfaces.ts
+65
-7
src/providers/koishi-metascan.service.ts
src/providers/koishi-metascan.service.ts
+99
-14
src/utility/koishi.constants.ts
src/utility/koishi.constants.ts
+1
-0
No files found.
README.md
View file @
f6ba250a
...
...
@@ -238,7 +238,7 @@ export class AppService {
@
CommandDescription
(
'
Echo command from decorators!
'
)
@
CommandUsage
(
'
Command usage
'
)
@
CommandExample
(
'
Command example
'
)
testEchoCommand
(
argv
:
Argv
,
content
:
string
)
{
testEchoCommand
(
@
PutArgv
()
argv
:
Argv
,
@
PutArg
(
0
)
content
:
string
)
{
return
content
;
}
}
...
...
@@ -282,7 +282,7 @@ export class AppService {
*
`@UsePlugin()`
使用该方法注册插件。在 Koishi 实例注册时该方法会自动被调用。该方法需要返回插件定义,可以使用
`PluginDef(plugin, options, select)`
方法生成。
[
参考
](
https://koishi.js.org/v4/guide/plugin/plugin.html#%E5%AE%89%E8%A3%85%E6%8F%92%E4%BB%B6
)
*
`@UseCommand(def: string, config?: Command.Config)`
注册指令。指令系统可以参考
[
Koishi 文档
](
https://koishi.js.org/guide/command.html
)
。指令回调参数位置和类型和 Koishi 指令一致。
*
`@UseCommand(def: string,
desc?: string,
config?: Command.Config)`
注册指令。指令系统可以参考
[
Koishi 文档
](
https://koishi.js.org/guide/command.html
)
。指令回调参数位置和类型和 Koishi 指令一致。
#### 指令描述装饰器
...
...
@@ -298,10 +298,29 @@ Koishi-Nest 使用一组装饰器进行描述指令的行为。这些装饰器
*
`@CommandShortcut(def: string, config?: Command.Shortcut)`
指令快捷方式。等价于
`cmd.shortcut(def, config)`
。
*
`@CommandOption(name: string, desc: string, config: Argv.OptionConfig = {})`
指令选项。等价于
`cmd.option(name, desc, config)`
。
*
`@CommandDef((cmd: Command) => Command)`
手动定义指令信息,用于 Koishi-Nest 不支持的指令类型。
#### 指令参数
指令参数也使用一组装饰器对指令参数进行注入。下列装饰器应对提供者方法参数进行操作。
*
`@PutArgv()`
注入
`Argv`
对象。
*
`@PutSession(field?: keyof Session)`
注入
`Session`
对象,或
`Session`
对象的指定字段。
*
`@PutArg(index: number)`
注入指令的第 n 个参数。
*
`@PutOption(name: string, desc: string, config: Argv.OptionConfig = {})`
给指令添加选项并注入到该参数。等价于
`cmd.option(name, desc, config)`
。
*
`@PutUser(fields: string[])`
添加一部分字段用于观测,并将 User 对象注入到该参数。
*
`@PutChannel(fields: string[])`
添加一部分字段用于观测,并将 Channel 对象注入到该参数。
关于 Koishi 的观察者概念详见
[
Koishi 文档
](
https://koishi.js.org/v4/guide/database/observer.html#%E8%A7%82%E5%AF%9F%E8%80%85%E5%AF%B9%E8%B1%A1
)
。
*
`@PutUserName(useDatabase: boolean = true)`
注入当前用户的用户名。
*
`useDatabase`
是否尝试从数据库获取用户名。
## 上下文 Service 交互
您可以使用装饰器与 Koishi 的 Service 系统进行交互。
...
...
@@ -459,3 +478,13 @@ export class AppModule {}
*
`plugin`
: Koishi 插件。
*
`options`
: Koishi 插件配置。等同于
`ctx.plugin(plugin, options)`
。
*
上下文选择器见本文
**上下文选择器**
部分。
## 更新历史
### 1.3
*
`@UseCommand`
现在定义和 Koishi 的指令定义,即
`ctx.command(name, desc, config)`
或
`ctx.command(name, config)`
一致了。
*
增加了
`@CommandUserFields`
对应
`cmd.userFields`
以及
`@CommandChannelFields`
对应
`cmd.channelFields`
。
*
增加了用于注入 Koishi 指令调用信息的提供者方法参数装饰器。详见
**指令参数**
部分。
\ No newline at end of file
package-lock.json
View file @
f6ba250a
{
"name"
:
"koishi-nestjs"
,
"version"
:
"1.
2.2
"
,
"version"
:
"1.
3.0
"
,
"lockfileVersion"
:
2
,
"requires"
:
true
,
"packages"
:
{
""
:
{
"name"
:
"koishi-nestjs"
,
"version"
:
"1.
2.2
"
,
"version"
:
"1.
3.0
"
,
"license"
:
"MIT"
,
"dependencies"
:
{
"@koa/router"
:
"^10.1.1"
,
...
...
package.json
View file @
f6ba250a
{
"name"
:
"koishi-nestjs"
,
"version"
:
"1.
2.3
"
,
"version"
:
"1.
3.0
"
,
"description"
:
"Koishi.js as Nest.js Module"
,
"main"
:
"dist/index.js"
,
"typings"
:
"dist/index.d.ts"
,
...
...
src/koishi.decorators.ts
View file @
f6ba250a
...
...
@@ -2,6 +2,7 @@ import { CustomDecorator, Inject, SetMetadata } from '@nestjs/common';
import
{
KOISHI_CONTEXT
,
KoishiCommandDefinition
,
KoishiCommandPutDef
,
KoishiDoRegister
,
KoishiOnContextScope
,
KoishiServiceProvideSym
,
...
...
@@ -10,14 +11,15 @@ import {
}
from
'
./utility/koishi.constants
'
;
import
{
CommandDefinitionFun
,
ContextFunction
,
CommandPutConfig
,
CommandPutConfigMap
,
DoRegisterConfig
,
EventName
,
EventNameAndPrepend
,
GenerateMappingStruct
,
OnContextFunction
,
Selection
,
}
from
'
./koishi.interfaces
'
;
import
{
Argv
,
Command
,
Context
}
from
'
koishi
'
;
import
{
Argv
,
Command
,
Context
,
FieldCollector
,
Session
}
from
'
koishi
'
;
import
{
ContextScopeTypes
,
getContextProvideToken
,
...
...
@@ -66,33 +68,55 @@ export const SetExtraMetadata = <K = string, V = any>(
// Register methods
export
const
UseMiddleware
=
(
prepend
?:
boolean
):
MethodDecorator
=>
SetMetadata
<
string
,
DoRegisterConfig
<
boolean
>>
(
KoishiDoRegister
,
{
type
:
'
middleware
'
,
data
:
prepend
,
}
);
SetMetadata
<
string
,
DoRegisterConfig
<
'
middleware
'
>>
(
KoishiDoRegister
,
GenerateMappingStruct
(
'
middleware
'
,
prepend
)
,
);
export
const
UseEvent
=
(
name
:
EventName
,
prepend
?:
boolean
):
MethodDecorator
=>
SetMetadata
<
string
,
DoRegisterConfig
<
EventNameAndPrepend
>>
(
KoishiDoRegister
,
{
type
:
'
onevent
'
,
data
:
{
name
,
prepend
}
,
}
);
SetMetadata
<
string
,
DoRegisterConfig
<
'
onevent
'
>>
(
KoishiDoRegister
,
GenerateMappingStruct
(
'
onevent
'
,
{
name
,
prepend
})
,
);
export
const
UsePlugin
=
():
MethodDecorator
=>
SetMetadata
<
string
,
DoRegisterConfig
>
(
KoishiDoRegister
,
{
type
:
'
plugin
'
,
});
SetMetadata
<
string
,
DoRegisterConfig
<
'
plugin
'
>>
(
KoishiDoRegister
,
GenerateMappingStruct
(
'
plugin
'
),
);
export
function
UseCommand
<
D
extends
string
>
(
def
:
D
,
config
?:
Command
.
Config
,
):
MethodDecorator
;
export
function
UseCommand
<
D
extends
string
>
(
def
:
D
,
desc
:
string
,
config
?:
Command
.
Config
,
):
MethodDecorator
;
export
function
UseCommand
(
def
:
string
,
...
args
:
[
Command
.
Config
?]
|
[
string
,
Command
.
Config
?]
):
MethodDecorator
{
return
SetMetadata
<
string
,
DoRegisterConfig
<
ContextFunction
<
Command
<
never
,
never
,
Argv
.
ArgumentType
<
D
>>>
>
>
(
KoishiDoRegister
,
{
const
desc
=
typeof
args
[
0
]
===
'
string
'
?
(
args
.
shift
()
as
string
)
:
''
;
const
config
=
args
[
0
]
as
Command
.
Config
;
return
(
obj
,
key
:
string
,
des
)
=>
{
const
putOptions
:
CommandPutConfig
<
keyof
CommandPutConfigMap
>
[]
=
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
:
(
ctx
)
=>
ctx
.
command
(
def
,
config
),
});
data
:
{
def
,
desc
,
config
,
putOptions
,
},
},
);
return
metadataDec
(
obj
,
key
,
des
);
};
}
// Context scopes
...
...
@@ -154,6 +178,54 @@ export const CommandOption = (
config
:
Argv
.
OptionConfig
=
{},
)
=>
CommandDef
((
cmd
)
=>
cmd
.
option
(
name
,
desc
,
config
));
export
const
CommandUserFields
=
(
fields
:
FieldCollector
<
'
user
'
>
)
=>
CommandDef
((
cmd
)
=>
cmd
.
userFields
(
fields
));
export
const
CommandChannelFields
=
(
fields
:
FieldCollector
<
'
channel
'
>
)
=>
CommandDef
((
cmd
)
=>
cmd
.
channelFields
(
fields
));
// Command put config
function
PutCommandParam
<
T
extends
keyof
CommandPutConfigMap
>
(
type
:
T
,
data
?:
CommandPutConfigMap
[
T
],
):
ParameterDecorator
{
return
(
obj
,
key
:
string
,
index
)
=>
{
const
objClass
=
obj
.
constructor
;
const
list
:
CommandPutConfig
<
T
>
[]
=
Reflect
.
getMetadata
(
KoishiCommandPutDef
,
objClass
,
key
)
||
[];
list
[
index
]
=
GenerateMappingStruct
(
type
,
data
);
Reflect
.
defineMetadata
(
KoishiCommandPutDef
,
list
,
objClass
,
key
);
};
}
export
const
PutArgv
=
()
=>
PutCommandParam
(
'
argv
'
);
export
const
PutSession
=
(
field
?:
keyof
Session
)
=>
field
?
PutCommandParam
(
'
sessionField
'
,
field
)
:
PutCommandParam
(
'
session
'
);
export
const
PutArg
=
(
i
:
number
)
=>
PutCommandParam
(
'
arg
'
,
i
);
export
const
PutOption
=
(
name
:
string
,
desc
:
string
,
config
:
Argv
.
OptionConfig
=
{},
)
=>
PutCommandParam
(
'
option
'
,
{
name
,
desc
,
config
});
export
const
PutUser
=
(
field
:
FieldCollector
<
'
user
'
>
)
=>
PutCommandParam
(
'
user
'
,
field
);
export
const
PutChannel
=
(
field
:
FieldCollector
<
'
channel
'
>
)
=>
PutCommandParam
(
'
channel
'
,
field
);
export
const
PutUserName
=
(
useDatabase
=
true
)
=>
PutCommandParam
(
'
username
'
,
useDatabase
);
export
const
PutUserId
=
()
=>
PutSession
(
'
userId
'
);
export
const
PutGuildId
=
()
=>
PutSession
(
'
guildId
'
);
export
const
PutGuildName
=
()
=>
PutSession
(
'
guildName
'
);
export
const
PutChannelId
=
()
=>
PutSession
(
'
channelId
'
);
export
const
PutChannelName
=
()
=>
PutSession
(
'
channelName
'
);
export
const
PutSelfId
=
()
=>
PutSession
(
'
selfId
'
);
export
const
PutBot
=
()
=>
PutSession
(
'
bot
'
);
// Service
export
function
WireContextService
(
name
?:
string
):
PropertyDecorator
{
...
...
src/koishi.interfaces.ts
View file @
f6ba250a
import
{
ModuleMetadata
,
Provider
,
Type
}
from
'
@nestjs/common
'
;
import
{
App
,
Command
,
Context
,
EventMap
,
MaybeArray
,
Plugin
}
from
'
koishi
'
;
import
{
App
,
Argv
,
Command
,
Context
,
EventMap
,
FieldCollector
,
MaybeArray
,
Plugin
,
Session
,
}
from
'
koishi
'
;
const
selectors
=
[
'
user
'
,
...
...
@@ -74,18 +84,66 @@ export interface EventNameAndPrepend {
name
:
EventName
;
prepend
?:
boolean
;
}
export
type
Promisify
<
T
>
=
T
extends
Promise
<
unknown
>
?
T
:
Promise
<
T
>
;
export
type
ContextFunction
<
T
>
=
(
ctx
:
Context
)
=>
T
;
export
type
OnContextFunction
=
ContextFunction
<
Context
>
;
export
type
DoRegisterType
=
'
middleware
'
|
'
command
'
|
'
onevent
'
|
'
plugin
'
;
export
interface
DoRegisterConfig
<
T
=
any
>
{
type
:
DoRegisterType
;
data
?:
T
;
export
interface
DoRegisterConfigDataMap
{
middleware
:
boolean
;
// prepend
onevent
:
EventNameAndPrepend
;
plugin
:
never
;
command
:
CommandRegisterConfig
;
}
export
interface
CommandConfigWIthDescription
extends
Command
.
Config
{
export
interface
MappingStruct
<
T
extends
Record
<
string
|
number
|
symbol
,
any
>
,
K
extends
keyof
T
>
{
type
:
K
;
data
?:
T
[
K
];
}
export
function
GenerateMappingStruct
<
T
extends
Record
<
string
|
number
|
symbol
,
any
>
,
K
extends
keyof
T
>
(
type
:
K
,
data
?:
T
[
K
]):
MappingStruct
<
T
,
K
>
{
return
{
type
,
data
,
};
}
export
type
DoRegisterConfig
<
K
extends
keyof
DoRegisterConfigDataMap
=
keyof
DoRegisterConfigDataMap
>
=
MappingStruct
<
DoRegisterConfigDataMap
,
K
>
;
// Command stuff
export
interface
CommandRegisterConfig
<
D
extends
string
=
string
>
{
def
:
D
;
desc
?:
string
;
config
?:
Command
.
Config
;
putOptions
?:
CommandPutConfig
<
keyof
CommandPutConfigMap
>
[];
}
export
interface
CommandOptionConfig
{
name
:
string
;
desc
:
string
;
config
?:
Argv
.
OptionConfig
;
}
export
interface
CommandPutConfigMap
{
arg
:
number
;
argv
:
never
;
session
:
never
;
option
:
CommandOptionConfig
;
user
:
FieldCollector
<
'
user
'
>
;
channel
:
FieldCollector
<
'
channel
'
>
;
username
:
boolean
;
sessionField
:
keyof
Session
;
}
export
type
CommandPutConfig
<
K
extends
keyof
CommandPutConfigMap
=
keyof
CommandPutConfigMap
>
=
MappingStruct
<
CommandPutConfigMap
,
K
>
;
export
type
CommandDefinitionFun
=
(
cmd
:
Command
)
=>
Command
;
src/providers/koishi-metascan.service.ts
View file @
f6ba250a
...
...
@@ -7,7 +7,7 @@ import {
ModulesContainer
,
Reflector
,
}
from
'
@nestjs/core
'
;
import
{
Argv
,
Command
,
Context
}
from
'
koishi
'
;
import
{
Argv
,
Command
,
Context
,
User
}
from
'
koishi
'
;
import
{
InstanceWrapper
}
from
'
@nestjs/core/injector/instance-wrapper
'
;
import
{
KoishiCommandDefinition
,
...
...
@@ -19,10 +19,9 @@ import {
}
from
'
../utility/koishi.constants
'
;
import
{
CommandDefinitionFun
,
Co
ntextFunction
,
Co
mmandPutConfig
,
DoRegisterConfig
,
EventName
,
EventNameAndPrepend
,
KoishiModulePlugin
,
OnContextFunction
,
}
from
'
../koishi.interfaces
'
;
...
...
@@ -50,6 +49,79 @@ export class KoishiMetascanService {
}
}
private
preRegisterCommandActionArg
(
config
:
CommandPutConfig
,
cmd
:
Command
)
{
if
(
!
config
)
{
return
;
}
switch
(
config
.
type
)
{
case
'
option
'
:
const
{
data
:
optionData
}
=
config
as
CommandPutConfig
<
'
option
'
>
;
cmd
.
option
(
optionData
.
name
,
optionData
.
desc
,
optionData
.
config
);
break
;
case
'
user
'
:
const
{
data
:
userFields
}
=
config
as
CommandPutConfig
<
'
user
'
>
;
if
(
userFields
)
{
cmd
.
userFields
(
userFields
);
}
break
;
case
'
channel
'
:
const
{
data
:
channelFields
}
=
config
as
CommandPutConfig
<
'
channel
'
>
;
if
(
channelFields
)
{
cmd
.
channelFields
(
channelFields
);
}
break
;
case
'
username
'
:
const
{
data
:
useDatabase
}
=
config
as
CommandPutConfig
<
'
username
'
>
;
if
(
useDatabase
)
{
cmd
.
userFields
([
'
name
'
]);
}
break
;
default
:
break
;
}
return
;
}
private
getCommandActionArg
(
config
:
CommandPutConfig
,
argv
:
Argv
,
args
:
any
[],
)
{
if
(
!
config
)
{
return
;
}
switch
(
config
.
type
)
{
case
'
arg
'
:
const
{
data
:
index
}
=
config
as
CommandPutConfig
<
'
arg
'
>
;
return
args
[
index
];
case
'
argv
'
:
return
argv
;
case
'
session
'
:
return
argv
.
session
;
case
'
option
'
:
const
{
data
:
optionData
}
=
config
as
CommandPutConfig
<
'
option
'
>
;
return
argv
.
options
[
optionData
.
name
];
case
'
user
'
:
return
argv
.
session
.
user
;
case
'
channel
'
:
return
argv
.
session
.
channel
;
case
'
username
'
:
const
{
data
:
useDatabase
}
=
config
as
CommandPutConfig
<
'
username
'
>
;
if
(
useDatabase
)
{
const
user
=
argv
.
session
.
user
as
User
.
Observed
<
'
name
'
>
;
if
(
user
?.
name
)
{
return
user
?.
name
;
}
}
return
argv
.
session
.
author
?.
nickname
||
argv
.
session
.
author
?.
username
;
case
'
sessionField
'
:
const
{
data
:
field
}
=
config
as
CommandPutConfig
<
'
sessionField
'
>
;
return
argv
.
session
[
field
];
default
:
return
;
}
}
private
async
handleInstanceRegistration
(
ctx
:
Context
,
instance
:
Record
<
string
,
any
>
,
...
...
@@ -84,15 +156,14 @@ export class KoishiMetascanService {
}
switch
(
regData
.
type
)
{
case
'
middleware
'
:
const
{
data
:
midPrepend
}
=
regData
as
DoRegisterConfig
<
'
middleware
'
>
;
baseContext
.
middleware
(
(
session
,
next
)
=>
methodFun
.
call
(
instance
,
session
,
next
),
regData
.
data
,
midPrepend
,
);
break
;
case
'
onevent
'
:
const
{
data
:
eventData
,
}
=
regData
as
DoRegisterConfig
<
EventNameAndPrepend
>
;
const
{
data
:
eventData
}
=
regData
as
DoRegisterConfig
<
'
onevent
'
>
;
const
eventName
=
eventData
.
name
;
baseContext
.
on
(
eventData
.
name
,
(...
args
:
any
[])
=>
methodFun
.
call
(
instance
,
...
args
),
...
...
@@ -116,10 +187,12 @@ export class KoishiMetascanService {
pluginCtx
.
plugin
(
pluginDesc
.
plugin
,
pluginDesc
.
options
);
break
;
case
'
command
'
:
const
{
data
:
commandData
}
=
regData
as
DoRegisterConfig
<
ContextFunction
<
Command
>
>
;
let
command
=
commandData
(
baseContext
);
const
{
data
:
commandData
}
=
regData
as
DoRegisterConfig
<
'
command
'
>
;
let
command
=
baseContext
.
command
(
commandData
.
def
,
commandData
.
desc
,
commandData
.
config
,
);
const
commandDefs
:
CommandDefinitionFun
[]
=
this
.
reflector
.
get
(
KoishiCommandDefinition
,
methodFun
,
...
...
@@ -129,9 +202,21 @@ export class KoishiMetascanService {
command
=
commandDef
(
command
)
||
command
;
}
}
if
(
!
commandData
.
putOptions
)
{
command
.
action
((
argv
:
Argv
,
...
args
:
any
[])
=>
methodFun
.
call
(
instance
,
argv
,
...
args
),
);
}
else
{
for
(
const
_optionToRegister
of
commandData
.
putOptions
)
{
this
.
preRegisterCommandActionArg
(
_optionToRegister
,
command
);
}
command
.
action
((
argv
:
Argv
,
...
args
:
any
[])
=>
{
const
params
=
commandData
.
putOptions
.
map
((
o
)
=>
this
.
getCommandActionArg
(
o
,
argv
,
args
),
);
return
methodFun
.
call
(
instance
,
...
params
);
});
}
break
;
default
:
throw
new
Error
(
`Unknown operaton type
${
regData
.
type
}
`
);
...
...
src/utility/koishi.constants.ts
View file @
f6ba250a
...
...
@@ -6,6 +6,7 @@ export const KOISHI_CONTEXT = 'KOISHI_CONTEXT';
export
const
KoishiOnContextScope
=
'
KoishiOnContextScope
'
;
export
const
KoishiDoRegister
=
'
KoishiDoRegister
'
;
export
const
KoishiCommandDefinition
=
'
KoishiCommandDefinition
'
;
export
const
KoishiCommandPutDef
=
Symbol
(
'
KoishiCommandPutDef
'
);
export
const
KoishiServiceWireProperty
=
Symbol
(
'
KoishiServiceWireProperty
'
);
export
const
KoishiServiceWireKeys
=
Symbol
(
'
KoishiServiceWireKeys
'
);
...
...
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