package server import ( "encoding" "encoding/json" "github.com/mitchellh/mapstructure" "github.com/rotisserie/eris" "go.uber.org/zap" ) type MessageType = int8 type ChatingContentType = uint8 const ( MessageTypeError MessageType = -1 // MessageTypeError 错误消息,当服务器出现错误时,会发送此消息 MessageTypePingPong MessageType = 1 // MessageTypePingPong ping pong 消息 MessageTypeEmpty MessageType = 2 // MessageTypeEmpty 空消息 MessageTypeUpChating MessageType = 30 // MessageTypeUpChating 语聊消息 MessageTypeDownChating MessageType = 31 // MessageTypeDownChating 语聊消息 MessageTypeNotification MessageType = 40 // MessageTypeNotification 通知消息 MessageTypeLocation MessageType = 50 // MessageTypeLocation 定位类型 MessageTypeVideoCall MessageType = 60 // MessageTypeVideoCall 视频通话来电 ) const ( ChatingContentTypeText ChatingContentType = 1 + iota // ChatingContentTypeText 语聊文字消息 ChatingContentTypeMetadata // ChatingContentTypeMetadata 媒体文件消息 ) const ( NotificationTypeDeviceStatusChange = 100 // NotificationTypeDeviceStatusChange 设备绑定状态变更通知 NotificationTypeAskLocation = 111 // NotificationTypeAskLocation 询问设备位置 NotificationTypeChangedContacts = 130 // NotificationTypeChangedContacts 联系人变动通知 NotificationTypeCreatedFriend = 120 // NotificationTypeCreatedFriend 通知添加好友结果通知 NotificationTypeChangedAlarmClock = 140 // NotificationTypeChangedAlarmClock 闹钟变更通知 NotificationTypeChangedSchoolMode = 141 // NotificationTypeChangedSchoolMode 上课禁用变更通知 NotificationTypeDeviceShutdown = 150 // NotificationTypeDeviceShutdown 设备关机通知 NotificationTypeDeviceReboot = 160 // NotificationTypeDeviceReboot 设备重启通知 ) var _ encoding.BinaryMarshaler = (*Message)(nil) var _ encoding.BinaryUnmarshaler = (*Message)(nil) type Message struct { MessageId string `json:"-"` // MessageId 消息Id, 不参与序列化 sender string `json:"-"` // sender 消息发送者, 给内部使用, 不参与序列化 Type MessageType `json:"type"` RequestId string `json:"requestId"` // RequestId 当前消息的Id Content any `json:"content"` // Content 内容 Receiver string `json:"receiver,omitempty"` // Receiver 消息接受者 } type FirebaseMessage struct { token string message *Message // clt *Client } type ( // ChatingContent to client chating message ChatingContent struct { MessageId string `json:"messageId" mapstructure:"messageId"` Receiver string `json:"receiver" mapstructure:"receiver"` SessionId string `json:"sessionId" mapstructure:"sessionId"` // SessionId 过度字段, 一般和 Receiver 内容相同 PayloadType ChatingContentType `json:"payloadType" mapstructure:"payloadType"` Payload any `json:"payload" mapstructure:"payload"` SendTime int64 `json:"sendTime" mapstructure:"sendTime"` } // NotificationContent to client notice message NotificationContent struct { ID int `json:"id" mapstructure:"id"` Payload map[string]interface{} `json:"payload" mapstructure:"payload"` } LocationMessageContent struct { Longitude string `json:"lng" mapstructure:"lng"` // Longitude 纬度 Latitude string `json:"lat" mapstructure:"lat"` // Latitude 经度 Radius int `json:"radius" mapstructure:"radius"` // Radius 精度 ChildrenId string `json:"cid" mapstructure:"cid"` // ChildrenId 孩子ID Type string `json:"type" mapstructure:"type"` // Type 类型 PositionTime int64 `json:"positionTime" mapstructure:"positionTime"` // PositionTime 时间 } VideoCallMessageContent struct { AccountId string `json:"accountId" mapstructure:"accountId"` // AccountId 账号ID } ) type ( // ContentText received a text message from the client ContentText struct { Raw string `json:"raw" mapstructure:"raw"` } // ContentMetadata received media file message from client ContentMetadata struct { Url string `json:"url" mapstructure:""` // Url 文件地址 FileType string `json:"fileType" mapstructure:""` // FileType 文件类型 Duration uint `json:"duration" mapstructure:""` // Duration 视频/语音时长 } // ContentReply content to be replied to after receiving the message ContentReply struct { MessageId string `json:"messageId" mapstructure:"messageId"` } // ContentError an error occurred while processing the message ContentError struct { Err string `json:"err" mapstructure:"err"` } ) func (m *Message) UnmarshalBinary(data []byte) error { return json.Unmarshal(data, m) } func (m *Message) MarshalBinary() (data []byte, err error) { return json.Marshal(m) } func (m *Message) IsNeedReply() bool { return m.RequestId != "" } func (m *Message) IsError() bool { return m.Type == MessageTypeError } func (m *Message) String() string { buf, _ := m.MarshalBinary() return string(buf) } func deserializeMessage(bytes []byte) *Message { if len(bytes) == 0 { return emptyMessage() } var message Message err := json.Unmarshal(bytes, &message) if err != nil { zap.L().Error("[message] unable to deserialize message", zap.Error(err)) return emptyMessage() } switch message.Type { // Is it a chating message case MessageTypeUpChating, MessageTypeDownChating: var chatingContent ChatingContent if err := mapstructure.Decode(message.Content, &chatingContent); err != nil { return errorMessage(message.RequestId, eris.Wrap(err, "failed to decode chating content")) } message.Content = chatingContent // Is it a notify message case MessageTypeNotification: var notificationContent NotificationContent if err := mapstructure.Decode(message.Content, ¬ificationContent); err != nil { return errorMessage(message.RequestId, eris.Wrap(err, "failed to decode notify content")) } message.Content = notificationContent // Is it a location message case MessageTypeLocation: var locationMessageContent LocationMessageContent if err := mapstructure.Decode(message.Content, &locationMessageContent); err != nil { return errorMessage(message.RequestId, eris.Wrap(err, "failed to decode location content")) } message.Content = locationMessageContent // Message without content case MessageTypeVideoCall: var videoCallMessageContent VideoCallMessageContent if err := mapstructure.Decode(message.Content, &videoCallMessageContent); err != nil { return errorMessage(message.RequestId, eris.Wrap(err, "failed to decode video call content")) } message.Content = videoCallMessageContent } return &message } func serializationMessage(message *Message) []byte { // Erases some fields that are not needed for the client message.Receiver = "" // Serializable bytes, _ := json.Marshal(message) return bytes } // newReplyMessage new a to client reply message func newReplyMessage(message *Message) *Message { switch message.Type { case MessageTypePingPong: message.Content = "pong" case MessageTypeUpChating: message.Content = &ContentReply{MessageId: message.MessageId} } return message } func emptyMessage() *Message { return &Message{Type: MessageTypeEmpty} } func errorMessage(requestId string, err error) *Message { return &Message{ Type: MessageTypeError, RequestId: requestId, Content: err.Error(), } }