owpengram-server/internal/store/memory/message_helpers.go

246 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package memory
import (
"encoding/binary"
"hash/fnv"
"reflect"
"sort"
"telesrv/internal/domain"
)
func (s *MessageStore) deleteMemoryMessagesLocked(userID int64, limit int, match func(domain.Message) bool) ([]deletedMemoryMessage, map[int64]struct{}, bool) {
messages := s.m[userID]
kept := messages[:0]
deleted := make([]deletedMemoryMessage, 0)
revokeUIDs := make(map[int64]struct{})
more := false
for _, msg := range messages {
if match(msg) {
if limit > 0 && len(deleted) >= limit {
kept = append(kept, msg)
more = true
continue
}
deleted = append(deleted, deletedMemoryMessage{
userID: userID,
peer: msg.Peer,
id: msg.ID,
privateMessageID: msg.UID,
messageSenderID: msg.From.ID,
randomID: msg.RandomID,
})
if msg.UID != 0 {
revokeUIDs[msg.UID] = struct{}{}
}
continue
}
kept = append(kept, msg)
}
s.m[userID] = kept
return deleted, revokeUIDs, more
}
func (s *MessageStore) deleteMemoryMessagesByUIDLocked(uids map[int64]struct{}, excludeUserID int64) []deletedMemoryMessage {
if len(uids) == 0 {
return nil
}
deleted := make([]deletedMemoryMessage, 0)
for userID, messages := range s.m {
if userID == excludeUserID {
continue
}
kept := messages[:0]
for _, msg := range messages {
if _, ok := uids[msg.UID]; ok {
deleted = append(deleted, deletedMemoryMessage{
userID: userID,
peer: msg.Peer,
id: msg.ID,
privateMessageID: msg.UID,
messageSenderID: msg.From.ID,
randomID: msg.RandomID,
})
continue
}
kept = append(kept, msg)
}
s.m[userID] = kept
}
return deleted
}
func normalizeMemoryMessageIDs(ids []int) []int {
if len(ids) == 0 {
return nil
}
out := make([]int, 0, len(ids))
seen := make(map[int]struct{}, len(ids))
for _, id := range ids {
if id <= 0 || id > domain.MaxMessageBoxID {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
out = append(out, id)
}
sort.Ints(out)
return out
}
func cloneMessage(msg domain.Message) domain.Message {
msg.Entities = append([]domain.MessageEntity(nil), msg.Entities...)
msg.ReplyTo = cloneMessageReply(msg.ReplyTo)
msg.Forward = cloneMessageForward(msg.Forward)
msg.Reactions = cloneChannelMessageReactionsPtr(msg.Reactions)
msg.ReplyMarkup = cloneReplyMarkup(msg.ReplyMarkup)
msg.RichMessage = cloneRichMessage(msg.RichMessage)
return msg
}
// cloneReplyMarkup 深拷 inline keyboard 快照:与 postgres 每盒独立 decode 对齐
// (双 store 行为一致),避免发送方/接收方两行共享底层 rows/Data 切片。
func cloneReplyMarkup(m *domain.MessageReplyMarkup) *domain.MessageReplyMarkup {
if m == nil {
return nil
}
clone := domain.MessageReplyMarkup{}
if m.Inline != nil {
clone.Inline = make([][]domain.MarkupButton, len(m.Inline))
for i, row := range m.Inline {
cloneRow := make([]domain.MarkupButton, len(row))
for j, btn := range row {
cloneRow[j] = btn
cloneRow[j].Data = append([]byte(nil), btn.Data...)
}
clone.Inline[i] = cloneRow
}
}
return &clone
}
// cloneRichMessage 深拷 Layer 227 富文本快照:复制不透明 blocks 字节与内嵌媒体切片,
// 避免发送方/接收方两行共享底层切片(与 postgres 每盒独立 decode 对齐)。
func cloneRichMessage(m *domain.MessageRichMessage) *domain.MessageRichMessage {
if m == nil {
return nil
}
clone := *m
clone.Blocks = append([]byte(nil), m.Blocks...)
clone.Photos = append([]domain.Photo(nil), m.Photos...)
clone.Documents = append([]domain.Document(nil), m.Documents...)
return &clone
}
func richMessagesEqual(a, b *domain.MessageRichMessage) bool {
if a.IsZero() && b.IsZero() {
return true
}
return reflect.DeepEqual(a, b)
}
func cloneMessageReply(reply *domain.MessageReply) *domain.MessageReply {
if reply == nil {
return nil
}
clone := *reply
clone.QuoteEntities = append([]domain.MessageEntity(nil), reply.QuoteEntities...)
return &clone
}
func cloneMessageForward(forward *domain.MessageForward) *domain.MessageForward {
if forward == nil {
return nil
}
clone := *forward
return &clone
}
func newMessageEvent(msg domain.Message) domain.UpdateEvent {
if msg.ID == 0 {
return domain.UpdateEvent{}
}
return domain.UpdateEvent{
UserID: msg.OwnerUserID,
Type: domain.UpdateEventNewMessage,
Pts: msg.Pts,
PtsCount: 1,
Date: msg.Date,
Message: cloneMessage(msg),
}
}
func editMessageEvent(msg domain.Message) domain.UpdateEvent {
if msg.ID == 0 {
return domain.UpdateEvent{}
}
return domain.UpdateEvent{
UserID: msg.OwnerUserID,
Type: domain.UpdateEventEditMessage,
Pts: msg.Pts,
PtsCount: 1,
Date: msg.EditDate,
Message: cloneMessage(msg),
}
}
// webPageEvent 是链接预览就地替换事件Date 取消息发送时间,不引入 edit_date
func webPageEvent(msg domain.Message) domain.UpdateEvent {
if msg.ID == 0 {
return domain.UpdateEvent{}
}
return domain.UpdateEvent{
UserID: msg.OwnerUserID,
Type: domain.UpdateEventWebPage,
Pts: msg.Pts,
PtsCount: 1,
Date: msg.Date,
Message: cloneMessage(msg),
}
}
func equalMessageEntities(a, b []domain.MessageEntity) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func hasUser(users []domain.User, id int64) bool {
for _, u := range users {
if u.ID == id {
return true
}
}
return false
}
func messageListHash(messages []domain.Message) int64 {
if len(messages) == 0 {
return 0
}
h := fnv.New64a()
var buf [16]byte
for _, msg := range messages {
binary.LittleEndian.PutUint32(buf[:4], uint32(msg.ID))
binary.LittleEndian.PutUint32(buf[4:8], uint32(msg.Date))
binary.LittleEndian.PutUint64(buf[8:16], uint64(msg.From.ID))
_, _ = h.Write(buf[:])
writeMessageReactionsHash(h, msg.Reactions)
}
return int64(h.Sum64())
}
func cloneMessages(messages []domain.Message) []domain.Message {
out := append([]domain.Message(nil), messages...)
for i := range out {
out[i] = cloneMessage(out[i])
}
return out
}