在上一节当中,我们介绍了中间件和指令系统的基本使用,那么我们接下来需要更进一步地了解指令系统。
下面来看一下一个最近非常火爆的插件中的一个指令:
chatgpt -r
这条指令是Koishi-ChatGPT
插件中重置上下文的指令。你可能会好奇,这个-r
参数是怎么实现的?在koishi
中,这种-X形式的参数我们称之为“选项”,对于上面这个例子,我们实现的选项是“-r
”
// 其他配置项
export function apply(ctx:Context){
ctx.command('chatgpt <input:text>')
.option('reset', '-r')
.action(async ({ options, session }, input) => {
if (options?.reset) {
//处理删除逻辑
return '重置上下文成功!'
}
return "这里是机器人返回的信息";
})
}
紧接着我们保存并自动重载,当我们使用chatgpt -r
指令或者chatgpt --reset
之后,机器人回复了一句"重置上下文成功!“,而当我们不加任何参数的时候,机器人回复的是"这里是机器人返回的信息”。
Koishi的命令系统还包含一些高级选项,例如设置命令的描述,或者使用authority
用于指定权限等级,这些部分的具体用法需要你参照Koishi的文档,下面是一些例子:
ctx.command("管理命令",{authority:5}) // 限制命令使用权限
ctx.command("pull","这个命令的描述") // 限制命令使用权限
ctx.command("汴京","和群友汴京",{authority:2}) // 限制命令使用权限,并给一些选项
如果你还想更深入了解指令系统的相关内容,你可以参阅Koishi
的官方文档https://koishi.chat/guide/command/index.html
现在,让我们开始写一个带有监测违规内容的复读姬吧!这个复读姬设计有一个中间件,负责过滤发给机器人的词中不包含违禁词;以及一个命令,用于临时增加违禁词列表,那么,开始啦!
首先,我们再次输入第一课所说的那个指令(yarn new
/npm run new
),插件名称你可以自己想,本例中采用“repeater”作为插件名称
$ yarn new
yarn run v1.22.19
> koishi-scripts new
√ plugin name: ... repeater
√ description: ... A repeater machine.
然后按照第一课所说,设置好koishi.yml
,接着在控制台中输入yarn dev
启动开发服务器。接着我们开始编写index.ts
文件
在index.ts文件中,我们需要写一个中间件(用于拦截任何有违规内容的消息)、两个命令(拦截系统、复读系统),下面是一个完整的例子:
import { Context, Schema } from 'koishi'
export const name = 'repeater'
export interface Config {}
export const Config: Schema<Config> = Schema.object({})
export function apply(ctx: Context) {
const illegalWords = ['预设的一些黑名单词汇','fxxk','sb'];
ctx.middleware((session,next)=>{
for(let i in illegalWords){
if(session.content.includes(illegalWords[i]))
return '群主!这里有人发违规词汇了!快来处理叭!';
}
return next();
})
ctx.command("拉黑 <word:string>","拉黑某个词汇")
.action((argv,word)=>{
illegalWords.push(word)
})
ctx.command("复读 <word:text>","复读一句话")
.action((argv,text)=>{
return text;
})
}
上述代码使用中间件在处理消息时,检查是否包含黑名单词汇。如果包含,则返回固定的提示语;否则,继续处理消息。实现了两个命令:一个名为 “拉黑” 的命令,用于将某个词汇添加到黑名单中。一个名为 “复读” 的命令,用于将输入的一句话作为回复返回给用户。
但是,眼尖的小伙伴可能会发现,上述代码并没有对数据进行持久化处理,所以配置的违禁词列表在下一次重启之后就会消失,而且配置违禁词需要和机器人交互。那有人会提出疑问,那我怎么实现一个存储在配置文件,可以在Web控制台中修改的配置呢?这就是我们今天的主角----配置系统
首先,配置系统里的主角,也是我们今天要介绍的,是Schema。何谓Schema?Schema是"模式验证器"的意思。用大白话理解Schema,就是你可以规定传入的数据以什么样的格式和类型传入。因此它可以被用于检测配置。我们来看一个例子:
export interface Config {
interval:number
content:string
}
export const Config: Schema<Config> = Schema.object({
interval:Schema.number(),
content:Schema.string()
})
这里定义了一个number型的配置项,名为interval
、和一个string
型的配置项,名为content,此时你可以在该插件对应的配置中加入interval属性(你可以修改koishi.yml
,也可以在WebUI中进行操作)。例如下面这个例子(我们假设这个插件的名字是scheduled-notice
),那么我可以以下面这个配置文件初始化它:
plugins:
scheduled-notice:
interval: 3600
content: "大家好,我是本群的提醒睡觉小助手,希望此刻看见消息的人可以和我一样,盖好被子,小心着凉,过会我会继续提醒大家及时睡觉,和我一起成为一天睡24小时的five吧!"
上述这个配置文件就是符合之前的Schema定义的配置,但如果我们将interval
中的值改为一个字符串"两年半"
,则会引发koishi
弹出一下面这段提示:
2022-12-17 15:54:34 [W] app TypeError: expected number but got 两年半
这就代表着Koishi正确检测到了配置的错误并弹出了错误提示。
但有人突然想,我如果想要允许配置项里传入不同的类型怎么办?诶,koishi提供了一种特殊的Schema元素: union。我们修改一下上述代码,让他变成这样:
export interface Config {
interval:number|string
content:string
}
export const Config: Schema<Config> = Schema.object({
interval:Schema.union([
Schema.number(),
Schema.string()
]),
content:Schema.string()
})
然后我们配置文件就可以增加interval的传入类型:
plugins:
scheduled-notice:
interval: "16:00"
content: "喂!三点几嚟,做撚啊做,饮茶先啦,做咁多都冇用嘅!"
(注:Schema不会自动帮你处理类型,它只负责检查,所以具体的类型分类处理逻辑需要自己编写)
现在,我们可以向古老的apply中添加一个参数config
,也即是变成下面这样:
export function apply(ctx: Context,config:Config) {
// ... 你的代码...
}
接着,我们就能在config
中接收到配置文件了。现在,我们修改一下之前的代码,变成这个样子:
import { Context, Schema } from 'koishi'
export const name = 'repeater'
export interface Config {
illegalWords:string[]
}
export const Config: Schema<Config> = Schema.object({
illegalWords:Schema.array(Schema.string())
})
export function apply(ctx: Context,config:Config) {
ctx.middleware((session,next)=>{
for(let i in config.illegalWords){
if(session.content.includes(config.illegalWords[i]))
return '主人!这里有人发违规词汇了!快来处理叭!';
}
return next();
})
ctx.command("复读 <word:text>","复读一句话")
.action((argv,text)=>{
return text;
})
}
然后我们就可以在熟悉的WebUI或者koishi.yml
中配置违禁词了: