上一篇文章的末尾,我教大家简单尝试使用了Koishi
的中间件middleware
。那么今天,我们从接着上一次的末尾开始讲起,教会大家如何使用中间件和指令系统。在本章节完成后,你将可以完成一个简单的带有敏感词记录的复读姬
中间件
首先,究竟什么是中间件。用通俗的话来讲,中间件系统可以理解成为一款闯关游戏,你可以依次通过每一关,当你在某一关失败后,就无法接着向后执行。中间件也是同理,消息在某一层的中间件被拦截后,后面的中间件都不会被执行了。我们可以看一个例子:
// ....其他配置
export function apply(ctx : Context){
ctx.middleware((session, next)=>{
if(session.content == '天王盖地虎') {
return '宝塔镇河妖';
}else{
return next();
}
})
ctx.middleware((session, next)=>{
if(session.content == '天王盖地虎') {
return '小鸡炖蘑菇'
}else{
return next();
}
})
}
然后我们开始执行这个程序,他会显示下面这些内容:
你:天王盖地虎
机器人:宝塔镇河妖
你会发现,机器人并没有输出"小鸡炖蘑菇" ! 是的!因为我们在第一个middleware中匹配到了"天王盖地虎",而在接下来的语句(return '宝塔镇河妖';
)中直接返回了"宝塔镇河妖"并直接结束了整个中间件流程。后面的“小鸡炖蘑菇”就不会继续执行了。而如果没有匹配到,则会执行return next();
则告诉koishi,消息通过了我这个中间件,可以通往下一层中间件了!
那么,我们先使用中间件,实现一个简单的复读姬吧!
// ....其他配置
export function apply(ctx : Context){
ctx.middleware((session,next)=>{
// startWith函数的含义是判断字符串是否以某段文字开头
if(session.content.startsWith("复读姬复读")){
// substr(5)是去掉开头的"复读机复读"这五个字
return session.content.substr(5);
}else{
return next();
}
})
}
紧接着,运行这个插件,然后在你的群内输入“复读姬复读我是一个复读姬”,然后你的机器人就会回复“我是一个复读姬”了!
但是,如果这个时候某些不怀好意的坏群友想要教坏复读姬 (复读姬:人家才不要**!) ,那他只需要发送“复读姬复读+各种奇奇怪怪的句子”,复读姬就会复述这些句子。但是,作为复读姬的父亲(母亲),你怎么能容忍大家这么对待你的复读姬!于是,你可以在复读的middleware之前(也就是代码最前面),通过middleware实现一个敏感词过滤功能
// ....其他配置
export function apply(ctx : Context){
// 从这里开始是添加的代码
const illegalWords = ["fxxk","example","银趴","其他你想要的敏感词"];
ctx.middleware((session,next)=>{
for(let i in illegalWords){
if(session.content.includes(illegalWords[i]))
return '不要!人家才不要听你这句话呢!哼~';
}
return next();
})
// 这下面保持不动!
ctx.middleware((session,next)=>{
// startWith函数的含义是判断字符串是否以某段文字开头
if(session.content.startsWith("复读姬复读")){
// substr(5)是去掉开头的"复读机复读"这五个字
return session.content.substr(5);
}else{
return next();
}
})
}
然后你可以发送给复读姬“复读姬复读+带有敏感词的句子”,这时候复读姬就不会再复读你的句子,而是输出一个“不要!人家才不要听你这句话呢!哼”。其实,上述代码只要你发送了带有敏感词的内容,复读姬就会发送一个“不要!人家才不要听你这句话呢!哼”到聊天窗口里。(这个复读姬我爱了)
到现在为止,你已经成功对koishi
的中间件系统有了初步的了解,下面我们就进入下一个板块:命令系统
命令系统
不知道各位读者在看到这里有没有一个问题:那如果我有一堆功能,用middleware实现不太好吧,首先是middleware需要自己判断内容是否满足条件,第二个是,如果出现更加复杂的命令,比如查单词的含义,计算加减法,那样用middleware就需要自己提取参数了。但作为koishi
的创始人和开发者,聪明的梦梦(Shigma)为什么会想不到这一点呢?因此,在Koishi中有一套完整的命令处理系统。
那说了这半天,命令系统到底该怎么用呢?我们还是以一个例子开始:
// ....其他配置
export function apply(ctx : Context){
ctx.command("furry")
.action(({session})=>{
return '是福瑞控!'
})
}
然后你可以在群内测试:
你:furry
机器人:是福瑞控!
我们来分析以下上述的代码,首先,ctx.command
里定义了一个叫furry的命令,当你输入furry的时候,会执行action函数内传入的参数。这就能大大简化了命令的定义,但仅仅做到这点还远远不够,我们可以接着向上面的代码中加入一些私货
// ....其他配置
export function apply(ctx : Context){
ctx.command("furry <person:string>")
.action(({session},person)=>{
return person+'是福瑞控!'
})
}
然后在聊天群里输入“furry 梦梦”,你能看到下面这段话:
机器人: 梦梦是福瑞控!
(梦梦:我没有,我不是,别乱说)
(作者被梦梦强大的气场镇住,本系列完)
## 命令的格式
通过上面的例子,你已经初步了解了如何创建一个简单的Koishi
命令,那么接下来就该深入了解它了!
首先,ctx.command
传入的参数(也就是那个字符串)是一个叫做命令定义的东东,它定义了这个命令运行的格式是怎么样的。就好比你定义自助售票机上有哪些按钮,该怎么按,需要输入什么数据(比如防止有人把自己姓名填入了身份证号的输入框里~~,不要问那是谁,因为那就是我~~)。同时,如果用户输入了错误的格式,koishi会自动提示用户“您老的格式输错嘞”而不是直接原地爆炸升天,并且留下一地烂摊子等着开发人员收拾。
那我们来看看命令是怎么定义的。首先命令定义的第一个参数是“命令名称”,比如“furry”,比如“查图”(当然取名原地爆炸也不是不可以,毕竟小爱就有这个功能),然后后面接着很多个参数,参数与参数之间用空格连接。就比如这样:
我想和你 <todo> [when]
你可能已经注意到了,上面似乎有两种参数,一种参数用方括号[]
包裹,另一种则用尖括号<>
包裹,这两者有什么不同呢?既然你诚心诚意的问了,那我们就上代码吧:
export function apply(ctx : Context){
ctx.command("我想和你 <todo> [when]",{ checkArgCount: true })
.action(({session},todo,when)=>{
return "好的,"+when+"我会提醒你"+todo
})
}
(请注意,由于一些历史遗留问题,目前的koishi
不会检查参数是否有正确的数量,所以需要加入{ checkArgCount: true }
作为选项来强制执行参数数量检查,这个问题可能将会在未来的koishi
版本中修复)
接着我们在群里发送命令,然后你就能看到效果了(请注意空格):
你: 我想和你
机器人: 缺少参数,输入帮助以查看用法。
你: 我想和你 一起闯进森林潜入海底
机器人: 好的,undefined我会提醒你一起闯进森林潜入海底
你: 我想和你 一起闯进森林潜入海底 明天上午
机器人: 好的,明天上午我会提醒你一起闯进森林潜入海底
是的,尖括号(<>
)代表的是必选参数,而方括号表示的是可选参数,必选参数如果没有选择的话会显示“缺少参数,输入帮助以查看用法。”,而可选参数在没有输入的情况下是不会报错,但会往对应的参数中传入undefined
题外话:如果不设置checkArgCount: true
,目前的默认行为是让必选参数和可选参数一样,不管有没有提供都传给开发者,由开发者做判断。
但是这里又有一个问题:那如果我输入错误的类型怎么办?比如我往下面这个命令里输入“买票 地球上所有的票”
ctx.command("买票 <count>") //下面的代码省略
那么机器人难道需要跑遍天南地北,天涯海角,就为帮你买到世界上所有的票(好尽职尽责的机器人,他真的,我哭死)?不!Koishi自己自带了类型检查功能,当输入的不是一个合法的数字的时候,Koishi
便能温馨的提醒你:你四不四(是不是)不小心输错了?还是以上面这个命令为例,我可以改为:
ctx.command("买票 <count:posint>") //下面的代码省略
也就是"参数名称:类型"的这种形式。这时候你再输入“买票 地球上所有的票”,机器人就会回答“参数 count 输入无效,请提供一个正整数。”
koishi
支持的参数类型有以下几种:
string: string 字符串
number: number 数值
boolean: boolean 布尔值(实际上不带参数)
text: string 贪婪匹配的字符串
user: string 用户,格式为 {platform}:{id}
channel: string 频道,格式为 {platform}:{id}
integer: number 整数
posint: number 正整数
date: Date 日期
今天暂时就先讲这么多,关于命令系统还有一小节内容,我将在下一篇文章中进行讲解。