owpengram-server/internal/rpc/reactions_android_compat.go

156 lines
4.6 KiB
Go

package rpc
import (
"context"
"strings"
"sync"
"github.com/iamxvbaba/td/tg"
"telesrv/internal/domain"
)
// availableReactionDocumentMapCache caches the global reaction catalog mapping.
// Returned maps are shared and must be treated as read-only.
type availableReactionDocumentMapCache struct {
mu sync.RWMutex
loaded bool
emojiToDocumentID map[string]int64
documentIDToEmoji map[int64]string
}
func channelMemberCanChangeInfo(member domain.ChannelMember) bool {
return member.Role == domain.ChannelRoleCreator ||
(member.Role == domain.ChannelRoleAdmin && member.AdminRights.ChangeInfo)
}
func (r *Router) applyAndroidChannelReactionEditorCompat(ctx context.Context, full *tg.ChannelFull, canChangeInfo bool) {
if full == nil || !canChangeInfo || ClientTypeFrom(ctx) != ClientTypeAndroid {
return
}
raw, ok := full.GetAvailableReactions()
if !ok {
return
}
some, ok := raw.(*tg.ChatReactionsSome)
if !ok || len(some.Reactions) == 0 {
return
}
emojiToDocumentID, _ := r.availableReactionDocumentMaps(ctx)
if len(emojiToDocumentID) == 0 {
return
}
out := &tg.ChatReactionsSome{Reactions: make([]tg.ReactionClass, 0, len(some.Reactions))}
changed := false
for _, reaction := range some.Reactions {
if emoji, ok := reaction.(*tg.ReactionEmoji); ok && emoji != nil {
if documentID := emojiToDocumentID[strings.TrimSpace(emoji.Emoticon)]; documentID > 0 {
out.Reactions = append(out.Reactions, &tg.ReactionCustomEmoji{DocumentID: documentID})
changed = true
continue
}
}
out.Reactions = append(out.Reactions, reaction)
}
if changed {
full.SetAvailableReactions(out)
}
}
func (r *Router) normalizeDefaultReactionDocuments(ctx context.Context, reactions []domain.MessageReaction) []domain.MessageReaction {
needsCatalog := false
for _, reaction := range reactions {
if reaction.Type == domain.MessageReactionCustomEmoji && reaction.DocumentID > 0 {
needsCatalog = true
break
}
}
if !needsCatalog {
return reactions
}
_, documentIDToEmoji := r.availableReactionDocumentMaps(ctx)
if len(documentIDToEmoji) == 0 {
return reactions
}
out := make([]domain.MessageReaction, 0, len(reactions))
seen := make(map[string]struct{}, len(reactions))
for _, reaction := range reactions {
normalized := normalizeDefaultReactionDocument(reaction, documentIDToEmoji)
key := normalized.Key()
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
out = append(out, normalized)
}
return out
}
func normalizeDefaultReactionDocument(reaction domain.MessageReaction, documentIDToEmoji map[int64]string) domain.MessageReaction {
if reaction.Type != domain.MessageReactionCustomEmoji || reaction.DocumentID <= 0 {
return reaction
}
if emoticon := documentIDToEmoji[reaction.DocumentID]; emoticon != "" {
return domain.MessageReaction{Type: domain.MessageReactionEmoji, Emoticon: emoticon}
}
return reaction
}
func (r *Router) availableReactionDocumentMaps(ctx context.Context) (map[string]int64, map[int64]string) {
if r == nil || r.deps.Files == nil {
return nil, nil
}
return r.availableReactionDocuments.get(ctx, r.deps.Files)
}
func (c *availableReactionDocumentMapCache) get(ctx context.Context, files FilesService) (map[string]int64, map[int64]string) {
if files == nil {
return nil, nil
}
c.mu.RLock()
if c.loaded {
emojiToDocumentID, documentIDToEmoji := c.emojiToDocumentID, c.documentIDToEmoji
c.mu.RUnlock()
return emojiToDocumentID, documentIDToEmoji
}
c.mu.RUnlock()
c.mu.Lock()
defer c.mu.Unlock()
if c.loaded {
return c.emojiToDocumentID, c.documentIDToEmoji
}
catalog, err := files.ListAvailableReactions(ctx)
if err != nil {
return nil, nil
}
c.emojiToDocumentID, c.documentIDToEmoji = buildAvailableReactionDocumentMaps(catalog)
c.loaded = true
return c.emojiToDocumentID, c.documentIDToEmoji
}
func (c *availableReactionDocumentMapCache) clear() {
c.mu.Lock()
defer c.mu.Unlock()
c.loaded = false
c.emojiToDocumentID = nil
c.documentIDToEmoji = nil
}
func buildAvailableReactionDocumentMaps(catalog []domain.AvailableReaction) (map[string]int64, map[int64]string) {
emojiToDocumentID := make(map[string]int64, len(catalog))
documentIDToEmoji := make(map[int64]string, len(catalog))
for _, item := range catalog {
emoticon := strings.TrimSpace(item.Reaction)
if item.Inactive || emoticon == "" || item.ActivateAnimationID <= 0 {
continue
}
if _, exists := emojiToDocumentID[emoticon]; !exists {
emojiToDocumentID[emoticon] = item.ActivateAnimationID
}
if _, exists := documentIDToEmoji[item.ActivateAnimationID]; !exists {
documentIDToEmoji[item.ActivateAnimationID] = emoticon
}
}
return emojiToDocumentID, documentIDToEmoji
}