feat: add rate limiting and improve event handling

- Introduced rate limiting configuration in config.toml with options for enabling, requests per second (RPS), and burst capacity.
- Enhanced event handling in the OneBot11 adapter to ignore messages sent by the bot itself.
- Updated the dispatcher to register rate limit middleware based on configuration settings.
- Refactored WebSocket message handling to support flexible JSON parsing and improved event type detection.
- Removed deprecated echo plugin and associated tests to streamline the codebase.
This commit is contained in:
lafay
2026-01-05 01:00:38 +08:00
parent 44fe05ff62
commit d16261e6bd
11 changed files with 1001 additions and 427 deletions

View File

@@ -1,137 +0,0 @@
package echo
import (
"context"
"cellbot/internal/protocol"
"go.uber.org/zap"
)
// EchoPlugin 回声插件
type EchoPlugin struct {
logger *zap.Logger
botManager *protocol.BotManager
}
// NewEchoPlugin 创建回声插件
func NewEchoPlugin(logger *zap.Logger, botManager *protocol.BotManager) *EchoPlugin {
return &EchoPlugin{
logger: logger.Named("echo-plugin"),
botManager: botManager,
}
}
// Handle 处理事件
func (p *EchoPlugin) Handle(ctx context.Context, event protocol.Event) error {
// 获取事件数据
data := event.GetData()
// 获取消息内容
message, ok := data["message"]
if !ok {
p.logger.Debug("No message field in event")
return nil
}
rawMessage, ok := data["raw_message"].(string)
if !ok {
p.logger.Debug("No raw_message field in event")
return nil
}
// 获取用户ID
userID, ok := data["user_id"]
if !ok {
p.logger.Debug("No user_id field in event")
return nil
}
p.logger.Info("Received private message",
zap.Any("user_id", userID),
zap.String("message", rawMessage))
// 获取 self_id 来确定是哪个 bot
selfID := event.GetSelfID()
// 获取对应的 bot 实例
bot, ok := p.botManager.Get(selfID)
if !ok {
// 如果通过 selfID 找不到,尝试获取第一个 bot
bots := p.botManager.GetAll()
if len(bots) == 0 {
p.logger.Error("No bot instance available")
return nil
}
bot = bots[0]
p.logger.Debug("Using first available bot",
zap.String("bot_id", bot.GetID()))
}
// 构建回复动作
action := &protocol.BaseAction{
Type: protocol.ActionTypeSendPrivateMessage,
Params: map[string]interface{}{
"user_id": userID,
"message": message, // 原封不动返回消息
},
}
p.logger.Info("Sending echo reply",
zap.Any("user_id", userID),
zap.String("reply", rawMessage))
// 发送消息
result, err := bot.SendAction(ctx, action)
if err != nil {
p.logger.Error("Failed to send echo reply",
zap.Error(err),
zap.Any("user_id", userID))
return err
}
p.logger.Info("Echo reply sent successfully",
zap.Any("user_id", userID),
zap.Any("result", result))
return nil
}
// Priority 获取处理器优先级
func (p *EchoPlugin) Priority() int {
return 100 // 中等优先级
}
// Match 判断是否匹配事件
func (p *EchoPlugin) Match(event protocol.Event) bool {
// 只处理私聊消息
eventType := event.GetType()
detailType := event.GetDetailType()
p.logger.Debug("Echo plugin matching event",
zap.String("event_type", string(eventType)),
zap.String("detail_type", detailType))
if eventType != protocol.EventTypeMessage {
p.logger.Debug("Event type mismatch", zap.String("expected", string(protocol.EventTypeMessage)))
return false
}
if detailType != "private" {
p.logger.Debug("Detail type mismatch", zap.String("expected", "private"), zap.String("got", detailType))
return false
}
p.logger.Info("Echo plugin matched event!")
return true
}
// Name 获取插件名称
func (p *EchoPlugin) Name() string {
return "Echo"
}
// Description 获取插件描述
func (p *EchoPlugin) Description() string {
return "回声插件:将私聊消息原封不动返回"
}

View File

@@ -0,0 +1,64 @@
package echo
import (
"context"
"cellbot/internal/engine"
"cellbot/internal/protocol"
"go.uber.org/zap"
)
func init() {
// 在 init 函数中注册多个处理函数(类似 ZeroBot 风格)
// 处理私聊消息
engine.OnPrivateMessage().
Handle(func(ctx context.Context, event protocol.Event, botManager *protocol.BotManager, logger *zap.Logger) error {
// 获取消息内容
data := event.GetData()
message, ok := data["message"]
if !ok {
return nil
}
userID, ok := data["user_id"]
if !ok {
return nil
}
// 获取 bot 实例
selfID := event.GetSelfID()
bot, ok := botManager.Get(selfID)
if !ok {
bots := botManager.GetAll()
if len(bots) == 0 {
logger.Error("No bot instance available")
return nil
}
bot = bots[0]
}
// 发送回复
action := &protocol.BaseAction{
Type: protocol.ActionTypeSendPrivateMessage,
Params: map[string]interface{}{
"user_id": userID,
"message": message,
},
}
_, err := bot.SendAction(ctx, action)
if err != nil {
logger.Error("Failed to send reply", zap.Error(err))
return err
}
logger.Info("Echo reply sent", zap.Any("user_id", userID))
return nil
})
// 可以继续注册更多处理函数
// engine.OnGroupMessage().Handle(...)
// engine.OnCommand("help").Handle(...)
}