开发文档

RicheyJang ... 2021-12-3 大约 7 分钟

# 开发文档

# 基本介绍

派蒙Bot使用插件式统一管理,用以提供帮助、限流、鉴权等能力。开发语言Golang,因此理论性能要比使用python开发的机器人高出不少。

推荐Golang教程:iswbm (opens new window)

# 插件开发

插件管理器所在包:manager

开发一个新插件时,请在plugins目录下新建一个目录,其目录名(包名)会被manager自动沿用为插件Key,用于进行插件配置、限流管理等等。

例子

例如,开发一个复读插件,在plugins目录下新建目录echo,在echo下创建echo.go文件,它的插件Key将会自动设为echo,在config-plugin.yaml文件中,该插件的配置项将会以echo为根。

# 初始化

首先,为你的新插件声明一个manager.PluginInfo类型变量,用以描述插件信息。

manager.PluginInfo结构定义:

// PluginInfo 插件信息
type PluginInfo struct {
	Name        string // [必填] 插件名称
	Usage       string // [必填] 插件用法描述:会在插件帮助详情中展示

	SuperUsage  string // [选填] 插件超级用户用法描述
	Classify    string // [选填] 插件分类,为空时代表默认分类
	IsPassive   bool   // [选填] 是否为被动插件:在帮助中被标识为被动功能;
	IsSuperOnly bool   // [选填] 是否为超级用户专属插件:若true,消息性事件会自动加上SuperOnly检查;在帮助中只有超级用户私聊可见;
	AdminLevel  int    // [选填] 群管理员使用最低级别: 0 表示非群管理员专用插件 >0 表示数字越低,权限要求越高;会在帮助中进行标识;配置文件中 插件Key.adminlevel 配置项优先级高于此项
}
1
2
3
4
5
6
7
8
9
10
11

随后,声明一个*manager.PluginProxy类型变量,用来作为插件代理 使用插件管理器提供的各项能力。

接下来,在init函数中,调用manager.RegisterPlugin(info manager.PluginInfo)函数来初始化你的*manager.PluginProxy

至此,你的新插件(以echo为例)应该是这个样子:

package echo

import (
	"github.com/RicheyJang/PaimengBot/manager"
	
	zero "github.com/wdvxdr1123/ZeroBot"
)

var info = manager.PluginInfo{ // [1] 声明插件信息结构变量
	Name: "复读",
	Usage: `
用法:
	echo [复读内容]:将echo后的内容进行复读
`,
}
var proxy *manager.PluginProxy // [2] 声明插件代理变量

func init() {
	proxy = manager.RegisterPlugin(info) // [3] 使用插件信息初始化插件代理
	if proxy == nil { // 若初始化失败,请return,失败原因会在日志中打印
		return
	}
	// [4] 此处进行其它初始化操作
}

// [5] 其它代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 定义事件处理

Onebot基础知识

派蒙Bot全面基于Onebot v11协议 (opens new window),Onebot采用事件与API系统:收到新消息、收到好友请求、收到群邀请等等皆为由前端(如go-cqhttp)推送给后端(如派蒙Bot)的事件,后端会对事件进行处理,并可以通过一定的方式来调用前端API:如发送各类消息、撤回消息、同意好友请求等等。

proxy初始化后,便可以使用proxy提供的能力了。

最常用的便是定义某个事件的处理函数(Handler)。例如,想要在收到以复读echo开头的消息时进行一定的处理,便可以:

proxy.OnCommands([]string{"复读","echo"}).SetBlock(true).FirstPriority().Handle(EchoHandler)
1

OnCommands这类以On开头的函数为事件注册函数,会针对某一类事件进行注册以提供事件处理函数。同类型的函数有:

func (p *PluginProxy) OnCommands(cmd []string, rules ...zero.Rule) *zero.Matcher // 处理消息内容以cmd中任意一项开头的消息事件,消息内容中剩下的部分则为参数
func (p *PluginProxy) OnRegex(reg string, rules ...zero.Rule) *zero.Matcher // 处理消息内容满足reg正则的消息事件
func (p *PluginProxy) OnFullMatch(cmd []string, rules ...zero.Rule) *zero.Matcher // 处理消息内容与cmd中任意一项完全相同的消息事件

func (p *PluginProxy) On(tp string, rules ...zero.Rule) *zero.Matcher // 处理[tp]类型事件
func (p *PluginProxy) OnMessage(rules ...zero.Rule) *zero.Matcher // 处理消息类型事件
func (p *PluginProxy) OnRequest(rules ...zero.Rule) *zero.Matcher // 处理Request类型事件
func (p *PluginProxy) OnNotice(rules ...zero.Rule) *zero.Matcher // 处理Notice类型事件
// 上述三种类型参见Onebot协议
1
2
3
4
5
6
7
8
9

可以注意到,每个事件注册函数参数中都包含了rules ...zero.Rule,它是一系列过滤函数,函数定义如下,只有所有过滤函数皆返回true,该事件才会被你所指定的Handler处理。

func(ctx *zero.Ctx) bool
1

回归此前的事件注册,在OnCommand后的各个函数:

