356 lines
8.2 KiB
Go
356 lines
8.2 KiB
Go
|
|
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,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|