# CellBot 插件开发指南 ## 快速开始 CellBot 提供了类似 ZeroBot 风格的插件注册方式,可以在一个包内注册多个处理函数。 ### ZeroBot 风格(推荐) 在 `init` 函数中使用 `OnXXX().Handle()` 注册处理函数,一个包内可以注册多个: ```go package echo import ( "context" "cellbot/internal/engine" "cellbot/internal/protocol" "go.uber.org/zap" ) func init() { // 处理私聊消息 engine.OnPrivateMessage(). Handle(func(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error { data := event.GetData() message := data["message"] userID := data["user_id"] // 获取 bot 实例 bot, _ := botManager.Get(event.GetSelfID()) // 发送回复 action := &protocol.BaseAction{ Type: protocol.ActionTypeSendPrivateMessage, Params: map[string]interface{}{ "user_id": userID, "message": message, }, } return bot.SendAction(ctx, action) }) // 可以继续注册更多处理函数 engine.OnGroupMessage(). Handle(func(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error { // 处理群消息 return nil }) // 处理命令 engine.OnCommand("/help"). Handle(func(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error { // 处理 /help 命令 return nil }) } ``` **注意**:需要在 `internal/di/providers.go` 中导入插件包以触发 `init` 函数: ```go import ( _ "cellbot/internal/plugins/echo" // 导入插件以触发 init ) ``` ### 方式二:传统方式 实现 `protocol.EventHandler` 接口: ```go package myplugin import ( "context" "cellbot/internal/protocol" "go.uber.org/zap" ) type MyPlugin struct { logger *zap.Logger botManager *protocol.BotManager } func NewMyPlugin(logger *zap.Logger, botManager *protocol.BotManager) *MyPlugin { return &MyPlugin{ logger: logger.Named("my-plugin"), botManager: botManager, } } func (p *MyPlugin) Name() string { return "MyPlugin" } func (p *MyPlugin) Description() string { return "我的插件" } func (p *MyPlugin) Priority() int { return 100 } func (p *MyPlugin) Match(event protocol.Event) bool { return event.GetType() == protocol.EventTypeMessage } func (p *MyPlugin) Handle(ctx context.Context, event protocol.Event) error { // 处理逻辑 return nil } ``` ## 内置匹配器 提供了以下便捷的匹配器函数: - `engine.OnPrivateMessage()` - 匹配私聊消息 - `engine.OnGroupMessage()` - 匹配群消息 - `engine.OnMessage()` - 匹配所有消息 - `engine.OnNotice()` - 匹配通知事件 - `engine.OnRequest()` - 匹配请求事件 - `engine.OnCommand(prefix)` - 匹配命令(以指定前缀开头) - `engine.OnPrefix(prefix)` - 匹配以指定前缀开头的消息 - `engine.OnSuffix(suffix)` - 匹配以指定后缀结尾的消息 - `engine.OnKeyword(keyword)` - 匹配包含指定关键词的消息 - `engine.On(matchFunc)` - 自定义匹配器 ### 自定义匹配器 ```go func init() { engine.On(func(event protocol.Event) bool { // 自定义匹配逻辑 if event.GetType() != protocol.EventTypeMessage { return false } data := event.GetData() message, ok := data["raw_message"].(string) if !ok { return false } // 只匹配以 "/" 开头的消息 return len(message) > 0 && message[0] == '/' }). Priority(50). // 可以设置优先级 Handle(func(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error { // 处理命令 return nil }) } ``` ## 注册插件 插件通过 `init` 函数自动注册,只需在 `internal/di/providers.go` 中导入插件包: ```go import ( _ "cellbot/internal/plugins/echo" // 导入插件以触发 init _ "cellbot/internal/plugins/other" // 可以导入多个插件包 ) ``` 插件会在应用启动时自动加载,无需手动注册。 ## 插件优先级 优先级数值越小,越先执行。建议: - 0-50: 高优先级(预处理、权限检查等) - 51-100: 中等优先级(普通功能插件) - 101-200: 低优先级(日志记录、统计等) ## 完整示例 ### 示例 1:关键词回复插件 ```go package keyword import ( "context" "cellbot/internal/engine" "cellbot/internal/protocol" "go.uber.org/zap" ) func init() { keywords := map[string]string{ "你好": "你好呀!", "再见": "再见~", "帮助": "发送 /help 查看帮助", } engine.OnMessage(). Priority(80). Handle(func(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error { data := event.GetData() message, ok := data["raw_message"].(string) if !ok { return nil } // 检查关键词 reply, found := keywords[message] if !found { return nil } // 获取 bot 和用户信息 bot, _ := botManager.Get(event.GetSelfID()) userID := data["user_id"] // 发送回复 action := &protocol.BaseAction{ Type: protocol.ActionTypeSendPrivateMessage, Params: map[string]interface{}{ "user_id": userID, "message": reply, }, } _, err := bot.SendAction(ctx, action) return err }) } ``` ### 示例 2:命令插件 ```go func RegisterCommandPlugin(registry *engine.PluginRegistry, botManager *protocol.BotManager, logger *zap.Logger) { plugin := engine.NewPlugin("CommandPlugin"). Description("命令处理插件"). Priority(50). Match(func(event protocol.Event) bool { if event.GetType() != protocol.EventTypeMessage { return false } data := event.GetData() message, ok := data["raw_message"].(string) return ok && len(message) > 0 && message[0] == '/' }). Handle(func(ctx context.Context, event protocol.Event) error { data := event.GetData() message := data["raw_message"].(string) userID := data["user_id"] bot, _ := botManager.Get(event.GetSelfID()) var reply string switch message { case "/help": reply = "可用命令:\n/help - 显示帮助\n/ping - 测试连接\n/time - 显示时间" case "/ping": reply = "pong!" case "/time": reply = time.Now().Format("2006-01-02 15:04:05") default: reply = "未知命令,发送 /help 查看帮助" } action := &protocol.BaseAction{ Type: protocol.ActionTypeSendPrivateMessage, Params: map[string]interface{}{ "user_id": userID, "message": reply, }, } _, err := bot.SendAction(ctx, action) return err }). Build() registry.Register(plugin) } ``` ## 最佳实践 1. **使用简化方式**:对于简单插件,使用 `engine.NewPlugin` 构建器 2. **使用传统方式**:对于复杂插件(需要状态管理、配置等),使用传统方式 3. **合理设置优先级**:确保插件按正确顺序执行 4. **错误处理**:在 Handle 函数中妥善处理错误 5. **日志记录**:使用 logger 记录关键操作 6. **避免阻塞**:Handle 函数应快速返回,耗时操作应使用 goroutine ## 插件生命周期 插件在应用启动时注册,在应用运行期间持续监听事件。目前不支持热重载。 ## 调试技巧 1. 使用 `logger.Debug` 记录调试信息 2. 在 Match 函数中添加日志,确认匹配逻辑 3. 检查事件数据结构,确保字段存在 4. 使用 `zap.Any` 打印完整事件数据 ```go logger.Debug("Event data", zap.Any("data", event.GetData())) ```