Files
cellbot/internal/adapter/milky/event.go

694 lines
21 KiB
Go
Raw Normal View History

package milky
import (
"cellbot/internal/protocol"
"fmt"
"strconv"
"github.com/bytedance/sonic"
"go.uber.org/zap"
)
// EventConverter 事件转换器
// 将 Milky 事件转换为通用 protocol.Event
type EventConverter struct {
logger *zap.Logger
}
// NewEventConverter 创建事件转换器
func NewEventConverter(logger *zap.Logger) *EventConverter {
return &EventConverter{
logger: logger.Named("event-converter"),
}
}
// Convert 转换事件
func (c *EventConverter) Convert(raw []byte) (protocol.Event, error) {
// 解析原始事件
var milkyEvent Event
if err := sonic.Unmarshal(raw, &milkyEvent); err != nil {
return nil, fmt.Errorf("failed to unmarshal event: %w", err)
}
c.logger.Debug("Converting event",
zap.String("event_type", milkyEvent.EventType),
zap.Int64("self_id", milkyEvent.SelfID))
// 根据事件类型转换
switch milkyEvent.EventType {
case EventTypeMessageReceive:
return c.convertMessageEvent(&milkyEvent)
case EventTypeFriendRequest:
return c.convertFriendRequestEvent(&milkyEvent)
case EventTypeGroupJoinRequest:
return c.convertGroupJoinRequestEvent(&milkyEvent)
case EventTypeGroupInvitedJoinRequest:
return c.convertGroupInvitedJoinRequestEvent(&milkyEvent)
case EventTypeGroupInvitation:
return c.convertGroupInvitationEvent(&milkyEvent)
case EventTypeMessageRecall:
return c.convertMessageRecallEvent(&milkyEvent)
case EventTypeBotOffline:
return c.convertBotOfflineEvent(&milkyEvent)
case EventTypeFriendNudge:
return c.convertFriendNudgeEvent(&milkyEvent)
case EventTypeFriendFileUpload:
return c.convertFriendFileUploadEvent(&milkyEvent)
case EventTypeGroupAdminChange:
return c.convertGroupAdminChangeEvent(&milkyEvent)
case EventTypeGroupEssenceMessageChange:
return c.convertGroupEssenceMessageChangeEvent(&milkyEvent)
case EventTypeGroupMemberIncrease:
return c.convertGroupMemberIncreaseEvent(&milkyEvent)
case EventTypeGroupMemberDecrease:
return c.convertGroupMemberDecreaseEvent(&milkyEvent)
case EventTypeGroupNameChange:
return c.convertGroupNameChangeEvent(&milkyEvent)
case EventTypeGroupMessageReaction:
return c.convertGroupMessageReactionEvent(&milkyEvent)
case EventTypeGroupMute:
return c.convertGroupMuteEvent(&milkyEvent)
case EventTypeGroupWholeMute:
return c.convertGroupWholeMuteEvent(&milkyEvent)
case EventTypeGroupNudge:
return c.convertGroupNudgeEvent(&milkyEvent)
case EventTypeGroupFileUpload:
return c.convertGroupFileUploadEvent(&milkyEvent)
default:
c.logger.Warn("Unknown event type", zap.String("event_type", milkyEvent.EventType))
return nil, fmt.Errorf("unknown event type: %s", milkyEvent.EventType)
}
}
// convertMessageEvent 转换消息事件
func (c *EventConverter) convertMessageEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
// 解析消息数据
var msgData IncomingMessage
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &msgData); err != nil {
return nil, fmt.Errorf("failed to parse message data: %w", err)
}
// 构建消息文本
messageText := c.buildMessageText(msgData.Segments)
event := &protocol.MessageEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeMessage,
DetailType: msgData.MessageScene, // friend, group, temp
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"peer_id": msgData.PeerID,
"message_seq": msgData.MessageSeq,
"sender_id": msgData.SenderID,
"time": msgData.Time,
"segments": msgData.Segments,
},
},
MessageID: strconv.FormatInt(msgData.MessageSeq, 10),
Message: messageText,
AltText: messageText,
}
// 添加场景特定数据
if msgData.Friend != nil {
event.Data["friend"] = msgData.Friend
}
if msgData.Group != nil {
event.Data["group"] = msgData.Group
}
if msgData.GroupMember != nil {
event.Data["group_member"] = msgData.GroupMember
}
return event, nil
}
// convertFriendRequestEvent 转换好友请求事件
func (c *EventConverter) convertFriendRequestEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data FriendRequestEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse friend request data: %w", err)
}
event := &protocol.RequestEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeRequest,
DetailType: "friend",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"initiator_id": data.InitiatorID,
"initiator_uid": data.InitiatorUID,
"comment": data.Comment,
"via": data.Via,
},
},
RequestID: strconv.FormatInt(data.InitiatorID, 10),
UserID: strconv.FormatInt(data.InitiatorID, 10),
Comment: data.Comment,
Flag: data.InitiatorUID,
}
return event, nil
}
// convertGroupJoinRequestEvent 转换入群申请事件
func (c *EventConverter) convertGroupJoinRequestEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupJoinRequestEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group join request data: %w", err)
}
event := &protocol.RequestEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeRequest,
DetailType: "group",
SubType: "add",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"group_id": data.GroupID,
"notification_seq": data.NotificationSeq,
"is_filtered": data.IsFiltered,
"initiator_id": data.InitiatorID,
"comment": data.Comment,
},
},
RequestID: strconv.FormatInt(data.NotificationSeq, 10),
UserID: strconv.FormatInt(data.InitiatorID, 10),
Comment: data.Comment,
Flag: strconv.FormatInt(data.NotificationSeq, 10),
}
return event, nil
}
// convertGroupInvitedJoinRequestEvent 转换群成员邀请他人入群事件
func (c *EventConverter) convertGroupInvitedJoinRequestEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupInvitedJoinRequestEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group invited join request data: %w", err)
}
event := &protocol.RequestEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeRequest,
DetailType: "group",
SubType: "invite",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"group_id": data.GroupID,
"notification_seq": data.NotificationSeq,
"initiator_id": data.InitiatorID,
"target_user_id": data.TargetUserID,
},
},
RequestID: strconv.FormatInt(data.NotificationSeq, 10),
UserID: strconv.FormatInt(data.InitiatorID, 10),
Comment: "",
Flag: strconv.FormatInt(data.NotificationSeq, 10),
}
return event, nil
}
// convertGroupInvitationEvent 转换他人邀请自身入群事件
func (c *EventConverter) convertGroupInvitationEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupInvitationEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group invitation data: %w", err)
}
event := &protocol.RequestEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeRequest,
DetailType: "group",
SubType: "invite_self",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"group_id": data.GroupID,
"invitation_seq": data.InvitationSeq,
"initiator_id": data.InitiatorID,
},
},
RequestID: strconv.FormatInt(data.InvitationSeq, 10),
UserID: strconv.FormatInt(data.InitiatorID, 10),
Comment: "",
Flag: strconv.FormatInt(data.InvitationSeq, 10),
}
return event, nil
}
// convertMessageRecallEvent 转换消息撤回事件
func (c *EventConverter) convertMessageRecallEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data MessageRecallEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse message recall data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "message_recall",
SubType: data.MessageScene,
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"message_scene": data.MessageScene,
"peer_id": data.PeerID,
"message_seq": data.MessageSeq,
"sender_id": data.SenderID,
"operator_id": data.OperatorID,
"display_suffix": data.DisplaySuffix,
},
},
UserID: strconv.FormatInt(data.SenderID, 10),
Operator: strconv.FormatInt(data.OperatorID, 10),
}
return event, nil
}
// convertBotOfflineEvent 转换机器人离线事件
func (c *EventConverter) convertBotOfflineEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data BotOfflineEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse bot offline data: %w", err)
}
event := &protocol.MetaEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeMeta,
DetailType: "bot_offline",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}{
"reason": data.Reason,
},
},
Status: "offline",
}
return event, nil
}
// convertFriendNudgeEvent 转换好友戳一戳事件
func (c *EventConverter) convertFriendNudgeEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data FriendNudgeEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse friend nudge data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "friend_nudge",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
UserID: strconv.FormatInt(data.UserID, 10),
}
return event, nil
}
// convertFriendFileUploadEvent 转换好友文件上传事件
func (c *EventConverter) convertFriendFileUploadEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data FriendFileUploadEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse friend file upload data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "friend_file_upload",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
UserID: strconv.FormatInt(data.UserID, 10),
}
return event, nil
}
// convertGroupAdminChangeEvent 转换群管理员变更事件
func (c *EventConverter) convertGroupAdminChangeEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupAdminChangeEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group admin change data: %w", err)
}
subType := "unset"
if data.IsSet {
subType = "set"
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_admin",
SubType: subType,
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.UserID, 10),
}
return event, nil
}
// convertGroupEssenceMessageChangeEvent 转换群精华消息变更事件
func (c *EventConverter) convertGroupEssenceMessageChangeEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupEssenceMessageChangeEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group essence message change data: %w", err)
}
subType := "delete"
if data.IsSet {
subType = "add"
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_essence",
SubType: subType,
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
}
return event, nil
}
// convertGroupMemberIncreaseEvent 转换群成员增加事件
func (c *EventConverter) convertGroupMemberIncreaseEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupMemberIncreaseEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group member increase data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_increase",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.UserID, 10),
}
if data.OperatorID != nil {
event.Operator = strconv.FormatInt(*data.OperatorID, 10)
}
return event, nil
}
// convertGroupMemberDecreaseEvent 转换群成员减少事件
func (c *EventConverter) convertGroupMemberDecreaseEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupMemberDecreaseEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group member decrease data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_decrease",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.UserID, 10),
}
if data.OperatorID != nil {
event.Operator = strconv.FormatInt(*data.OperatorID, 10)
}
return event, nil
}
// convertGroupNameChangeEvent 转换群名称变更事件
func (c *EventConverter) convertGroupNameChangeEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupNameChangeEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group name change data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_name_change",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
Operator: strconv.FormatInt(data.OperatorID, 10),
}
return event, nil
}
// convertGroupMessageReactionEvent 转换群消息回应事件
func (c *EventConverter) convertGroupMessageReactionEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupMessageReactionEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group message reaction data: %w", err)
}
subType := "remove"
if data.IsAdd {
subType = "add"
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_message_reaction",
SubType: subType,
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.UserID, 10),
}
return event, nil
}
// convertGroupMuteEvent 转换群禁言事件
func (c *EventConverter) convertGroupMuteEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupMuteEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group mute data: %w", err)
}
subType := "ban"
if data.Duration == 0 {
subType = "lift_ban"
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_ban",
SubType: subType,
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.UserID, 10),
Operator: strconv.FormatInt(data.OperatorID, 10),
}
return event, nil
}
// convertGroupWholeMuteEvent 转换群全体禁言事件
func (c *EventConverter) convertGroupWholeMuteEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupWholeMuteEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group whole mute data: %w", err)
}
subType := "ban"
if !data.IsMute {
subType = "lift_ban"
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_whole_ban",
SubType: subType,
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
Operator: strconv.FormatInt(data.OperatorID, 10),
}
return event, nil
}
// convertGroupNudgeEvent 转换群戳一戳事件
func (c *EventConverter) convertGroupNudgeEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupNudgeEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group nudge data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_nudge",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.SenderID, 10),
}
return event, nil
}
// convertGroupFileUploadEvent 转换群文件上传事件
func (c *EventConverter) convertGroupFileUploadEvent(milkyEvent *Event) (protocol.Event, error) {
selfID := strconv.FormatInt(milkyEvent.SelfID, 10)
var data GroupFileUploadEventData
dataBytes, _ := sonic.Marshal(milkyEvent.Data)
if err := sonic.Unmarshal(dataBytes, &data); err != nil {
return nil, fmt.Errorf("failed to parse group file upload data: %w", err)
}
event := &protocol.NoticeEvent{
BaseEvent: protocol.BaseEvent{
Type: protocol.EventTypeNotice,
DetailType: "group_file_upload",
SubType: "",
Timestamp: milkyEvent.Time,
SelfID: selfID,
Data: map[string]interface{}(milkyEvent.Data),
},
GroupID: strconv.FormatInt(data.GroupID, 10),
UserID: strconv.FormatInt(data.UserID, 10),
}
return event, nil
}
// buildMessageText 构建消息文本
func (c *EventConverter) buildMessageText(segments []IncomingSegment) string {
var text string
for _, seg := range segments {
if seg.Type == "text" {
if textData, ok := seg.Data["text"].(string); ok {
text += textData
}
} else if seg.Type == "mention" {
if userID, ok := seg.Data["user_id"].(float64); ok {
text += fmt.Sprintf("@%d", int64(userID))
}
} else if seg.Type == "image" {
text += "[图片]"
} else if seg.Type == "voice" {
text += "[语音]"
} else if seg.Type == "video" {
text += "[视频]"
} else if seg.Type == "file" {
text += "[文件]"
} else if seg.Type == "face" {
text += "[表情]"
} else if seg.Type == "forward" {
text += "[转发消息]"
}
}
return text
}