插件示例
在本节中,我们通过一个简单的例子,使用插件实现一个虚构的规则指令,来展示插件是如何编写的。
新建一个插件
在程序文件同级目录找到 plugins
文件夹,其中每个子文件夹就是一个插件。
文件夹中已经有一些自带插件了。我们可以新建一个文件夹作为自己的插件,假设文件夹名字为 com.example.myplugin
。
文件夹名字同时也代表你的插件 id 。你可以任意修改名字,但尽量独特些,避免与其他人写的插件冲突。
在文件夹中,我们新建一个文本文件,并命名为 index.js
,代表插件的入口文件。
打开这个文件,并输入以下代码:
module.exports = (apis) => {
return {
// 插件 id,与文件夹名字保持一致
id: 'com.example.myplugin',
// 插件名称,用于展示
name: '测试插件',
// 插件版本,自己控制即可
version: 1,
// 自定义回复
customReply: [
{
// 自定义回复 id,可以自己起,在同一个插件内部唯一即可
id: 'test',
// 自定义回复命令名称,用于展示
name: '测试命令',
// 自定义回复命令描述,用于展示,可以在此处介绍用法
description: '使用【.test 技能】进行检定',
// 自定义回复匹配规则,这个例子匹配所有 .testXXXX 格式的消息
command: '^test(?<content>.*)',
trigger: 'regex',
// 自定义回复处理方法
handler(env, matchGroup) {
return 'Hello World!'
}
}
]
}
}
WARNING
推荐使用专业的代码编辑器,如 VSCode、WebStorm 等编写代码。文本文件建议存储为 UTF-8 格式,避免中文乱码。
这段代码定义了一个插件的基本结构。注意如果你修改了文件夹的名字,也要同步修改代码中的插件 id。
这段代码的作用是,定义了一个新的自定义回复插件。当用户发送任意以 .test
开头的指令时,机器人会回复消息 Hello World!
。
如果你希望修改触发的指令,把 command
字段中的 test
修改为你想要的指令名即可。command
字段实际上代表了一个正则表达式。如果你熟悉正则表达式,可以自己设计更复杂的匹配规则。
编写消息处理逻辑
让我们考虑一个虚构的规则:指定玩家的一项技能,当技能值大于 50 时,骰 5d10
,否则骰 3d10
。当投骰结果大于 20 时,提示玩家 成功
。
在之前的章节中,我们已经看到通过指令嵌套与变量来实现读取人物卡和条件判断的例子。但当判断条件复杂时,这样的指令就显得不太直观,也难以完全自定义返回消息的内容。那么就让我们使用插件来实现上面这个虚构的规则吧~
我们期望的效果如下:
User: .test 力量
Bot: User 🎲 力量(60) 5d10: [3, 5, 5, 8, 3] = 24 成功
首先我们需要考虑如何得到玩家人物卡中某个特定技能的值。在 handler
方法中输入如下内容:
// 自定义回复处理方法
handler(env, matchGroup) {
// 提取用户输入的技能名,例如用户输入 '.test 力量' 则提取出 '力量'
const skillName = matchGroup.content.trim()
// 尝试获取用户的人物卡
const card = apis.getCard(env)
if (!card) {
return '你没有人物卡!'
}
// 尝试获取用户的技能
const entry = card.getEntry(skillName)
if (!entry) {
return '人物卡中没有这个技能!'
}
// 用户的技能值
const skillValue = entry.value
return '你的' + skillName + '值是' + skillValue
}
我们可以通过 matchGroup.content
获得玩家输入的 .test
指令之后的内容(如果你熟悉正则表达式,content
其实对应的是正则表达式中的命名捕获组)。并通过 trim
去除前后的空格,得到干净的技能名。
随后我们可以尝试获取人物卡中这项技能的值,具体逻辑看注释即可。
其次我们需要根据技能值的不同大小决定怎么投骰。让我们继续完善代码:
// 自定义回复处理方法
handler(env, matchGroup) {
... 省略前面的代码
// 用户的技能值
const skillValue = entry.value
// 根据技能值大小,决定投骰的表达式
let roll
if (skillValue > 50) {
roll = apis.roll('5d10')
} else {
roll = apis.roll('3d10')
}
// 展示投骰的结果
return '投骰的结果是' + roll.total
}
我们已经能够投骰了,接下来就根据投骰的结果来拼装机器人返回的消息吧。
// 自定义回复处理方法
handler(env, matchGroup) {
...省略前面的代码
// 展示投骰的结果
let result = env.nick + ' 🎲 ' + skillName + '(' + skillValue + ') ' + roll.output
// 投骰结果大于 20 时,提示成功
if (roll.total > 20) {
result = result + ' 成功'
}
// 发消息回子频道
return result
}
其中 env.nick
代表用户的昵称,roll.output
包含整个投骰过程的描述,例如 5d10: [3, 5, 5, 8, 3] = 24
这段内容。
这样我们的小插件就完成了。如果你遇到了任何问题,可以参考如下完整代码:
点击查看完整代码
module.exports = (apis) => {
return {
// 插件 id,与文件夹名字保持一致
id: 'com.example.myplugin',
// 插件名称,用于展示
name: '测试插件',
// 插件版本,自己控制即可
version: 1,
// 自定义回复
customReply: [
{
// 自定义回复 id,可以自己起,在同一个插件内部唯一即可
id: 'test',
// 自定义回复命令名称,用于展示
name: '测试命令',
// 自定义回复命令描述,用于展示,可以在此处介绍用法
description: '使用【.test 技能】进行检定',
// 自定义回复匹配规则,这个例子匹配所有 .testXXXX 格式的消息
command: '^test(?<content>.*)',
trigger: 'regex',
// 自定义回复处理方法
handler(env, matchGroup) {
// 提取用户输入的技能名,例如用户输入 '.test 力量' 则提取出 '力量'
const skillName = matchGroup.content.trim()
// 尝试获取用户的人物卡
const card = apis.getCard(env)
if (!card) {
return '你没有人物卡!'
}
// 尝试获取用户的技能
const entry = card.getEntry(skillName)
if (!entry) {
return '人物卡中没有这个技能!'
}
// 用户的技能值
const skillValue = entry.value
// 根据技能值大小,决定投骰的表达式
let roll
if (skillValue > 50) {
roll = apis.roll('5d10')
} else {
roll = apis.roll('3d10')
}
// 展示投骰的结果
let result = env.nick + ' 🎲 ' + skillName + '(' + skillValue + ') ' + roll.output
// 投骰结果大于 20 时,提示成功
if (roll.total > 20) {
result = result + ' 成功'
}
// 发消息回子频道
return result
}
}
]
}
}
测试插件
让我们实际试用一下这个插件。打开软件,点击【配置】->【自定义回复】,勾选我们的测试插件。最后别忘了点击【保存】。
如果你的软件之前已经在运行了,你需要重启一下软件,或在【插件管理】菜单中点击【重载所有插件】,以确保加载了最新的代码。
测试一下:
这很合理,因为我们还没有关联人物卡。那么我们就给自己关联一张人物卡再试试:
很好,看上去我们已经成功达到了设计的目标!
接下来,你或许会根据实际的需求进一步修改插件的代码。要记得修改代码后都需要重启软件或点击重载插件后才能生效。
分享插件
编写完插件后,我们可以自己用,也可以分享给其他有需要的人使用。
由于每个插件都是一个文件夹,我们只要把文件夹打个压缩包发给别人,别人解压到 plugins
文件夹下,就可以使用了。
如果你开发了一个插件,非常建议联系我们,把你的插件添加到官方仓库中,让更多人知道。我们也可以总结大家开发的插件,更进一步优化插件体系。