- 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)
239 lines
5.4 KiB
Go
239 lines
5.4 KiB
Go
package onebot11
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"cellbot/internal/protocol"
|
||
)
|
||
|
||
// ConvertMessageChainToOB11 将通用消息链转换为 OneBot11 格式
|
||
func ConvertMessageChainToOB11(chain protocol.MessageChain) any {
|
||
if len(chain) == 0 {
|
||
return ""
|
||
}
|
||
|
||
// 转换为 OneBot11 消息段格式
|
||
segments := make([]MessageSegment, 0, len(chain))
|
||
for _, seg := range chain {
|
||
ob11Seg := convertSegmentToOB11(seg)
|
||
if ob11Seg != nil {
|
||
segments = append(segments, *ob11Seg)
|
||
}
|
||
}
|
||
|
||
// 如果只有一个文本消息段,直接返回文本字符串
|
||
if len(segments) == 1 && segments[0].Type == SegmentTypeText {
|
||
if text, ok := segments[0].Data["text"].(string); ok {
|
||
return text
|
||
}
|
||
}
|
||
|
||
return segments
|
||
}
|
||
|
||
// convertSegmentToOB11 将通用消息段转换为 OneBot11 消息段
|
||
func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||
switch seg.Type {
|
||
case protocol.SegmentTypeText:
|
||
// 文本消息段
|
||
return &MessageSegment{
|
||
Type: SegmentTypeText,
|
||
Data: map[string]any{
|
||
"text": seg.Data["text"],
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeMention:
|
||
// @提及(OneBot12 -> OneBot11)
|
||
userID := seg.Data["user_id"]
|
||
return &MessageSegment{
|
||
Type: SegmentTypeAt,
|
||
Data: map[string]any{
|
||
"qq": userID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeAt:
|
||
// OneBot11 兼容格式
|
||
userID, ok := seg.Data["user_id"]
|
||
if !ok {
|
||
userID = seg.Data["qq"]
|
||
}
|
||
return &MessageSegment{
|
||
Type: SegmentTypeAt,
|
||
Data: map[string]any{
|
||
"qq": userID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeImage:
|
||
// 图片消息段
|
||
fileID, ok := seg.Data["file_id"].(string)
|
||
if !ok {
|
||
// 兼容 file 字段
|
||
fileID, _ = seg.Data["file"].(string)
|
||
}
|
||
return &MessageSegment{
|
||
Type: SegmentTypeImage,
|
||
Data: map[string]any{
|
||
"file": fileID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeVoice:
|
||
// 语音消息段(OneBot12 -> OneBot11)
|
||
fileID, ok := seg.Data["file_id"].(string)
|
||
if !ok {
|
||
fileID, _ = seg.Data["file"].(string)
|
||
}
|
||
return &MessageSegment{
|
||
Type: SegmentTypeRecord,
|
||
Data: map[string]any{
|
||
"file": fileID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeRecord:
|
||
// OneBot11 兼容格式
|
||
fileID, ok := seg.Data["file"].(string)
|
||
if !ok {
|
||
fileID, _ = seg.Data["file_id"].(string)
|
||
}
|
||
return &MessageSegment{
|
||
Type: SegmentTypeRecord,
|
||
Data: map[string]any{
|
||
"file": fileID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeVideo:
|
||
// 视频消息段
|
||
fileID, ok := seg.Data["file_id"].(string)
|
||
if !ok {
|
||
fileID, _ = seg.Data["file"].(string)
|
||
}
|
||
return &MessageSegment{
|
||
Type: SegmentTypeVideo,
|
||
Data: map[string]any{
|
||
"file": fileID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeReply:
|
||
// 回复消息段
|
||
messageID := seg.Data["message_id"]
|
||
return &MessageSegment{
|
||
Type: SegmentTypeReply,
|
||
Data: map[string]any{
|
||
"id": messageID,
|
||
},
|
||
}
|
||
|
||
case protocol.SegmentTypeFace:
|
||
// 表情消息段
|
||
faceID := seg.Data["id"]
|
||
return &MessageSegment{
|
||
Type: SegmentTypeFace,
|
||
Data: map[string]any{
|
||
"id": faceID,
|
||
},
|
||
}
|
||
|
||
default:
|
||
// 其他类型,尝试直接转换
|
||
return &MessageSegment{
|
||
Type: seg.Type,
|
||
Data: seg.Data,
|
||
}
|
||
}
|
||
}
|
||
|
||
// ConvertOB11ToMessageChain 将 OneBot11 消息段转换为通用消息链
|
||
func ConvertOB11ToMessageChain(ob11Message any) (protocol.MessageChain, error) {
|
||
chain := protocol.MessageChain{}
|
||
|
||
// 如果是字符串,转换为文本消息段
|
||
if str, ok := ob11Message.(string); ok {
|
||
return protocol.NewMessageChain(protocol.NewTextSegment(str)), nil
|
||
}
|
||
|
||
// 如果是数组,解析为消息段数组
|
||
if segments, ok := ob11Message.([]MessageSegment); ok {
|
||
for _, seg := range segments {
|
||
genericSeg := convertOB11SegmentToGeneric(seg)
|
||
chain = append(chain, genericSeg)
|
||
}
|
||
return chain, nil
|
||
}
|
||
|
||
// 如果是接口数组,尝试转换
|
||
if segments, ok := ob11Message.([]any); ok {
|
||
for _, seg := range segments {
|
||
if segMap, ok := seg.(map[string]any); ok {
|
||
segType, _ := segMap["type"].(string)
|
||
segData, _ := segMap["data"].(map[string]any)
|
||
genericSeg := convertOB11SegmentToGeneric(MessageSegment{
|
||
Type: segType,
|
||
Data: segData,
|
||
})
|
||
chain = append(chain, genericSeg)
|
||
}
|
||
}
|
||
return chain, nil
|
||
}
|
||
|
||
return nil, fmt.Errorf("unsupported message format: %T", ob11Message)
|
||
}
|
||
|
||
// convertOB11SegmentToGeneric 将 OneBot11 消息段转换为通用消息段
|
||
func convertOB11SegmentToGeneric(seg MessageSegment) protocol.MessageSegment {
|
||
switch seg.Type {
|
||
case SegmentTypeText:
|
||
return protocol.NewTextSegment(seg.Data["text"].(string))
|
||
|
||
case SegmentTypeAt:
|
||
// OneBot11 @ 转换为 OneBot12 mention
|
||
userID := seg.Data["qq"]
|
||
return protocol.NewMentionSegment(userID)
|
||
|
||
case SegmentTypeImage:
|
||
fileID, ok := seg.Data["file"].(string)
|
||
if !ok {
|
||
fileID, _ = seg.Data["file_id"].(string)
|
||
}
|
||
return protocol.NewImageSegment(fileID)
|
||
|
||
case SegmentTypeRecord:
|
||
// OneBot11 record 转换为 OneBot12 voice
|
||
fileID, ok := seg.Data["file"].(string)
|
||
if !ok {
|
||
fileID, _ = seg.Data["file_id"].(string)
|
||
}
|
||
return protocol.MessageSegment{
|
||
Type: protocol.SegmentTypeVoice,
|
||
Data: map[string]any{
|
||
"file_id": fileID,
|
||
},
|
||
}
|
||
|
||
case SegmentTypeReply:
|
||
messageID := seg.Data["id"]
|
||
return protocol.NewReplySegment(messageID)
|
||
|
||
case SegmentTypeFace:
|
||
return protocol.MessageSegment{
|
||
Type: protocol.SegmentTypeFace,
|
||
Data: map[string]any{
|
||
"id": seg.Data["id"],
|
||
},
|
||
}
|
||
|
||
default:
|
||
// 其他类型,直接转换
|
||
return protocol.MessageSegment{
|
||
Type: seg.Type,
|
||
Data: seg.Data,
|
||
}
|
||
}
|
||
}
|