chore: update dependencies and improve bot configuration

- Upgrade Go version to 1.24.0 and update toolchain.
- Update various dependencies in go.mod and go.sum, including:
  - Upgrade `fasthttp/websocket` to v1.5.12
  - Upgrade `fsnotify/fsnotify` to v1.9.0
  - Upgrade `valyala/fasthttp` to v1.58.0
  - Add new dependencies for `bytedance/sonic` and `google/uuid`.
- Refactor bot configuration in config.toml to support multiple bot protocols, including "milky" and "onebot11".
- Modify internal configuration structures to accommodate new bot settings.
- Enhance event dispatcher with metrics tracking and asynchronous processing capabilities.
- Implement WebSocket connection management with heartbeat and reconnection logic.
- Update server handling for bot management and event publishing.
This commit is contained in:
lafay
2026-01-05 00:40:09 +08:00
parent ac0dfb64c9
commit 44fe05ff62
30 changed files with 6311 additions and 182 deletions

View File

@@ -1,12 +1,13 @@
package net
import (
"fmt"
"net"
"strconv"
"cellbot/internal/engine"
"cellbot/internal/protocol"
"github.com/bytedance/sonic"
"github.com/valyala/fasthttp"
"go.uber.org/zap"
)
@@ -18,17 +19,20 @@ type Server struct {
logger *zap.Logger
botManager *protocol.BotManager
eventBus *engine.EventBus
wsManager *WebSocketManager
server *fasthttp.Server
}
// NewServer 创建HTTP服务器
func NewServer(host string, port int, logger *zap.Logger, botManager *protocol.BotManager, eventBus *engine.EventBus) *Server {
wsManager := NewWebSocketManager(logger, eventBus)
return &Server{
host: host,
port: port,
logger: logger.Named("server"),
botManager: botManager,
eventBus: eventBus,
wsManager: wsManager,
}
}
@@ -105,28 +109,55 @@ func (s *Server) handleHealth(ctx *fasthttp.RequestCtx) {
ctx.SetBodyString(`{"status":"ok"}`)
}
// BotInfo 机器人信息结构
type BotInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
Status string `json:"status"`
SelfID string `json:"self_id"`
Connected bool `json:"connected"`
}
// handleBots 获取机器人列表
func (s *Server) handleBots(ctx *fasthttp.RequestCtx) {
bots := s.botManager.GetAll()
ctx.SetContentType("application/json")
if len(bots) == 0 {
ctx.SetBodyString(`{"bots":[]}`)
// 构建机器人信息列表
botInfos := make([]BotInfo, 0, len(bots))
for _, bot := range bots {
botInfos = append(botInfos, BotInfo{
ID: bot.GetID(),
Name: bot.Name(),
Version: bot.Version(),
Status: string(bot.GetStatus()),
SelfID: bot.GetSelfID(),
Connected: bot.IsConnected(),
})
}
// 序列化为JSON
response := map[string]interface{}{
"bots": botInfos,
"count": len(botInfos),
}
data, err := sonic.Marshal(response)
if err != nil {
s.logger.Error("Failed to marshal bots response", zap.Error(err))
ctx.Error("Internal Server Error", fasthttp.StatusInternalServerError)
return
}
// 简化实现,实际应该序列化完整信息
response := `{"bots":[`
for i, bot := range bots {
if i > 0 {
response += ","
}
response += fmt.Sprintf(`{"id":"%s","name":"%s","status":"%s"}`,
bot.GetID(), bot.Name(), bot.GetStatus())
}
response += `]}`
ctx.SetBody(data)
}
ctx.SetBodyString(response)
// CreateBotRequest 创建机器人请求
type CreateBotRequest struct {
ID string `json:"id"`
Protocol string `json:"protocol"`
Config map[string]interface{} `json:"config"`
}
// handleCreateBot 创建机器人
@@ -137,7 +168,73 @@ func (s *Server) handleCreateBot(ctx *fasthttp.RequestCtx) {
}
ctx.SetContentType("application/json")
ctx.SetBodyString(`{"message":"Bot creation not implemented yet"}`)
// 解析请求体
var req CreateBotRequest
if err := sonic.Unmarshal(ctx.PostBody(), &req); err != nil {
s.logger.Error("Failed to parse create bot request", zap.Error(err))
response := map[string]interface{}{
"success": false,
"error": "Invalid request body",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBody(data)
return
}
// 验证必需字段
if req.ID == "" {
response := map[string]interface{}{
"success": false,
"error": "Bot ID is required",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBody(data)
return
}
if req.Protocol == "" {
response := map[string]interface{}{
"success": false,
"error": "Protocol is required",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBody(data)
return
}
// 检查机器人是否已存在
if _, exists := s.botManager.Get(req.ID); exists {
response := map[string]interface{}{
"success": false,
"error": "Bot with this ID already exists",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusConflict)
ctx.SetBody(data)
return
}
// TODO: 根据协议类型创建相应的机器人实例
// 这里需要协议工厂来创建不同类型的机器人
// 目前返回成功但提示需要实现协议适配器
s.logger.Info("Bot creation requested",
zap.String("bot_id", req.ID),
zap.String("protocol", req.Protocol))
response := map[string]interface{}{
"success": true,
"message": "Bot creation queued (protocol adapter implementation required)",
"bot_id": req.ID,
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusAccepted)
ctx.SetBody(data)
}
// handlePublishEvent 发布事件
@@ -147,19 +244,169 @@ func (s *Server) handlePublishEvent(ctx *fasthttp.RequestCtx) {
return
}
// 解析请求体并发布事件
// 这里简化实现,实际应该解析JSON并创建Event
ctx.SetContentType("application/json")
ctx.SetBodyString(`{"message":"Event published"}`)
// 解析请求体为事件对象
var event protocol.BaseEvent
if err := sonic.Unmarshal(ctx.PostBody(), &event); err != nil {
s.logger.Error("Failed to parse event", zap.Error(err))
response := map[string]interface{}{
"success": false,
"error": "Invalid event format",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBody(data)
return
}
// 验证事件类型
if event.Type == "" {
response := map[string]interface{}{
"success": false,
"error": "Event type is required",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBody(data)
return
}
// 如果没有时间戳,使用当前时间
if event.Timestamp == 0 {
event.Timestamp = ctx.Time().Unix()
}
// 确保Data字段不为nil
if event.Data == nil {
event.Data = make(map[string]interface{})
}
s.logger.Info("Publishing event",
zap.String("type", string(event.Type)),
zap.String("detail_type", event.DetailType),
zap.String("self_id", event.SelfID))
// 发布到事件总线
s.eventBus.Publish(&event)
response := map[string]interface{}{
"success": true,
"message": "Event published successfully",
"timestamp": event.Timestamp,
}
data, _ := sonic.Marshal(response)
ctx.SetBody(data)
}
// handleSubscribeEvent 订阅事件
// handleSubscribeEvent 订阅事件WebSocket升级
func (s *Server) handleSubscribeEvent(ctx *fasthttp.RequestCtx) {
if string(ctx.Method()) != "GET" {
// 检查是否为WebSocket升级请求
if !ctx.IsGet() {
ctx.Error("Method Not Allowed", fasthttp.StatusMethodNotAllowed)
return
}
ctx.SetContentType("application/json")
ctx.SetBodyString(`{"message":"Event subscription not implemented yet"}`)
// 检查是否为WebSocket升级请求
if string(ctx.Request.Header.Peek("Upgrade")) != "websocket" {
ctx.SetContentType("application/json")
response := map[string]interface{}{
"success": false,
"error": "WebSocket upgrade required",
"message": "This endpoint requires WebSocket connection",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusBadRequest)
ctx.SetBody(data)
return
}
// 获取订阅者ID可选
subscriberID := string(ctx.QueryArgs().Peek("subscriber_id"))
if subscriberID == "" {
subscriberID = "event-subscriber"
}
// 获取要订阅的事件类型(可选,为空则订阅所有类型)
eventTypeStr := string(ctx.QueryArgs().Peek("event_type"))
s.logger.Info("WebSocket event subscription request",
zap.String("subscriber_id", subscriberID),
zap.String("event_type", eventTypeStr),
zap.String("remote_addr", ctx.RemoteAddr().String()))
// 升级为WebSocket连接
wsConn, err := s.wsManager.UpgradeWebSocket(ctx)
if err != nil {
s.logger.Error("Failed to upgrade WebSocket", zap.Error(err))
ctx.SetContentType("application/json")
response := map[string]interface{}{
"success": false,
"error": "Failed to upgrade WebSocket connection",
}
data, _ := sonic.Marshal(response)
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
ctx.SetBody(data)
return
}
// 订阅所有主要事件类型
eventTypes := []protocol.EventType{
protocol.EventTypeMessage,
protocol.EventTypeNotice,
protocol.EventTypeRequest,
protocol.EventTypeMeta,
protocol.EventTypeMessageSent,
protocol.EventTypeNoticeSent,
protocol.EventTypeRequestSent,
}
// 如果指定了事件类型,只订阅该类型
if eventTypeStr != "" {
eventTypes = []protocol.EventType{protocol.EventType(eventTypeStr)}
}
// 为每种事件类型创建订阅
for _, eventType := range eventTypes {
eventChan := s.eventBus.Subscribe(eventType, nil) // nil filter means accept all events
// 启动goroutine监听事件并发送到WebSocket
go func(ch chan protocol.Event, et protocol.EventType) {
for {
select {
case event, ok := <-ch:
if !ok {
return
}
// 序列化事件为JSON
data, err := sonic.Marshal(event)
if err != nil {
s.logger.Error("Failed to marshal event", zap.Error(err))
continue
}
// 发送到WebSocket连接
if err := wsConn.SendMessage(data); err != nil {
s.logger.Error("Failed to send event to WebSocket",
zap.String("conn_id", wsConn.ID),
zap.Error(err))
// 连接可能已断开,取消订阅
s.eventBus.Unsubscribe(et, ch)
return
}
case <-wsConn.ctx.Done():
// 连接关闭,取消订阅
s.eventBus.Unsubscribe(et, ch)
return
}
}
}(eventChan, eventType)
}
s.logger.Info("WebSocket event subscription established",
zap.String("conn_id", wsConn.ID),
zap.String("subscriber_id", subscriberID))
}