refactor: modernize codebase for Go 1.26 and fix adapter issues
- Upgrade Go version from 1.24 to 1.26.1 with updated dependencies
- Replace interface{} with any type throughout codebase
- Add message deduplication in OneBot11 adapter to prevent duplicate event processing
- URL-encode access token in WebSocket connections to handle special characters
- Remove duplicate bot startup hooks in DI providers (now handled by botManager.StartAll)
- Use slices.Clone and errgroup.Go patterns for cleaner concurrent code
- Apply omitzero JSON tags for optional fields (Go 1.24+ feature)
This commit is contained in:
@@ -3,6 +3,7 @@ package onebot11
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -28,6 +29,10 @@ type Adapter struct {
|
||||
wsConnection *net.WebSocketConnection
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// 消息去重
|
||||
processedMessages map[int64]time.Time
|
||||
messageMu sync.RWMutex
|
||||
}
|
||||
|
||||
// Config OneBot11配置
|
||||
@@ -65,16 +70,20 @@ func NewAdapter(config *Config, logger *zap.Logger, wsManager *net.WebSocketMana
|
||||
}
|
||||
|
||||
adapter := &Adapter{
|
||||
config: config,
|
||||
logger: logger.Named("onebot11"),
|
||||
wsManager: wsManager,
|
||||
wsWaiter: NewWSResponseWaiter(timeout, logger),
|
||||
eventBus: eventBus,
|
||||
selfID: config.SelfID,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
config: config,
|
||||
logger: logger.Named("onebot11"),
|
||||
wsManager: wsManager,
|
||||
wsWaiter: NewWSResponseWaiter(timeout, logger),
|
||||
eventBus: eventBus,
|
||||
selfID: config.SelfID,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
processedMessages: make(map[int64]time.Time),
|
||||
}
|
||||
|
||||
// 启动定期清理过期消息的 goroutine
|
||||
go adapter.cleanupProcessedMessages()
|
||||
|
||||
// 如果使用HTTP连接,初始化HTTP客户端
|
||||
if config.ConnectionType == "http" && config.HTTPUrl != "" {
|
||||
adapter.httpClient = NewHTTPClient(config.HTTPUrl, config.AccessToken, timeout, logger)
|
||||
@@ -83,6 +92,43 @@ func NewAdapter(config *Config, logger *zap.Logger, wsManager *net.WebSocketMana
|
||||
return adapter
|
||||
}
|
||||
|
||||
// cleanupProcessedMessages 定期清理过期的已处理消息记录
|
||||
func (a *Adapter) cleanupProcessedMessages() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.messageMu.Lock()
|
||||
now := time.Now()
|
||||
for msgID, processedAt := range a.processedMessages {
|
||||
if now.Sub(processedAt) > 10*time.Minute {
|
||||
delete(a.processedMessages, msgID)
|
||||
}
|
||||
}
|
||||
a.messageMu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isMessageProcessed 检查消息是否已处理
|
||||
func (a *Adapter) isMessageProcessed(messageID int64) bool {
|
||||
a.messageMu.RLock()
|
||||
_, exists := a.processedMessages[messageID]
|
||||
a.messageMu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// markMessageProcessed 标记消息为已处理
|
||||
func (a *Adapter) markMessageProcessed(messageID int64) {
|
||||
a.messageMu.Lock()
|
||||
a.processedMessages[messageID] = time.Now()
|
||||
a.messageMu.Unlock()
|
||||
}
|
||||
|
||||
// Name 获取协议名称
|
||||
func (a *Adapter) Name() string {
|
||||
return "OneBot"
|
||||
@@ -174,7 +220,7 @@ func (a *Adapter) GetSelfID() string {
|
||||
}
|
||||
|
||||
// SendAction 发送动作
|
||||
func (a *Adapter) SendAction(ctx context.Context, action protocol.Action) (map[string]interface{}, error) {
|
||||
func (a *Adapter) SendAction(ctx context.Context, action protocol.Action) (map[string]any, error) {
|
||||
// 序列化为OneBot11格式
|
||||
data, err := a.SerializeAction(action)
|
||||
if err != nil {
|
||||
@@ -228,7 +274,7 @@ func (a *Adapter) SerializeAction(action protocol.Action) ([]byte, error) {
|
||||
}
|
||||
|
||||
// 复制参数并转换消息链
|
||||
params := make(map[string]interface{})
|
||||
params := make(map[string]any)
|
||||
for k, v := range action.GetParams() {
|
||||
// 检查是否是消息链
|
||||
if k == "message" {
|
||||
@@ -259,10 +305,10 @@ func (a *Adapter) connectWebSocket(ctx context.Context) error {
|
||||
zap.String("url", a.config.WSUrl),
|
||||
zap.Bool("has_token", a.config.AccessToken != ""))
|
||||
|
||||
// 添加访问令牌到URL
|
||||
// 添加访问令牌到URL(需要对token进行URL编码以处理特殊字符)
|
||||
wsURL := a.config.WSUrl
|
||||
if a.config.AccessToken != "" {
|
||||
wsURL += "?access_token=" + a.config.AccessToken
|
||||
wsURL += "?access_token=" + url.QueryEscape(a.config.AccessToken)
|
||||
a.logger.Debug("Added access token to WebSocket URL")
|
||||
}
|
||||
|
||||
@@ -342,7 +388,7 @@ func (a *Adapter) connectHTTPPost(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// sendActionWebSocket 通过WebSocket发送动作
|
||||
func (a *Adapter) sendActionWebSocket(data []byte) (map[string]interface{}, error) {
|
||||
func (a *Adapter) sendActionWebSocket(data []byte) (map[string]any, error) {
|
||||
if a.wsConnection == nil {
|
||||
return nil, fmt.Errorf("websocket connection not established")
|
||||
}
|
||||
@@ -383,7 +429,7 @@ func (a *Adapter) sendActionWebSocket(data []byte) (map[string]interface{}, erro
|
||||
}
|
||||
|
||||
// sendActionHTTP 通过HTTP发送动作
|
||||
func (a *Adapter) sendActionHTTP(data []byte) (map[string]interface{}, error) {
|
||||
func (a *Adapter) sendActionHTTP(data []byte) (map[string]any, error) {
|
||||
if a.httpClient == nil {
|
||||
return nil, fmt.Errorf("http client not initialized")
|
||||
}
|
||||
@@ -439,7 +485,7 @@ func (a *Adapter) handleWebSocketMessages() {
|
||||
zap.String("preview", string(message[:min(len(message), 200)])))
|
||||
|
||||
// 尝试解析为响应(先检查是否有 echo 字段)
|
||||
var tempMap map[string]interface{}
|
||||
var tempMap map[string]any
|
||||
if err := sonic.Unmarshal(message, &tempMap); err == nil {
|
||||
if echo, ok := tempMap["echo"].(string); ok && echo != "" {
|
||||
// 有 echo 字段,说明是API响应
|
||||
@@ -478,6 +524,24 @@ func (a *Adapter) handleWebSocketMessages() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 消息去重检查(基于 message_id)
|
||||
data := event.GetData()
|
||||
// message_id 可能是 int32 或 int64
|
||||
var messageID int64
|
||||
if mid, ok := data["message_id"].(int32); ok && mid > 0 {
|
||||
messageID = int64(mid)
|
||||
} else if mid, ok := data["message_id"].(int64); ok && mid > 0 {
|
||||
messageID = mid
|
||||
}
|
||||
if messageID > 0 {
|
||||
if a.isMessageProcessed(messageID) {
|
||||
a.logger.Warn("Duplicate message detected, skipping",
|
||||
zap.Int64("message_id", messageID))
|
||||
continue
|
||||
}
|
||||
a.markMessageProcessed(messageID)
|
||||
}
|
||||
|
||||
// 发布事件到事件总线
|
||||
a.logger.Info("Publishing event to event bus",
|
||||
zap.String("event_type", string(event.GetType())),
|
||||
|
||||
Reference in New Issue
Block a user