251 lines
8 KiB
Go
251 lines
8 KiB
Go
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()),
|
||
})
|
||
}
|