fix: sync serve client sticker categories
This commit is contained in:
parent
a989e118d7
commit
2f995f607a
2 changed files with 80 additions and 4 deletions
|
|
@ -73,11 +73,33 @@ func normalizeStickerEmoticon(e string) string {
|
||||||
return strings.TrimSpace(e)
|
return strings.TrimSpace(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeStickerSearchEmoticon 解析官方客户端通过 messages.getStickers
|
||||||
|
// 传递的特殊贴纸类别标记。TDesktop、DrKLO Android 与 Telegram-iOS 都使用
|
||||||
|
// wave+star 获取 greeting、double-star 获取 premium preview、folder+star 获取
|
||||||
|
// premium/cloud catalog;它们不是普通复合 emoji,需先映射到 seed pack 的基础键。
|
||||||
|
//
|
||||||
|
// 只匹配这三个完整标记,不能把任意复合 emoji 拆分成单个 emoji,否则会改变普通
|
||||||
|
// sticker search 的精确匹配语义。先去掉 variation selector,可同时接纳三端的
|
||||||
|
// Unicode 表示差异。
|
||||||
|
func normalizeStickerSearchEmoticon(e string) string {
|
||||||
|
e = normalizeStickerEmoticon(e)
|
||||||
|
switch e {
|
||||||
|
case "👋⭐":
|
||||||
|
return "👋"
|
||||||
|
case "⭐⭐":
|
||||||
|
return "⭐"
|
||||||
|
case "📂⭐":
|
||||||
|
return "📂"
|
||||||
|
default:
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) onMessagesGetStickers(ctx context.Context, req *tg.MessagesGetStickersRequest) (tg.MessagesStickersClass, error) {
|
func (r *Router) onMessagesGetStickers(ctx context.Context, req *tg.MessagesGetStickersRequest) (tg.MessagesStickersClass, error) {
|
||||||
if req == nil || r.deps.Files == nil || r.emojiStickers == nil {
|
if req == nil || r.deps.Files == nil || r.emojiStickers == nil {
|
||||||
return &tg.MessagesStickers{Hash: 0, Stickers: []tg.DocumentClass{}}, nil
|
return &tg.MessagesStickers{Hash: 0, Stickers: []tg.DocumentClass{}}, nil
|
||||||
}
|
}
|
||||||
docIDs := r.emojiStickers.lookup(normalizeStickerEmoticon(req.Emoticon), func() map[string][]int64 {
|
docIDs := r.emojiStickers.lookup(normalizeStickerSearchEmoticon(req.Emoticon), func() map[string][]int64 {
|
||||||
return r.buildEmojiStickerIndex(ctx)
|
return r.buildEmojiStickerIndex(ctx)
|
||||||
})
|
})
|
||||||
if len(docIDs) > maxStickersPerEmoji {
|
if len(docIDs) > maxStickersPerEmoji {
|
||||||
|
|
|
||||||
|
|
@ -15,15 +15,21 @@ func emojiStickerRouter(t *testing.T) *Router {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
files := &fakeFiles{
|
files := &fakeFiles{
|
||||||
docs: map[int64]domain.Document{
|
docs: map[int64]domain.Document{
|
||||||
201: {ID: 201, AccessHash: 1, MimeType: "application/x-tgsticker"},
|
201: {ID: 201, AccessHash: 1, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}}},
|
||||||
202: {ID: 202, AccessHash: 2, MimeType: "application/x-tgsticker"},
|
202: {ID: 202, AccessHash: 2, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}}},
|
||||||
301: {ID: 301, AccessHash: 3, MimeType: "application/x-tgsticker"},
|
301: {ID: 301, AccessHash: 3, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}}},
|
||||||
|
401: {ID: 401, AccessHash: 4, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}}},
|
||||||
|
402: {ID: 402, AccessHash: 5, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}}},
|
||||||
|
403: {ID: 403, AccessHash: 6, MimeType: "application/x-tgsticker", Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}}},
|
||||||
},
|
},
|
||||||
sets: map[domain.StickerSetKind][]domain.StickerSet{
|
sets: map[domain.StickerSetKind][]domain.StickerSet{
|
||||||
domain.StickerSetKindStickers: {
|
domain.StickerSetKindStickers: {
|
||||||
{ID: 10, Hash: 1, Packs: []domain.StickerPack{
|
{ID: 10, Hash: 1, Packs: []domain.StickerPack{
|
||||||
{Emoticon: "👍", DocumentIDs: []int64{201, 202}},
|
{Emoticon: "👍", DocumentIDs: []int64{201, 202}},
|
||||||
{Emoticon: "🔥", DocumentIDs: []int64{301}},
|
{Emoticon: "🔥", DocumentIDs: []int64{301}},
|
||||||
|
{Emoticon: "👋", DocumentIDs: []int64{401}},
|
||||||
|
{Emoticon: "⭐", DocumentIDs: []int64{402}},
|
||||||
|
{Emoticon: "📂", DocumentIDs: []int64{403}},
|
||||||
}},
|
}},
|
||||||
{ID: 12, Hash: 2, Archived: true, Packs: []domain.StickerPack{
|
{ID: 12, Hash: 2, Archived: true, Packs: []domain.StickerPack{
|
||||||
{Emoticon: "👍", DocumentIDs: []int64{999}}, // 归档集应被排除
|
{Emoticon: "👍", DocumentIDs: []int64{999}}, // 归档集应被排除
|
||||||
|
|
@ -98,6 +104,54 @@ func TestMessagesGetStickersByEmoji(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMessagesGetStickersSpecialCategories 固定 TDesktop、DrKLO Android 与
|
||||||
|
// Telegram-iOS 共用的特殊类别标记。这些标记不是普通复合 emoji:服务端应把它们
|
||||||
|
// 解析到对应的基础目录,同时不能拆分任意多 emoji 查询。
|
||||||
|
func TestMessagesGetStickersSpecialCategories(t *testing.T) {
|
||||||
|
r := emojiStickerRouter(t)
|
||||||
|
ctx := WithUserID(context.Background(), 1000000001)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
emoticon string
|
||||||
|
base string
|
||||||
|
wantDocID int64
|
||||||
|
}{
|
||||||
|
{name: "greeting", emoticon: "👋⭐️", base: "👋", wantDocID: 401},
|
||||||
|
{name: "greeting_without_vs16", emoticon: "👋⭐", base: "👋", wantDocID: 401},
|
||||||
|
{name: "premium_preview", emoticon: "⭐️⭐️", base: "⭐", wantDocID: 402},
|
||||||
|
{name: "premium_preview_mixed_vs16", emoticon: "⭐⭐️", base: "⭐", wantDocID: 402},
|
||||||
|
{name: "all_premium", emoticon: "📂⭐️", base: "📂", wantDocID: 403},
|
||||||
|
{name: "all_premium_without_vs16", emoticon: "📂⭐", base: "📂", wantDocID: 403},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
base := mustStickers(t, r, ctx, tt.base, 0).(*tg.MessagesStickers)
|
||||||
|
got := mustStickers(t, r, ctx, tt.emoticon, 0)
|
||||||
|
ids := stickerDocIDs(t, got)
|
||||||
|
if len(ids) != 1 || ids[0] != tt.wantDocID {
|
||||||
|
t.Fatalf("%q stickers = %v, want [%d]", tt.emoticon, ids, tt.wantDocID)
|
||||||
|
}
|
||||||
|
full := got.(*tg.MessagesStickers)
|
||||||
|
if full.Hash == 0 || full.Hash != base.Hash {
|
||||||
|
t.Fatalf("%q hash = %d, base %q hash = %d", tt.emoticon, full.Hash, tt.base, base.Hash)
|
||||||
|
}
|
||||||
|
if again, err := r.onMessagesGetStickers(ctx, &tg.MessagesGetStickersRequest{
|
||||||
|
Emoticon: tt.emoticon,
|
||||||
|
Hash: full.Hash,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("getStickers %q hash: %v", tt.emoticon, err)
|
||||||
|
} else if _, ok := again.(*tg.MessagesStickersNotModified); !ok {
|
||||||
|
t.Fatalf("getStickers %q with matching hash = %T, want NotModified", tt.emoticon, again)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if ids := stickerDocIDs(t, mustStickers(t, r, ctx, "👋🔥", 0)); len(ids) != 0 {
|
||||||
|
t.Fatalf("ordinary compound emoji stickers = %v, want empty", ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func mustStickers(t *testing.T, r *Router, ctx context.Context, emoticon string, hash int64) tg.MessagesStickersClass {
|
func mustStickers(t *testing.T, r *Router, ctx context.Context, emoticon string, hash int64) tg.MessagesStickersClass {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
res, err := r.onMessagesGetStickers(ctx, &tg.MessagesGetStickersRequest{Emoticon: emoticon, Hash: hash})
|
res, err := r.onMessagesGetStickers(ctx, &tg.MessagesGetStickersRequest{Emoticon: emoticon, Hash: hash})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue