291 lines
8.4 KiB
Go
291 lines
8.4 KiB
Go
|
package cache
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"sort"
|
||
|
|
||
|
"go.mau.fi/mautrix-gmessages/libgm/binary"
|
||
|
)
|
||
|
|
||
|
type SmallInfo struct {
|
||
|
Type int64 `json:"type,omitempty"`
|
||
|
Number string `json:"number,omitempty"`
|
||
|
ParticipantId string `json:"participantId,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Participant struct {
|
||
|
SmallInfo *SmallInfo `json:"smallInfo,omitempty"`
|
||
|
HexHash string `json:"hexHash,omitempty"`
|
||
|
IsMe bool `json:"isMe,omitempty"`
|
||
|
Bs int64 `json:"bs,omitempty"`
|
||
|
DisplayName string `json:"displayName,omitempty"`
|
||
|
}
|
||
|
|
||
|
type ImagePixels struct {
|
||
|
Width int64 `json:"width,omitempty"`
|
||
|
Height int64 `json:"height,omitempty"`
|
||
|
}
|
||
|
|
||
|
type ImageMessage struct {
|
||
|
SomeNumber int64 `json:"someNumber"`
|
||
|
ImageId string `json:"imageId"`
|
||
|
ImageName string `json:"imageName"`
|
||
|
Size int64 `json:"size"`
|
||
|
Pixels ImagePixels `json:"pixels"`
|
||
|
ImageBuffer []byte `json:"imageBuffer"`
|
||
|
DecryptionKey []byte `json:"decryptionKey"`
|
||
|
}
|
||
|
|
||
|
type TextMessage struct {
|
||
|
Content string `json:"content"`
|
||
|
}
|
||
|
|
||
|
type IsFromMe struct {
|
||
|
FromMe bool `json:"fromMe"`
|
||
|
}
|
||
|
|
||
|
type MessageData struct {
|
||
|
OrderInternal string `json:"orderInternal"`
|
||
|
TextData *TextMessage `json:"textData,omitempty"`
|
||
|
ImageData *ImageMessage `json:"imageData,omitempty"`
|
||
|
}
|
||
|
|
||
|
type MessageStatus struct {
|
||
|
Code int64 `json:"code,omitempty"`
|
||
|
ErrMsg string `json:"errMsg,omitempty"`
|
||
|
MsgType string `json:"msgType,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Message struct {
|
||
|
cache *Cache
|
||
|
|
||
|
MessageId string `json:"messageId"`
|
||
|
From IsFromMe `json:"from"`
|
||
|
MessageStatus MessageStatus `json:"details"`
|
||
|
Timestamp int64 `json:"timestamp"`
|
||
|
ConvId string `json:"convId"`
|
||
|
ParticipantId string `json:"participantId"`
|
||
|
MessageData []MessageData `json:"messageData"`
|
||
|
MessageType string `json:"messageType"`
|
||
|
}
|
||
|
|
||
|
func (m *Message) FromMe() bool {
|
||
|
conv, _ := m.cache.Conversations.GetConversation(m.ConvId)
|
||
|
return m.ParticipantId == conv.SelfParticipantId
|
||
|
}
|
||
|
|
||
|
type LatestMessage struct {
|
||
|
Content string `json:"content,omitempty"`
|
||
|
FromMe bool `json:"fromMe,omitempty"`
|
||
|
DisplayName string `json:"displayName,omitempty"`
|
||
|
MessageId string `json:"messageId,omitempty"`
|
||
|
}
|
||
|
|
||
|
type Conversation struct {
|
||
|
cache *Cache
|
||
|
|
||
|
MessageOrder map[int]string `json:"messageOrder"`
|
||
|
Messages map[string]Message `json:"messages"`
|
||
|
ConversationId string `json:"conversationId"`
|
||
|
DisplayName string `json:"displayName"`
|
||
|
LatestMessage LatestMessage `json:"latestMessage"`
|
||
|
IsGroupChat bool `json:"isGroupChat,omitempty"`
|
||
|
Timestamp int64 `json:"timestamp"`
|
||
|
Status int64 `json:"status"`
|
||
|
HexHash string `json:"hexHash"`
|
||
|
Type int64 `json:"type"`
|
||
|
SelfParticipantId string `json:"selfParticipantId,omitempty"`
|
||
|
Participants []Participant `json:"participants"`
|
||
|
ParticipantIds []string `json:"participantIds,omitempty"` // excluded self id
|
||
|
}
|
||
|
|
||
|
type Conversations struct {
|
||
|
cache *Cache
|
||
|
/*
|
||
|
{0: "1", 1: "4"}
|
||
|
order -> conversationId
|
||
|
index 0 = first conversation in order
|
||
|
*/
|
||
|
Order map[int]string `json:"order"`
|
||
|
/*
|
||
|
Map conversations by conversationId
|
||
|
*/
|
||
|
Conversations map[string]*Conversation `json:"conversations"`
|
||
|
}
|
||
|
|
||
|
func (c *Conversations) SetCache(cache *Cache) {
|
||
|
c.cache = cache
|
||
|
}
|
||
|
|
||
|
func (c *Conversations) DeleteConversation(convId string) {
|
||
|
delete(c.Conversations, convId)
|
||
|
}
|
||
|
|
||
|
func (c *Conversations) UpdateConversation(conversation *binary.Conversation) *Conversation {
|
||
|
newConversation := NewConversation(c.cache, conversation)
|
||
|
c.Conversations[conversation.ConversationId] = newConversation
|
||
|
return newConversation
|
||
|
}
|
||
|
|
||
|
func (c *Conversation) GetMessage(msgId string) (Message, error) {
|
||
|
message, foundMsg := c.Messages[msgId]
|
||
|
if !foundMsg {
|
||
|
return Message{}, fmt.Errorf("could not find that message cached")
|
||
|
}
|
||
|
return message, nil
|
||
|
}
|
||
|
|
||
|
func (c *Conversation) Delete() {
|
||
|
c.cache.Conversations.DeleteConversation(c.ConversationId)
|
||
|
}
|
||
|
|
||
|
func (c *Conversation) UpdateMessage(msg *binary.Message) Message {
|
||
|
newMsg := NewMessage(c.cache, msg)
|
||
|
|
||
|
if c.Messages == nil {
|
||
|
log.Println("c.messages was nil so created new map")
|
||
|
c.Messages = make(map[string]Message)
|
||
|
}
|
||
|
|
||
|
c.Messages[msg.MessageId] = newMsg
|
||
|
return newMsg
|
||
|
}
|
||
|
|
||
|
func (c *Conversations) GetConversationByOrder(order int) (*Conversation, error) {
|
||
|
convId, ok := c.Order[order]
|
||
|
if !ok {
|
||
|
return &Conversation{}, fmt.Errorf("could not find a conversation that occupies that order")
|
||
|
}
|
||
|
conversation, foundConvo := c.Conversations[convId]
|
||
|
if !foundConvo {
|
||
|
return &Conversation{}, fmt.Errorf("could not find that conversation cached, oddly enough it seems to be cached in the order map though... investigate further")
|
||
|
}
|
||
|
return conversation, nil
|
||
|
}
|
||
|
|
||
|
func (c *Conversation) GetOrderSlice() []string {
|
||
|
keys := make([]string, 0, len(c.Messages))
|
||
|
for k := range c.Messages {
|
||
|
keys = append(keys, k)
|
||
|
}
|
||
|
sort.Slice(keys, func(i, j int) bool {
|
||
|
return c.Messages[keys[i]].Timestamp < c.Messages[keys[j]].Timestamp
|
||
|
})
|
||
|
return keys
|
||
|
}
|
||
|
|
||
|
func (c *Conversations) GetOrderSlice() []int {
|
||
|
s := make([]int, 0)
|
||
|
for i := range c.Order {
|
||
|
s = append(s, i)
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
func (c *Conversations) GetConversation(convId string) (*Conversation, error) {
|
||
|
convo, ok := c.Conversations[convId]
|
||
|
if !ok {
|
||
|
// handle error, such as creating a new conversation or returning
|
||
|
return &Conversation{}, fmt.Errorf("could not find conversation cached")
|
||
|
} else {
|
||
|
return convo, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewConversation(cache *Cache, conv *binary.Conversation) *Conversation {
|
||
|
currConv, convErr := cache.Conversations.GetConversation(conv.ConversationId)
|
||
|
participants := ParseParticipants(conv.Participants)
|
||
|
newConversation := Conversation{
|
||
|
cache: cache,
|
||
|
ConversationId: conv.ConversationId,
|
||
|
DisplayName: conv.Name,
|
||
|
Timestamp: conv.TimestampMs,
|
||
|
Status: conv.Status,
|
||
|
HexHash: conv.HashHex,
|
||
|
Type: conv.Type,
|
||
|
Participants: participants,
|
||
|
ParticipantIds: conv.OtherParticipants,
|
||
|
SelfParticipantId: conv.SelfParticipantId,
|
||
|
}
|
||
|
|
||
|
if conv.LatestMessage != nil {
|
||
|
newConversation.LatestMessage = LatestMessage{
|
||
|
Content: conv.LatestMessage.Content,
|
||
|
FromMe: conv.LatestMessage.FromMe,
|
||
|
DisplayName: conv.LatestMessage.DisplayName,
|
||
|
MessageId: conv.MessageId,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if convErr == nil {
|
||
|
newConversation.MessageOrder = currConv.MessageOrder
|
||
|
newConversation.Messages = currConv.Messages
|
||
|
} else {
|
||
|
newConversation.MessageOrder = make(map[int]string)
|
||
|
newConversation.Messages = make(map[string]Message)
|
||
|
}
|
||
|
return &newConversation
|
||
|
}
|
||
|
|
||
|
func ParseParticipants(participants []*binary.Participant) []Participant {
|
||
|
partSlice := make([]Participant, 0)
|
||
|
for _, p := range participants {
|
||
|
partSlice = append(partSlice, Participant{
|
||
|
SmallInfo: &SmallInfo{
|
||
|
Type: p.SmallInfo.Type,
|
||
|
Number: p.SmallInfo.Number,
|
||
|
ParticipantId: p.SmallInfo.ParticipantId,
|
||
|
},
|
||
|
HexHash: p.HashHex,
|
||
|
IsMe: p.IsMe,
|
||
|
Bs: p.Bs,
|
||
|
DisplayName: p.DisplayName,
|
||
|
})
|
||
|
}
|
||
|
return partSlice
|
||
|
}
|
||
|
|
||
|
func NewMessage(cache *Cache, message *binary.Message) Message {
|
||
|
msg := Message{
|
||
|
cache: cache,
|
||
|
MessageId: message.MessageId,
|
||
|
ConvId: message.ConversationId,
|
||
|
From: IsFromMe{
|
||
|
FromMe: message.From.FromMe,
|
||
|
},
|
||
|
Timestamp: message.Timestamp,
|
||
|
ParticipantId: message.ParticipantId,
|
||
|
MessageType: message.Type.String(),
|
||
|
MessageStatus: MessageStatus{
|
||
|
Code: message.MessageStatus.Code,
|
||
|
ErrMsg: message.MessageStatus.ErrMsg,
|
||
|
MsgType: message.MessageStatus.MsgStatus,
|
||
|
},
|
||
|
MessageData: make([]MessageData, 0),
|
||
|
}
|
||
|
for _, data := range message.MessageInfo {
|
||
|
msgData := MessageData{
|
||
|
OrderInternal: data.OrderInternal,
|
||
|
}
|
||
|
switch d := data.Data.(type) {
|
||
|
case *binary.MessageInfo_ImageContent:
|
||
|
msgData.ImageData = &ImageMessage{
|
||
|
SomeNumber: d.ImageContent.SomeNumber,
|
||
|
ImageId: d.ImageContent.ImageId,
|
||
|
ImageName: d.ImageContent.ImageName,
|
||
|
Size: d.ImageContent.Size,
|
||
|
Pixels: ImagePixels{Width: d.ImageContent.Pixels.Width, Height: d.ImageContent.Pixels.Height},
|
||
|
ImageBuffer: d.ImageContent.ImageData,
|
||
|
DecryptionKey: d.ImageContent.DecryptionKey,
|
||
|
}
|
||
|
case *binary.MessageInfo_MessageContent:
|
||
|
msgData.TextData = &TextMessage{
|
||
|
Content: d.MessageContent.Content,
|
||
|
}
|
||
|
}
|
||
|
msg.MessageData = append(msg.MessageData, msgData)
|
||
|
}
|
||
|
return msg
|
||
|
}
|