SetBlock(true) // 代表经过此Handler处理后,其它事件处理函数不再处理此次事件,一般需要设为true,防止同一事件被多个插件处理
FirstPriority() // 此Handler的优先级,同类型函数还有SetPriority(int)
Handle(EchoHandler) // 指定Handler为EchoHandler函数
1
2
3

而一个Handler(例如EchoHandler)的类型应该为:

func (ctx *zero.Ctx)
1

在Handler及Rule的定义中都出现了zero.Ctx,它是ZeroBot框架(一个实现了Onebot协议的Go后端框架,派蒙Bot是对它的上层封装)所提供的事件上下文,常用属性或方法有:

ctx.Event // 此次事件的内容
ctx.MessageString() string // 获取消息事件的消息纯文本内容
ctx.Send(message interface{}) int64 // 快捷向消息发送者发送一条消息,返回消息ID
ctx.SendChain(message ...message.MessageSegment) int64 // 以消息链的方式向消息发送者发送一条消息
// 其它请参见zerobot.Ctx定义
1
2
3
4
5

例如,上述例子的EchoHandler,其内容如下:

func EchoHandler(ctx *zero.Ctx) {
	str := utils.GetArgs(ctx) // 派蒙Bot提供的工具函数,用于获取此次事件的消息参数内容
	tm := proxy.GetConfigInt64("times") // proxy提供的统一配置项管理功能,此函数用于获取int64类型的times配置项值
	for i := int64(0); i < tm; i++ {
		ctx.Send(str)
	}
}
1
2
3
4
5
6
7

重要

派蒙Bot作为一个较为完毕的Onebot后端框架,无论是插件代理proxy还是工具包utils,都提供了大量固有能力:如统一插件配置管理、定时任务、插件用户级锁、关系型数据库、K-V数据库、快捷获取指定消息内容、2D绘图、可重试的Http Client、字符串处理等等,无需自己造轮子,详细请参见proxy文档工具包文档

# 完整复读插件示例:

最后,写完一个插件后,别忘在cmd/main.go文件的import中引用该插件所在的包,以启用该插件,例如:

// cmd/main.go中
import (
	// ...其它包
	_ "github.com/RicheyJang/PaimengBot/plugins/echo"
)
1
2
3
4
5
package echo

import (
	"github.com/RicheyJang/PaimengBot/manager"
	"github.com/RicheyJang/PaimengBot/utils"
	
	zero "github.com/wdvxdr1123/ZeroBot"
)

var info = manager.PluginInfo{ // [1] 声明插件信息结构变量
	Name: "复读",
	Usage: `
用法:
	echo [复读内容]:将echo后的内容进行复读
`,
}
var proxy *manager.PluginProxy // [2] 声明插件代理变量

func init() {
	proxy = manager.RegisterPlugin(info) // [3] 使用插件信息初始化插件代理
	if proxy == nil { // 若初始化失败,请return,失败原因会在日志中打印
		return
	}
	proxy.OnCommands([]string{"复读","echo"}).SetBlock(true).FirstPriority().Handle(EchoHandler) // [4] 注册事件处理函数
	proxy.AddConfig("times", 2) // proxy提供的统一配置项管理功能,此函数新增一个配置项times,默认值为2
}

// [5] Handler实现
func EchoHandler(ctx *zero.Ctx) {
	str := utils.GetArgs(ctx) // 派蒙Bot提供的工具函数,用于获取此次事件的消息参数内容
	tm := proxy.GetConfigInt64("times") // proxy提供的统一配置项管理功能,此函数用于获取int64类型的times配置项值
	for i := int64(0); i < tm; i++ {
		ctx.Send(str)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 事件处理Hook

此外,还可以为事件处理添加钩子函数,已有插件中的限流、鉴权、统计等便是基于钩子函数实现的。

挂载点有两处:

  1. 进入具体的事件处理函数前
  2. 事件处理函数处理完成后
  3. plugin-config配置文件有所变更时

第一处使用:

manager.AddPreHook(hook ...PluginHook)
1

第二处使用:

manager.AddPostHook(hook ...PluginHook)
1

第三处使用:

manager.WhenConfigFileChange(hook ...FileHook)
1

其中,PluginHook定义为:

func(condition *manager.PluginCondition, ctx *zero.Ctx) error
1

condition包含了即将被调用的插件信息:

type PluginCondition struct {
	PluginInfo            // 插件信息(由插件提供,只读)
	Key        string     // 插件Key
	NormalCmd  [][]string // 该插件的普通用户命令集
	SuperCmd   [][]string // 该插件的超级用户专用命令集
}
1
2
3
4
5
6

ctx为此次事件的zerobot上下文。

注意!对于返回值,若返回的error不为nil,控制器将立马终止此次事件,后续一切事件处理函数或其它钩子都将不会被调用。

此外,manager还提供了一些PluginCondition相关的工具函数:

manager.GetAllPluginConditions() []*PluginCondition // 返回所有插件的PluginCondition
GetPluginConditionByKey(key string) *PluginCondition // 通过插件Key查询该插件的PluginCondition
1
2

FileHook定义为:

func(event fsnotify.Event) error // 该error仅会在日志中有所体现
1
上次编辑于: 2022年3月4日 12:33
贡献者: RicheyJang