123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- 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(),
- }
- }
|