gmessages/libgm/cache/conversation.go
2023-06-30 12:54:08 +03:00

290 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
}