fix for gif and emojies categories
This commit is contained in:
parent
cf23b184d2
commit
5e18bd3830
6 changed files with 179 additions and 14 deletions
101
internal/app/files/emoji_group_icons_seed.go
Normal file
101
internal/app/files/emoji_group_icons_seed.go
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
package files
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"telesrv/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
// emojiGroupIconSpec 描述 messages.getEmojiGroups 一个分类图标:把已 seed 的
|
||||||
|
// DefaultSet_AnimatedEmoji 贴纸文档(SourceDocID,带 DocAttrSticker)在本地"复制"成一份
|
||||||
|
// 带 DocAttrCustomEmoji 的影子文档(NewID),二进制内容(blob)原样复用,不重新下载/编码。
|
||||||
|
//
|
||||||
|
// 背景:messages.getEmojiGroups 的 icon_emoji_id 语义上必须指向一份真正的自定义 emoji
|
||||||
|
// 文档——真实 Telegram 的这些 icon 文档始终带 DocumentAttributeCustomEmoji。我们最初直接
|
||||||
|
// 复用 DefaultSet_AnimatedEmoji 里现成的贴纸文档 id(见 internal/seed/catalog/emoji_groups.json),
|
||||||
|
// 但那些文档只带 DocAttrSticker,不带 DocAttrCustomEmoji——Android 客户端渲染分类图标那一小格
|
||||||
|
// 时找不到期望的自定义 emoji 元数据,一直转圈(桌面端似乎更宽松,不受影响)。
|
||||||
|
// NewID 特意选用远小于真实 Telegram snowflake id(19 位)的 13 位数字,不会与任何真实/已
|
||||||
|
// seed 的 document id 冲突。
|
||||||
|
type emojiGroupIconSpec struct {
|
||||||
|
NewID int64
|
||||||
|
SourceDocID int64
|
||||||
|
Alt string
|
||||||
|
}
|
||||||
|
|
||||||
|
var emojiGroupIconSpecs = []emojiGroupIconSpec{
|
||||||
|
{NewID: 9000000000001, SourceDocID: 1258816259753929, Alt: "❤"}, // Love
|
||||||
|
{NewID: 9000000000002, SourceDocID: 5181593617004757506, Alt: "👍"}, // Approval
|
||||||
|
{NewID: 9000000000003, SourceDocID: 5181852277115192162, Alt: "👎"}, // Disapproval
|
||||||
|
{NewID: 9000000000004, SourceDocID: 4909118808687378938, Alt: "🎉"}, // Cheers
|
||||||
|
{NewID: 9000000000005, SourceDocID: 5129721261156467513, Alt: "😄"}, // Laughter
|
||||||
|
{NewID: 9000000000006, SourceDocID: 5129715527375127060, Alt: "😨"}, // Astonishment
|
||||||
|
{NewID: 9000000000007, SourceDocID: 5129804416018285169, Alt: "😔"}, // Sadness
|
||||||
|
{NewID: 9000000000008, SourceDocID: 5129791445217051348, Alt: "😡"}, // Anger
|
||||||
|
{NewID: 9000000000009, SourceDocID: 5129653108615414540, Alt: "😐"}, // Neutral
|
||||||
|
{NewID: 9000000000010, SourceDocID: 5127352539448082997, Alt: "🤔"}, // Doubt
|
||||||
|
{NewID: 9000000000011, SourceDocID: 5127684445930783362, Alt: "🤪"}, // Silly
|
||||||
|
}
|
||||||
|
|
||||||
|
// seedEmojiGroupIcons 为 emojiGroupIconSpecs 中每一项确保存在一份带 DocAttrCustomEmoji
|
||||||
|
// 的影子文档。源贴纸未 seed(开发机没有该 sticker-seed 数据)时静默跳过该项而非报错。
|
||||||
|
func (s *Service) seedEmojiGroupIcons(ctx context.Context, stats *SeedStats) error {
|
||||||
|
for _, spec := range emojiGroupIconSpecs {
|
||||||
|
existing, found, err := s.media.GetDocument(ctx, spec.NewID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if found && existing.ID != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
src, found, err := s.media.GetDocument(ctx, spec.SourceDocID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !found || src.ID == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
blob, found, err := s.media.GetFileBlob(ctx, fmt.Sprintf("doc:%d", spec.SourceDocID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs := []domain.DocumentAttribute{
|
||||||
|
{Kind: domain.DocAttrCustomEmoji, Alt: spec.Alt},
|
||||||
|
}
|
||||||
|
for _, a := range src.Attributes {
|
||||||
|
if a.Kind == domain.DocAttrImageSize {
|
||||||
|
attrs = append(attrs, a)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc := domain.Document{
|
||||||
|
ID: spec.NewID,
|
||||||
|
AccessHash: src.AccessHash,
|
||||||
|
Date: src.Date,
|
||||||
|
MimeType: src.MimeType,
|
||||||
|
Size: src.Size,
|
||||||
|
DCID: src.DCID,
|
||||||
|
Attributes: attrs,
|
||||||
|
}
|
||||||
|
if err := s.media.PutDocument(ctx, doc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.media.PutFileBlob(ctx, domain.FileBlob{
|
||||||
|
LocationKey: fmt.Sprintf("doc:%d", spec.NewID),
|
||||||
|
Backend: blob.Backend,
|
||||||
|
ObjectKey: blob.ObjectKey,
|
||||||
|
Size: blob.Size,
|
||||||
|
MimeType: blob.MimeType,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stats.Documents++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -111,6 +111,15 @@ func (s *Service) SeedMedia(ctx context.Context, root string, maxRegularSets int
|
||||||
}
|
}
|
||||||
s.logSeedPhase("effects", phaseStarted, phaseBefore, stats)
|
s.logSeedPhase("effects", phaseStarted, phaseBefore, stats)
|
||||||
|
|
||||||
|
phaseStarted = time.Now()
|
||||||
|
phaseBefore = stats
|
||||||
|
// messages.getEmojiGroups 分类图标的影子自定义 emoji 文档,依赖上面 sticker sets 阶段
|
||||||
|
// 已 seed 的源文档,故放在其后。
|
||||||
|
if err := s.seedEmojiGroupIcons(ctx, &stats); err != nil {
|
||||||
|
return stats, fmt.Errorf("seed emoji group icons: %w", err)
|
||||||
|
}
|
||||||
|
s.logSeedPhase("emoji_group_icons", phaseStarted, phaseBefore, stats)
|
||||||
|
|
||||||
if stats.Reactions == 0 && stats.StickerSets == 0 && stats.Effects == 0 {
|
if stats.Reactions == 0 && stats.StickerSets == 0 && stats.Effects == 0 {
|
||||||
stats.Skipped = true
|
stats.Skipped = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,16 @@ func (r *Router) onMessagesGetInlineBotResults(ctx context.Context, req *tg.Mess
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
peer, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
|
// inputPeerEmpty 是合法输入:客户端在未绑定具体聊天的全局搜索场景(如 GIF 面板的
|
||||||
if err != nil {
|
// 类目/搜索,尚未选定发送目标)会这样发。之前这里无条件走 checkedDomainPeerFromInputPeer,
|
||||||
return nil, err
|
// 把它当无效 peer 拒绝(PEER_ID_INVALID),导致 Android 的 GIF 类目/搜索请求每次都
|
||||||
|
// 400,客户端只能一直转圈——真实 Telegram 服务端接受这种"无聊天上下文"的查询。
|
||||||
|
var peer domain.Peer
|
||||||
|
if _, empty := req.Peer.(*tg.InputPeerEmpty); !empty {
|
||||||
|
peer, err = r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 内置(进程内)service bot 分支:@gif 没有 MTProto session、也没有 Bot API 消费者,
|
// 内置(进程内)service bot 分支:@gif 没有 MTProto session、也没有 Bot API 消费者,
|
||||||
// 走下面的「推 updateBotInlineQuery + 挂起 25s」必然超时,故同步问 responder。
|
// 走下面的「推 updateBotInlineQuery + 挂起 25s」必然超时,故同步问 responder。
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/iamxvbaba/td/tg"
|
"github.com/iamxvbaba/td/tg"
|
||||||
|
"github.com/iamxvbaba/td/tgerr"
|
||||||
"sort"
|
"sort"
|
||||||
"telesrv/internal/domain"
|
"telesrv/internal/domain"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
@ -25,6 +26,25 @@ func (r *Router) onMessagesSearchStickerSets(ctx context.Context, req *tg.Messag
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onMessagesSearchEmojiStickerSets 之前完全未注册,Android 的 emoji 分类点击/搜索会
|
||||||
|
// 无限重试这个方法(每次都拿 NOT_IMPLEMENTED,客户端不认为这是终态错误,一直重发),
|
||||||
|
// 表现为分类点击后结果区永远不出来。和 onMessagesSearchStickerSets 同策略,返回合法空结果。
|
||||||
|
func (r *Router) onMessagesSearchEmojiStickerSets(ctx context.Context, req *tg.MessagesSearchEmojiStickerSetsRequest) (tg.MessagesFoundStickerSetsClass, error) {
|
||||||
|
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||||
|
return nil, internalErr()
|
||||||
|
}
|
||||||
|
if utf8.RuneCountInString(req.Q) > maxStickerSearchQLength {
|
||||||
|
return nil, limitInvalidErr()
|
||||||
|
}
|
||||||
|
if req.Hash != 0 {
|
||||||
|
return &tg.MessagesFoundStickerSetsNotModified{}, nil
|
||||||
|
}
|
||||||
|
return &tg.MessagesFoundStickerSets{
|
||||||
|
Hash: 0,
|
||||||
|
Sets: []tg.StickerSetCoveredClass{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) onMessagesSearchStickers(ctx context.Context, req *tg.MessagesSearchStickersRequest) (tg.MessagesFoundStickersClass, error) {
|
func (r *Router) onMessagesSearchStickers(ctx context.Context, req *tg.MessagesSearchStickersRequest) (tg.MessagesFoundStickersClass, error) {
|
||||||
if _, _, err := r.currentUserID(ctx); err != nil {
|
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||||
return nil, internalErr()
|
return nil, internalErr()
|
||||||
|
|
@ -52,6 +72,28 @@ func (r *Router) onMessagesSearchStickers(ctx context.Context, req *tg.MessagesS
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func emoticonEmptyErr() error { return tgerr.New(400, "EMOTICON_EMPTY") }
|
||||||
|
|
||||||
|
// onMessagesSearchCustomEmoji 目前没有按 emoticon 反查自定义 emoji 的索引,故与
|
||||||
|
// onMessagesSearchStickers/onMessagesSearchStickerSets 同策略:返回合法的空结果而非
|
||||||
|
// NOT_IMPLEMENTED——之前完全未注册此方法时,Android 的 emoji 分类/搜索会拿到
|
||||||
|
// notImplementedErr(),表现为一直转圈(客户端没有为这个具体错误重置搜索 UI 状态)。
|
||||||
|
func (r *Router) onMessagesSearchCustomEmoji(ctx context.Context, req *tg.MessagesSearchCustomEmojiRequest) (tg.EmojiListClass, error) {
|
||||||
|
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||||
|
return nil, internalErr()
|
||||||
|
}
|
||||||
|
if req.Emoticon == "" {
|
||||||
|
return nil, emoticonEmptyErr()
|
||||||
|
}
|
||||||
|
if utf8.RuneCountInString(req.Emoticon) > maxStickerSearchQLength {
|
||||||
|
return nil, limitInvalidErr()
|
||||||
|
}
|
||||||
|
if req.Hash != 0 {
|
||||||
|
return &tg.EmojiListNotModified{}, nil
|
||||||
|
}
|
||||||
|
return &tg.EmojiList{Hash: 0, DocumentID: []int64{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) onMessagesGetSearchResultsCalendar(ctx context.Context, req *tg.MessagesGetSearchResultsCalendarRequest) (*tg.MessagesSearchResultsCalendar, error) {
|
func (r *Router) onMessagesGetSearchResultsCalendar(ctx context.Context, req *tg.MessagesGetSearchResultsCalendarRequest) (*tg.MessagesSearchResultsCalendar, error) {
|
||||||
if req.OffsetID < 0 || req.OffsetID > domain.MaxMessageBoxID || req.OffsetDate < 0 {
|
if req.OffsetID < 0 || req.OffsetID > domain.MaxMessageBoxID || req.OffsetDate < 0 {
|
||||||
return nil, limitInvalidErr()
|
return nil, limitInvalidErr()
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,12 @@ func (r *Router) registerMessages(d *tlprofile.Dispatcher) {
|
||||||
registerRPC[*tg.MessagesSearchStickersRequest](d, tlprofile.SemanticMethodMessagesSearchStickers, func(ctx context.Context, layerRequest *tg.MessagesSearchStickersRequest) (any, error) {
|
registerRPC[*tg.MessagesSearchStickersRequest](d, tlprofile.SemanticMethodMessagesSearchStickers, func(ctx context.Context, layerRequest *tg.MessagesSearchStickersRequest) (any, error) {
|
||||||
return r.onMessagesSearchStickers(ctx, layerRequest)
|
return r.onMessagesSearchStickers(ctx, layerRequest)
|
||||||
})
|
})
|
||||||
|
registerRPC[*tg.MessagesSearchCustomEmojiRequest](d, tlprofile.SemanticMethodMessagesSearchCustomEmoji, func(ctx context.Context, layerRequest *tg.MessagesSearchCustomEmojiRequest) (any, error) {
|
||||||
|
return r.onMessagesSearchCustomEmoji(ctx, layerRequest)
|
||||||
|
})
|
||||||
|
registerRPC[*tg.MessagesSearchEmojiStickerSetsRequest](d, tlprofile.SemanticMethodMessagesSearchEmojiStickerSets, func(ctx context.Context, layerRequest *tg.MessagesSearchEmojiStickerSetsRequest) (any, error) {
|
||||||
|
return r.onMessagesSearchEmojiStickerSets(ctx, layerRequest)
|
||||||
|
})
|
||||||
registerRPC[*tg.MessagesGetAttachMenuBotsRequest](d, tlprofile.SemanticMethodMessagesGetAttachMenuBots, func(ctx context.Context, layerRequest *tg.MessagesGetAttachMenuBotsRequest) (any, error) {
|
registerRPC[*tg.MessagesGetAttachMenuBotsRequest](d, tlprofile.SemanticMethodMessagesGetAttachMenuBots, func(ctx context.Context, layerRequest *tg.MessagesGetAttachMenuBotsRequest) (any, error) {
|
||||||
return r.onMessagesGetAttachMenuBots(ctx, layerRequest.
|
return r.onMessagesGetAttachMenuBots(ctx, layerRequest.
|
||||||
Hash)
|
Hash)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"groups": [
|
"groups": [
|
||||||
{
|
{
|
||||||
"title": "Love",
|
"title": "Love",
|
||||||
"icon_emoji_id": 5355088357070218139,
|
"icon_emoji_id": 9000000000001,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"❤",
|
"❤",
|
||||||
"😍",
|
"😍",
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Approval",
|
"title": "Approval",
|
||||||
"icon_emoji_id": 5357465415310124060,
|
"icon_emoji_id": 9000000000002,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"👍",
|
"👍",
|
||||||
"👏",
|
"👏",
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Disapproval",
|
"title": "Disapproval",
|
||||||
"icon_emoji_id": 5357216259962315463,
|
"icon_emoji_id": 9000000000003,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"👎",
|
"👎",
|
||||||
"😒",
|
"😒",
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Cheers",
|
"title": "Cheers",
|
||||||
"icon_emoji_id": 5357257143756005264,
|
"icon_emoji_id": 9000000000004,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"🎉",
|
"🎉",
|
||||||
"🥳",
|
"🥳",
|
||||||
|
|
@ -93,7 +93,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Laughter",
|
"title": "Laughter",
|
||||||
"icon_emoji_id": 5354785574760753200,
|
"icon_emoji_id": 9000000000005,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"😄",
|
"😄",
|
||||||
"😁",
|
"😁",
|
||||||
|
|
@ -110,7 +110,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Astonishment",
|
"title": "Astonishment",
|
||||||
"icon_emoji_id": 5354978715145085779,
|
"icon_emoji_id": 9000000000006,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"😨",
|
"😨",
|
||||||
"😦",
|
"😦",
|
||||||
|
|
@ -128,7 +128,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Sadness",
|
"title": "Sadness",
|
||||||
"icon_emoji_id": 5355181227148059369,
|
"icon_emoji_id": 9000000000007,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"😔",
|
"😔",
|
||||||
"😪",
|
"😪",
|
||||||
|
|
@ -152,7 +152,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Anger",
|
"title": "Anger",
|
||||||
"icon_emoji_id": 5355125405458113381,
|
"icon_emoji_id": 9000000000008,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"😡",
|
"😡",
|
||||||
"🤬",
|
"🤬",
|
||||||
|
|
@ -165,7 +165,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Neutral",
|
"title": "Neutral",
|
||||||
"icon_emoji_id": 5357252827313874903,
|
"icon_emoji_id": 9000000000009,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"😐",
|
"😐",
|
||||||
"😑",
|
"😑",
|
||||||
|
|
@ -180,7 +180,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Doubt",
|
"title": "Doubt",
|
||||||
"icon_emoji_id": 5354848362887653592,
|
"icon_emoji_id": 9000000000010,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"🤔",
|
"🤔",
|
||||||
"🤨",
|
"🤨",
|
||||||
|
|
@ -195,7 +195,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Silly",
|
"title": "Silly",
|
||||||
"icon_emoji_id": 5354832952544993830,
|
"icon_emoji_id": 9000000000011,
|
||||||
"emoticons": [
|
"emoticons": [
|
||||||
"🤪",
|
"🤪",
|
||||||
"😜",
|
"😜",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue