267 lines
8.3 KiB
Go
267 lines
8.3 KiB
Go
package memory
|
|
|
|
import (
|
|
"context"
|
|
"encoding/binary"
|
|
"hash"
|
|
"sort"
|
|
"telesrv/internal/domain"
|
|
"time"
|
|
)
|
|
|
|
func (s *MessageStore) SetMessageReactions(_ context.Context, req domain.SetPrivateMessageReactionsRequest) (domain.PrivateMessageReactionsResult, error) {
|
|
if req.UserID == 0 || req.Peer.Type != domain.PeerTypeUser || req.Peer.ID == 0 || req.MessageID <= 0 || req.MessageID > domain.MaxMessageBoxID {
|
|
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
|
}
|
|
if len(req.Reactions) > domain.MaxChannelMessageReactionsPerUser {
|
|
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
|
}
|
|
req.Reactions = domain.TrimMessageReactionsToUserMax(req.Reactions, req.ReactionsPerUserMax)
|
|
if req.Date == 0 {
|
|
req.Date = int(time.Now().Unix())
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if req.Peer.ID == req.UserID {
|
|
return s.setSavedMessageTagsLocked(req)
|
|
}
|
|
var target domain.Message
|
|
for _, msg := range s.m[req.UserID] {
|
|
if msg.ID == req.MessageID && msg.Peer == req.Peer {
|
|
target = msg
|
|
break
|
|
}
|
|
}
|
|
if target.ID == 0 || target.UID == 0 {
|
|
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
|
}
|
|
if _, ok := s.privateReactions[target.UID]; !ok {
|
|
s.privateReactions[target.UID] = make(map[int64][]domain.ChannelMessagePeerReaction)
|
|
}
|
|
rows := make([]domain.ChannelMessagePeerReaction, 0, len(req.Reactions))
|
|
for i, reaction := range req.Reactions {
|
|
if !reaction.Valid() {
|
|
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
|
}
|
|
rows = append(rows, domain.ChannelMessagePeerReaction{
|
|
UserID: req.UserID,
|
|
Reaction: reaction,
|
|
Big: req.Big,
|
|
My: true,
|
|
ChosenOrder: i + 1,
|
|
Date: req.Date,
|
|
})
|
|
}
|
|
if len(rows) == 0 {
|
|
delete(s.privateReactions[target.UID], req.UserID)
|
|
} else {
|
|
s.privateReactions[target.UID][req.UserID] = rows
|
|
}
|
|
if target.From.ID != 0 && target.From.ID != req.UserID {
|
|
for i := range s.m[target.From.ID] {
|
|
if s.m[target.From.ID][i].UID != target.UID {
|
|
continue
|
|
}
|
|
s.m[target.From.ID][i].ReactionUnread = len(rows) > 0
|
|
break
|
|
}
|
|
}
|
|
s.refreshPrivateReactionDialogSnapshotsLocked(target.UID)
|
|
return s.privateReactionResultLocked(target.UID), nil
|
|
}
|
|
|
|
func (s *MessageStore) GetMessageReactions(_ context.Context, req domain.PrivateMessageReactionsRequest) (domain.PrivateMessageReactionsResult, error) {
|
|
if req.OwnerUserID == 0 || req.Peer.Type != domain.PeerTypeUser || req.Peer.ID == 0 || len(req.IDs) > domain.MaxGetMessageIDs {
|
|
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
|
}
|
|
ids := make(map[int]struct{}, len(req.IDs))
|
|
for _, id := range req.IDs {
|
|
if id <= 0 || id > domain.MaxMessageBoxID {
|
|
return domain.PrivateMessageReactionsResult{}, domain.ErrMessageIDInvalid
|
|
}
|
|
ids[id] = struct{}{}
|
|
}
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
out := domain.PrivateMessageReactionsResult{}
|
|
for _, msg := range s.m[req.OwnerUserID] {
|
|
if msg.Peer != req.Peer {
|
|
continue
|
|
}
|
|
if _, ok := ids[msg.ID]; !ok {
|
|
continue
|
|
}
|
|
item := cloneMessage(msg)
|
|
reactions := s.privateMessageReactionsForMessageLocked(item)
|
|
item.Reactions = cloneChannelMessageReactionsPtr(&reactions)
|
|
out.Messages = append(out.Messages, item)
|
|
if len(out.Reactions.Results) == 0 && len(out.Reactions.Recent) == 0 {
|
|
out.Reactions = reactions
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *MessageStore) privateReactionResultLocked(uid int64) domain.PrivateMessageReactionsResult {
|
|
out := domain.PrivateMessageReactionsResult{}
|
|
for _, messages := range s.m {
|
|
for _, msg := range messages {
|
|
if msg.UID != uid {
|
|
continue
|
|
}
|
|
item := cloneMessage(msg)
|
|
reactions := s.privateMessageReactionsForMessageLocked(item)
|
|
item.Reactions = cloneChannelMessageReactionsPtr(&reactions)
|
|
out.Messages = append(out.Messages, item)
|
|
if len(out.Reactions.Results) == 0 && len(out.Reactions.Recent) == 0 {
|
|
out.Reactions = reactions
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (s *MessageStore) privateMessageReactionsForMessageLocked(msg domain.Message) domain.ChannelMessageReactions {
|
|
if msg.OwnerUserID != 0 &&
|
|
msg.Peer == (domain.Peer{Type: domain.PeerTypeUser, ID: msg.OwnerUserID}) {
|
|
return s.savedMessageTagsForMessageLocked(msg)
|
|
}
|
|
reactions := s.privateMessageReactionsLocked(msg.UID, msg.OwnerUserID)
|
|
if len(reactions.Recent) == 0 || msg.From.ID == 0 {
|
|
return reactions
|
|
}
|
|
for i := range reactions.Recent {
|
|
reactions.Recent[i].SenderUserID = msg.From.ID
|
|
if msg.ReactionUnread && msg.From.ID == msg.OwnerUserID && reactions.Recent[i].UserID != msg.OwnerUserID {
|
|
reactions.Recent[i].Unread = true
|
|
}
|
|
}
|
|
return reactions
|
|
}
|
|
|
|
func (s *MessageStore) privateMessageReactionsLocked(uid, viewerUserID int64) domain.ChannelMessageReactions {
|
|
byUser := s.privateReactions[uid]
|
|
out := domain.ChannelMessageReactions{CanSeeList: true}
|
|
if len(byUser) == 0 {
|
|
return out
|
|
}
|
|
counts := make(map[string]int)
|
|
recent := make([]domain.ChannelMessagePeerReaction, 0, len(byUser))
|
|
for userID, rows := range byUser {
|
|
for _, row := range rows {
|
|
key := row.Reaction.Key()
|
|
index, ok := counts[key]
|
|
if !ok {
|
|
out.Results = append(out.Results, domain.ChannelMessageReactionCount{Reaction: row.Reaction})
|
|
index = len(out.Results) - 1
|
|
counts[key] = index
|
|
}
|
|
out.Results[index].Count++
|
|
if userID == viewerUserID && (out.Results[index].ChosenOrder == 0 || row.ChosenOrder < out.Results[index].ChosenOrder) {
|
|
out.Results[index].ChosenOrder = row.ChosenOrder
|
|
}
|
|
item := row
|
|
item.UserID = userID
|
|
item.My = userID == viewerUserID
|
|
recent = append(recent, item)
|
|
}
|
|
}
|
|
sort.Slice(out.Results, func(i, j int) bool {
|
|
if out.Results[i].Count != out.Results[j].Count {
|
|
return out.Results[i].Count > out.Results[j].Count
|
|
}
|
|
return out.Results[i].Reaction.Key() < out.Results[j].Reaction.Key()
|
|
})
|
|
sort.Slice(recent, func(i, j int) bool {
|
|
if recent[i].Date != recent[j].Date {
|
|
return recent[i].Date > recent[j].Date
|
|
}
|
|
return recent[i].UserID < recent[j].UserID
|
|
})
|
|
if len(recent) > domain.MaxChannelMessageReactionRecent {
|
|
recent = recent[:domain.MaxChannelMessageReactionRecent]
|
|
}
|
|
out.Recent = recent
|
|
return out
|
|
}
|
|
|
|
func (s *MessageStore) countPrivateUnreadReactionsLocked(ownerUserID int64, peer domain.Peer) int {
|
|
count := 0
|
|
for _, msg := range s.m[ownerUserID] {
|
|
if msg.Peer == peer && msg.ReactionUnread {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (s *MessageStore) refreshPrivateReactionDialogSnapshotsLocked(uid int64) {
|
|
if s.dialogs == nil || uid == 0 {
|
|
return
|
|
}
|
|
s.dialogs.mu.Lock()
|
|
defer s.dialogs.mu.Unlock()
|
|
for ownerID, messages := range s.m {
|
|
list := s.dialogs.m[ownerID]
|
|
changed := false
|
|
for _, msg := range messages {
|
|
if msg.UID != uid {
|
|
continue
|
|
}
|
|
enriched := cloneMessage(msg)
|
|
reactions := s.privateMessageReactionsForMessageLocked(enriched)
|
|
if len(reactions.Results) > 0 || len(reactions.Recent) > 0 {
|
|
enriched.Reactions = cloneChannelMessageReactionsPtr(&reactions)
|
|
}
|
|
for i := range list.Messages {
|
|
if list.Messages[i].UID == uid && list.Messages[i].OwnerUserID == ownerID {
|
|
list.Messages[i] = enriched
|
|
changed = true
|
|
}
|
|
}
|
|
for i := range list.Dialogs {
|
|
if list.Dialogs[i].Peer == msg.Peer {
|
|
list.Dialogs[i].UnreadReactions = s.countPrivateUnreadReactionsLocked(ownerID, msg.Peer)
|
|
changed = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if changed {
|
|
s.dialogs.m[ownerID] = list
|
|
}
|
|
}
|
|
}
|
|
|
|
func writeMessageReactionsHash(h hash.Hash64, reactions *domain.ChannelMessageReactions) {
|
|
if reactions == nil {
|
|
_, _ = h.Write([]byte{0})
|
|
return
|
|
}
|
|
var buf [16]byte
|
|
if reactions.AsTags {
|
|
_, _ = h.Write([]byte{1})
|
|
} else {
|
|
_, _ = h.Write([]byte{0})
|
|
}
|
|
for _, item := range reactions.Results {
|
|
_, _ = h.Write([]byte(item.Reaction.Type))
|
|
_, _ = h.Write([]byte{0})
|
|
_, _ = h.Write([]byte(item.Reaction.Value()))
|
|
_, _ = h.Write([]byte{0})
|
|
binary.LittleEndian.PutUint32(buf[:4], uint32(item.Count))
|
|
binary.LittleEndian.PutUint32(buf[4:8], uint32(item.ChosenOrder))
|
|
_, _ = h.Write(buf[:8])
|
|
}
|
|
_, _ = h.Write([]byte{0xfe})
|
|
for _, item := range reactions.Recent {
|
|
_, _ = h.Write([]byte(item.Reaction.Type))
|
|
_, _ = h.Write([]byte{0})
|
|
_, _ = h.Write([]byte(item.Reaction.Value()))
|
|
_, _ = h.Write([]byte{0})
|
|
binary.LittleEndian.PutUint64(buf[:8], uint64(item.UserID))
|
|
binary.LittleEndian.PutUint32(buf[8:12], uint32(item.Date))
|
|
binary.LittleEndian.PutUint32(buf[12:16], uint32(item.ChosenOrder))
|
|
_, _ = h.Write(buf[:])
|
|
}
|
|
}
|