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:
355
internal/adapter/onebot11/event.go
Normal file
355
internal/adapter/onebot11/event.go
Normal file
@@ -0,0 +1,355 @@
|
||||
package onebot11
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"cellbot/internal/protocol"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
)
|
||||
|
||||
// convertToEvent 将OneBot11原始事件转换为通用事件
|
||||
func (a *Adapter) convertToEvent(raw *RawEvent) (protocol.Event, error) {
|
||||
baseEvent := &protocol.BaseEvent{
|
||||
Timestamp: raw.Time,
|
||||
SelfID: strconv.FormatInt(raw.SelfID, 10),
|
||||
Data: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
switch raw.PostType {
|
||||
case PostTypeMessage:
|
||||
return a.convertMessageEvent(raw, baseEvent)
|
||||
case PostTypeNotice:
|
||||
return a.convertNoticeEvent(raw, baseEvent)
|
||||
case PostTypeRequest:
|
||||
return a.convertRequestEvent(raw, baseEvent)
|
||||
case PostTypeMetaEvent:
|
||||
return a.convertMetaEvent(raw, baseEvent)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown post_type: %s", raw.PostType)
|
||||
}
|
||||
}
|
||||
|
||||
// convertMessageEvent 转换消息事件
|
||||
func (a *Adapter) convertMessageEvent(raw *RawEvent, base *protocol.BaseEvent) (protocol.Event, error) {
|
||||
base.Type = protocol.EventTypeMessage
|
||||
base.DetailType = raw.MessageType
|
||||
base.SubType = raw.SubType
|
||||
|
||||
// 构建消息数据
|
||||
base.Data["message_id"] = raw.MessageID
|
||||
base.Data["user_id"] = raw.UserID
|
||||
base.Data["message"] = raw.Message
|
||||
base.Data["raw_message"] = raw.RawMessage
|
||||
base.Data["font"] = raw.Font
|
||||
|
||||
if raw.GroupID > 0 {
|
||||
base.Data["group_id"] = raw.GroupID
|
||||
}
|
||||
|
||||
if raw.Sender != nil {
|
||||
senderData := map[string]interface{}{
|
||||
"user_id": raw.Sender.UserID,
|
||||
"nickname": raw.Sender.Nickname,
|
||||
}
|
||||
if raw.Sender.Sex != "" {
|
||||
senderData["sex"] = raw.Sender.Sex
|
||||
}
|
||||
if raw.Sender.Age > 0 {
|
||||
senderData["age"] = raw.Sender.Age
|
||||
}
|
||||
if raw.Sender.Card != "" {
|
||||
senderData["card"] = raw.Sender.Card
|
||||
}
|
||||
if raw.Sender.Role != "" {
|
||||
senderData["role"] = raw.Sender.Role
|
||||
}
|
||||
base.Data["sender"] = senderData
|
||||
}
|
||||
|
||||
if raw.Anonymous != nil {
|
||||
base.Data["anonymous"] = map[string]interface{}{
|
||||
"id": raw.Anonymous.ID,
|
||||
"name": raw.Anonymous.Name,
|
||||
"flag": raw.Anonymous.Flag,
|
||||
}
|
||||
}
|
||||
|
||||
// 解析消息段
|
||||
if segments, err := a.parseMessageSegments(raw.Message); err == nil {
|
||||
base.Data["message_segments"] = segments
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// convertNoticeEvent 转换通知事件
|
||||
func (a *Adapter) convertNoticeEvent(raw *RawEvent, base *protocol.BaseEvent) (protocol.Event, error) {
|
||||
base.Type = protocol.EventTypeNotice
|
||||
base.DetailType = raw.NoticeType
|
||||
base.SubType = raw.SubType
|
||||
|
||||
base.Data["user_id"] = raw.UserID
|
||||
|
||||
if raw.GroupID > 0 {
|
||||
base.Data["group_id"] = raw.GroupID
|
||||
}
|
||||
|
||||
if raw.OperatorID > 0 {
|
||||
base.Data["operator_id"] = raw.OperatorID
|
||||
}
|
||||
|
||||
if raw.Duration > 0 {
|
||||
base.Data["duration"] = raw.Duration
|
||||
}
|
||||
|
||||
// 根据不同的通知类型添加特定数据
|
||||
switch raw.NoticeType {
|
||||
case NoticeTypeGroupBan:
|
||||
base.Data["duration"] = raw.Duration
|
||||
case NoticeTypeGroupUpload:
|
||||
// 文件上传信息
|
||||
if raw.File != nil {
|
||||
base.Data["file"] = map[string]interface{}{
|
||||
"id": raw.File.ID,
|
||||
"name": raw.File.Name,
|
||||
"size": raw.File.Size,
|
||||
"busid": raw.File.Busid,
|
||||
}
|
||||
}
|
||||
case NoticeTypeGroupRecall, NoticeTypeFriendRecall:
|
||||
base.Data["message_id"] = raw.MessageID
|
||||
case NoticeTypeNotify:
|
||||
// 处理通知子类型
|
||||
if raw.TargetID > 0 {
|
||||
base.Data["target_id"] = raw.TargetID
|
||||
}
|
||||
if raw.HonorType != "" {
|
||||
base.Data["honor_type"] = raw.HonorType
|
||||
}
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// convertRequestEvent 转换请求事件
|
||||
func (a *Adapter) convertRequestEvent(raw *RawEvent, base *protocol.BaseEvent) (protocol.Event, error) {
|
||||
base.Type = protocol.EventTypeRequest
|
||||
base.DetailType = raw.RequestType
|
||||
base.SubType = raw.SubType
|
||||
|
||||
base.Data["user_id"] = raw.UserID
|
||||
base.Data["comment"] = raw.Comment
|
||||
base.Data["flag"] = raw.Flag
|
||||
|
||||
if raw.GroupID > 0 {
|
||||
base.Data["group_id"] = raw.GroupID
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// convertMetaEvent 转换元事件
|
||||
func (a *Adapter) convertMetaEvent(raw *RawEvent, base *protocol.BaseEvent) (protocol.Event, error) {
|
||||
base.Type = protocol.EventTypeMeta
|
||||
base.DetailType = raw.MetaType
|
||||
|
||||
if raw.Status != nil {
|
||||
statusData := map[string]interface{}{
|
||||
"online": raw.Status.Online,
|
||||
"good": raw.Status.Good,
|
||||
}
|
||||
if raw.Status.Stat != nil {
|
||||
statusData["stat"] = map[string]interface{}{
|
||||
"packet_received": raw.Status.Stat.PacketReceived,
|
||||
"packet_sent": raw.Status.Stat.PacketSent,
|
||||
"packet_lost": raw.Status.Stat.PacketLost,
|
||||
"message_received": raw.Status.Stat.MessageReceived,
|
||||
"message_sent": raw.Status.Stat.MessageSent,
|
||||
"disconnect_times": raw.Status.Stat.DisconnectTimes,
|
||||
"lost_times": raw.Status.Stat.LostTimes,
|
||||
"last_message_time": raw.Status.Stat.LastMessageTime,
|
||||
}
|
||||
}
|
||||
base.Data["status"] = statusData
|
||||
}
|
||||
|
||||
if raw.Interval > 0 {
|
||||
base.Data["interval"] = raw.Interval
|
||||
}
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// parseMessageSegments 解析消息段
|
||||
func (a *Adapter) parseMessageSegments(message interface{}) ([]MessageSegment, error) {
|
||||
if message == nil {
|
||||
return nil, fmt.Errorf("message is nil")
|
||||
}
|
||||
|
||||
// 如果是字符串,转换为文本消息段
|
||||
if str, ok := message.(string); ok {
|
||||
return []MessageSegment{
|
||||
{
|
||||
Type: SegmentTypeText,
|
||||
Data: map[string]interface{}{
|
||||
"text": str,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 如果是数组,解析为消息段数组
|
||||
var segments []MessageSegment
|
||||
data, err := sonic.Marshal(message)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal message: %w", err)
|
||||
}
|
||||
|
||||
if err := sonic.Unmarshal(data, &segments); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal message segments: %w", err)
|
||||
}
|
||||
|
||||
return segments, nil
|
||||
}
|
||||
|
||||
// BuildMessage 构建OneBot11消息
|
||||
func BuildMessage(segments []MessageSegment) interface{} {
|
||||
if len(segments) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// 如果只有一个文本消息段,直接返回文本
|
||||
if len(segments) == 1 && segments[0].Type == SegmentTypeText {
|
||||
if text, ok := segments[0].Data["text"].(string); ok {
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
return segments
|
||||
}
|
||||
|
||||
// BuildTextMessage 构建文本消息
|
||||
func BuildTextMessage(text string) []MessageSegment {
|
||||
return []MessageSegment{
|
||||
{
|
||||
Type: SegmentTypeText,
|
||||
Data: map[string]interface{}{
|
||||
"text": text,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildImageMessage 构建图片消息
|
||||
func BuildImageMessage(file string) []MessageSegment {
|
||||
return []MessageSegment{
|
||||
{
|
||||
Type: SegmentTypeImage,
|
||||
Data: map[string]interface{}{
|
||||
"file": file,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildAtMessage 构建@消息
|
||||
func BuildAtMessage(userID int64) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeAt,
|
||||
Data: map[string]interface{}{
|
||||
"qq": userID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildReplyMessage 构建回复消息
|
||||
func BuildReplyMessage(messageID int32) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeReply,
|
||||
Data: map[string]interface{}{
|
||||
"id": messageID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildFaceMessage 构建表情消息
|
||||
func BuildFaceMessage(faceID int) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeFace,
|
||||
Data: map[string]interface{}{
|
||||
"id": faceID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildRecordMessage 构建语音消息
|
||||
func BuildRecordMessage(file string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeRecord,
|
||||
Data: map[string]interface{}{
|
||||
"file": file,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildVideoMessage 构建视频消息
|
||||
func BuildVideoMessage(file string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeVideo,
|
||||
Data: map[string]interface{}{
|
||||
"file": file,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildShareMessage 构建分享消息
|
||||
func BuildShareMessage(url, title string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeShare,
|
||||
Data: map[string]interface{}{
|
||||
"url": url,
|
||||
"title": title,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildLocationMessage 构建位置消息
|
||||
func BuildLocationMessage(lat, lon float64, title, content string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeLocation,
|
||||
Data: map[string]interface{}{
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"title": title,
|
||||
"content": content,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildMusicMessage 构建音乐消息
|
||||
func BuildMusicMessage(musicType, musicID string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeMusic,
|
||||
Data: map[string]interface{}{
|
||||
"type": musicType,
|
||||
"id": musicID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// BuildCustomMusicMessage 构建自定义音乐消息
|
||||
func BuildCustomMusicMessage(url, audio, title, content, image string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeMusic,
|
||||
Data: map[string]interface{}{
|
||||
"type": "custom",
|
||||
"url": url,
|
||||
"audio": audio,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"image": image,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user