owpengram-server/internal/rpc/messages_stickers_personal.go

251 lines
8 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 rpc
import (
"context"
"github.com/gotd/td/tg"
"telesrv/internal/domain"
)
// 个人贴纸集合:收藏贴纸 / 最近贴纸 / 保存的 GIF。读写都经 stickerCollectionService
// (由 *app/account.Service 实现,类型断言自 r.deps.Account。文档经 Files 解析为完整
// 文档对象,未接通服务时回落历史空 stub 行为。
type stickerCollectionService interface {
SaveStickerCollectionItem(ctx context.Context, userID int64, kind domain.StickerCollectionKind, documentID int64, unsave bool, now int) error
ListStickerCollection(ctx context.Context, userID int64, kind domain.StickerCollectionKind, limit int) ([]domain.StickerCollectionItem, error)
ClearStickerCollection(ctx context.Context, userID int64, kind domain.StickerCollectionKind) error
}
func (r *Router) stickerCollectionSvc() (stickerCollectionService, bool) {
svc, ok := r.deps.Account.(stickerCollectionService)
return svc, ok
}
// stickerDocumentFromInput 校验输入文档存在且为贴纸faveSticker/saveRecentSticker
func (r *Router) stickerDocumentFromInput(ctx context.Context, input tg.InputDocumentClass, requireGif bool) (domain.Document, error) {
in, ok := input.(*tg.InputDocument)
if !ok || in.ID == 0 || r.deps.Files == nil {
return domain.Document{}, stickerInvalidErr()
}
doc, found, err := r.deps.Files.GetDocument(ctx, in.ID)
if err != nil {
return domain.Document{}, internalErr()
}
ok = found && doc.AccessHash == in.AccessHash
if ok {
if requireGif {
ok = doc.IsGif()
} else {
ok = doc.IsSticker()
}
}
if !ok {
return domain.Document{}, stickerInvalidErr()
}
return doc, nil
}
func (r *Router) onMessagesFaveSticker(ctx context.Context, req *tg.MessagesFaveStickerRequest) (bool, error) {
if req == nil {
return false, stickerInvalidErr()
}
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
doc, err := r.stickerDocumentFromInput(ctx, req.ID, false)
if err != nil {
return false, err
}
if svc, ok := r.stickerCollectionSvc(); ok {
if err := svc.SaveStickerCollectionItem(ctx, userID, domain.StickerCollectionFaved, doc.ID, req.Unfave, int(r.clock.Now().Unix())); err != nil {
return false, internalErr()
}
}
r.pushStickerCollectionUpdate(ctx, userID, &tg.UpdateFavedStickers{})
return true, nil
}
func (r *Router) onMessagesSaveRecentSticker(ctx context.Context, req *tg.MessagesSaveRecentStickerRequest) (bool, error) {
if req == nil {
return false, stickerInvalidErr()
}
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
doc, err := r.stickerDocumentFromInput(ctx, req.ID, false)
if err != nil {
return false, err
}
kind := domain.StickerCollectionRecent
if req.Attached {
kind = domain.StickerCollectionRecentAttached
}
if svc, ok := r.stickerCollectionSvc(); ok {
if err := svc.SaveStickerCollectionItem(ctx, userID, kind, doc.ID, req.Unsave, int(r.clock.Now().Unix())); err != nil {
return false, internalErr()
}
}
r.pushStickerCollectionUpdate(ctx, userID, &tg.UpdateRecentStickers{})
return true, nil
}
func (r *Router) onMessagesSaveGif(ctx context.Context, req *tg.MessagesSaveGifRequest) (bool, error) {
if req == nil {
return false, stickerInvalidErr()
}
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
doc, err := r.stickerDocumentFromInput(ctx, req.ID, true)
if err != nil {
return false, err
}
if svc, ok := r.stickerCollectionSvc(); ok {
if err := svc.SaveStickerCollectionItem(ctx, userID, domain.StickerCollectionGif, doc.ID, req.Unsave, int(r.clock.Now().Unix())); err != nil {
return false, internalErr()
}
}
r.pushStickerCollectionUpdate(ctx, userID, &tg.UpdateSavedGifs{})
return true, nil
}
func (r *Router) onMessagesClearRecentStickers(ctx context.Context, req *tg.MessagesClearRecentStickersRequest) (bool, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
kind := domain.StickerCollectionRecent
if req != nil && req.Attached {
kind = domain.StickerCollectionRecentAttached
}
if svc, ok := r.stickerCollectionSvc(); ok {
if err := svc.ClearStickerCollection(ctx, userID, kind); err != nil {
return false, internalErr()
}
}
r.pushStickerCollectionUpdate(ctx, userID, &tg.UpdateRecentStickers{})
return true, nil
}
func (r *Router) onMessagesGetFavedStickers(ctx context.Context, hash int64) (tg.MessagesFavedStickersClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
docs := r.stickerCollectionDocuments(ctx, userID, domain.StickerCollectionFaved, nil)
catalogHash := stickerDocumentsHash(docs)
if hash != 0 && hash == catalogHash {
return &tg.MessagesFavedStickersNotModified{}, nil
}
return &tg.MessagesFavedStickers{
Hash: catalogHash,
Packs: []tg.StickerPack{},
Stickers: tgDocuments(docs),
}, nil
}
func (r *Router) onMessagesGetRecentStickers(ctx context.Context, req *tg.MessagesGetRecentStickersRequest) (tg.MessagesRecentStickersClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
kind := domain.StickerCollectionRecent
if req != nil && req.Attached {
kind = domain.StickerCollectionRecentAttached
}
var dates []int
docs := r.stickerCollectionDocuments(ctx, userID, kind, &dates)
catalogHash := stickerDocumentsHash(docs)
if req != nil && req.Hash != 0 && req.Hash == catalogHash {
return &tg.MessagesRecentStickersNotModified{}, nil
}
return &tg.MessagesRecentStickers{
Hash: catalogHash,
Packs: []tg.StickerPack{},
Stickers: tgDocuments(docs),
Dates: dates,
}, nil
}
func (r *Router) onMessagesGetSavedGifs(ctx context.Context, hash int64) (tg.MessagesSavedGifsClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
docs := r.stickerCollectionDocuments(ctx, userID, domain.StickerCollectionGif, nil)
catalogHash := stickerDocumentsHash(docs)
if hash != 0 && hash == catalogHash {
return &tg.MessagesSavedGifsNotModified{}, nil
}
return &tg.MessagesSavedGifs{Hash: catalogHash, Gifs: tgDocuments(docs)}, nil
}
// stickerCollectionDocuments 取某集合并解析为完整文档(最新在前,顺序与集合一致)。
// 解析不到的文档(已删/不可用)跳过。若 datesOut 非 nil按相同顺序填充 used_at。
func (r *Router) stickerCollectionDocuments(ctx context.Context, userID int64, kind domain.StickerCollectionKind, datesOut *[]int) []domain.Document {
svc, ok := r.stickerCollectionSvc()
if !ok || r.deps.Files == nil {
if datesOut != nil {
*datesOut = []int{}
}
return nil
}
items, err := svc.ListStickerCollection(ctx, userID, kind, domain.MaxStickerCollectionItems(kind))
if err != nil || len(items) == 0 {
if datesOut != nil {
*datesOut = []int{}
}
return nil
}
ids := make([]int64, 0, len(items))
dateByID := make(map[int64]int, len(items))
for _, it := range items {
ids = append(ids, it.DocumentID)
dateByID[it.DocumentID] = it.Date
}
resolved, err := r.deps.Files.GetDocuments(ctx, ids)
if err != nil {
if datesOut != nil {
*datesOut = []int{}
}
return nil
}
byID := documentsByID(resolved)
docs := make([]domain.Document, 0, len(items))
dates := make([]int, 0, len(items))
for _, id := range ids { // 保持集合顺序(最新在前)
doc, ok := byID[id]
if !ok {
continue
}
docs = append(docs, doc)
dates = append(dates, dateByID[id])
}
if datesOut != nil {
*datesOut = dates
}
return docs
}
func stickerDocumentsHash(docs []domain.Document) int64 {
values := make([]int64, 0, len(docs))
for _, d := range docs {
values = append(values, d.ID)
}
return int64(tdesktopCountHash(values))
}
// pushStickerCollectionUpdate 把 updateFaved/Recent/SavedGifs nudge 推给本人其它在线设备。
func (r *Router) pushStickerCollectionUpdate(ctx context.Context, userID int64, update tg.UpdateClass) {
r.pushUserUpdates(ctx, userID, &tg.Updates{
Updates: []tg.UpdateClass{update},
Users: []tg.UserClass{},
Chats: []tg.ChatClass{},
Date: int(r.clock.Now().Unix()),
})
}