Skip to content

Commit 5a42ea6

Browse files
ernadoclaude
andcommitted
feat: add forum topic icons and modern sticker thumbnail methods
GetForumTopicIconStickers (messages.getStickerSet of the default topic icons set), plus SetStickerSetThumbnail and SetCustomEmojiStickerSetThumbnail (stickers.setStickerSetThumb). Dedupe the invalid custom_emoji_id error. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c74277c commit 5a42ea6

6 files changed

Lines changed: 177 additions & 2 deletions

File tree

errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,9 @@ func Code(err error) int {
9090

9191
return 0
9292
}
93+
94+
// errInvalidCustomEmojiID is returned when a custom_emoji_id is not a valid
95+
// integer document id.
96+
func errInvalidCustomEmojiID() *Error {
97+
return &Error{Code: 400, Description: "Bad Request: invalid custom_emoji_id"}
98+
}

forum_topics.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
6+
"github.com/gotd/td/tg"
7+
)
8+
9+
// GetForumTopicIconStickers returns custom emoji stickers that can be used as a
10+
// forum topic icon by any user.
11+
func (b *Bot) GetForumTopicIconStickers(ctx context.Context) ([]Sticker, error) {
12+
res, err := b.raw.MessagesGetStickerSet(ctx, &tg.MessagesGetStickerSetRequest{
13+
Stickerset: &tg.InputStickerSetEmojiDefaultTopicIcons{},
14+
Hash: 0,
15+
})
16+
if err != nil {
17+
return nil, asAPIError(err)
18+
}
19+
20+
set, ok := res.(*tg.MessagesStickerSet)
21+
if !ok {
22+
return nil, &Error{Code: 400, Description: "Bad Request: sticker set not found"}
23+
}
24+
25+
out := make([]Sticker, 0, len(set.Documents))
26+
27+
for _, d := range set.Documents {
28+
if doc, ok := d.(*tg.Document); ok {
29+
out = append(out, stickerFromDocument(doc, set.Set.ShortName, StickerCustomEmoji))
30+
}
31+
}
32+
33+
return out, nil
34+
}

reactions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func reactionToTg(r ReactionType) (tg.ReactionClass, error) {
1717
case ReactionTypeCustomEmoji:
1818
id, err := strconv.ParseInt(v.CustomEmojiID, 10, 64)
1919
if err != nil {
20-
return nil, &Error{Code: 400, Description: "Bad Request: invalid custom_emoji_id"}
20+
return nil, errInvalidCustomEmojiID()
2121
}
2222

2323
return &tg.ReactionCustomEmoji{DocumentID: id}, nil

sticker_edit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (b *Bot) GetCustomEmojiStickers(ctx context.Context, customEmojiIDs []strin
160160
for _, s := range customEmojiIDs {
161161
id, err := strconv.ParseInt(s, 10, 64)
162162
if err != nil {
163-
return nil, &Error{Code: 400, Description: "Bad Request: invalid custom_emoji_id"}
163+
return nil, errInvalidCustomEmojiID()
164164
}
165165

166166
ids = append(ids, id)

sticker_thumb.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
"strconv"
6+
7+
"github.com/gotd/td/tg"
8+
)
9+
10+
// SetStickerSetThumbnail sets the thumbnail of a regular or mask sticker set
11+
// owned by the bot. The thumbnail is referenced by file_id or uploaded; format
12+
// selects the uploaded file's encoding. This is the format-aware counterpart of
13+
// SetStickerSetThumb.
14+
func (b *Bot) SetStickerSetThumbnail(ctx context.Context, name string, thumb InputFile, format StickerFormat) error {
15+
var doc tg.InputDocumentClass
16+
17+
switch f := thumb.(type) {
18+
case InputFileID:
19+
d, err := inputDocumentFromFileID(string(f))
20+
if err != nil {
21+
return err
22+
}
23+
24+
doc = d
25+
case *InputFileUpload:
26+
uploaded, err := b.uploadStickerDocument(ctx, f, format)
27+
if err != nil {
28+
return err
29+
}
30+
31+
doc = &tg.InputDocument{ID: uploaded.ID, AccessHash: uploaded.AccessHash, FileReference: uploaded.FileReference}
32+
default:
33+
return &Error{Code: 400, Description: "Bad Request: thumbnail must be a file_id or an uploaded file"}
34+
}
35+
36+
req := &tg.StickersSetStickerSetThumbRequest{Stickerset: &tg.InputStickerSetShortName{ShortName: name}}
37+
req.SetThumb(doc)
38+
39+
if _, err := b.raw.StickersSetStickerSetThumb(ctx, req); err != nil {
40+
return asAPIError(err)
41+
}
42+
43+
return nil
44+
}
45+
46+
// SetCustomEmojiStickerSetThumbnail sets the thumbnail of a custom emoji sticker
47+
// set owned by the bot to one of its stickers, referenced by custom_emoji_id. An
48+
// empty customEmojiID drops the set thumbnail.
49+
func (b *Bot) SetCustomEmojiStickerSetThumbnail(ctx context.Context, name, customEmojiID string) error {
50+
req := &tg.StickersSetStickerSetThumbRequest{Stickerset: &tg.InputStickerSetShortName{ShortName: name}}
51+
52+
if customEmojiID != "" {
53+
id, err := strconv.ParseInt(customEmojiID, 10, 64)
54+
if err != nil {
55+
return errInvalidCustomEmojiID()
56+
}
57+
58+
req.SetThumbDocumentID(id)
59+
}
60+
61+
if _, err := b.raw.StickersSetStickerSetThumb(ctx, req); err != nil {
62+
return asAPIError(err)
63+
}
64+
65+
return nil
66+
}

sticker_thumb_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package botapi
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/gotd/td/tg"
8+
)
9+
10+
func TestGetForumTopicIconStickers(t *testing.T) {
11+
inv := newMockInvoker()
12+
inv.reply(tg.MessagesGetStickerSetRequestTypeID, &tg.MessagesStickerSet{
13+
Set: tg.StickerSet{ShortName: "topics", Emojis: true},
14+
Documents: []tg.DocumentClass{
15+
&tg.Document{
16+
ID: 1,
17+
MimeType: "image/webp",
18+
Attributes: []tg.DocumentAttributeClass{
19+
&tg.DocumentAttributeSticker{Alt: "🎯", Stickerset: &tg.InputStickerSetEmpty{}},
20+
},
21+
},
22+
},
23+
})
24+
25+
stickers, err := newMockBot(inv).GetForumTopicIconStickers(context.Background())
26+
if err != nil {
27+
t.Fatalf("GetForumTopicIconStickers: %v", err)
28+
}
29+
30+
if len(stickers) != 1 || stickers[0].Type != StickerCustomEmoji {
31+
t.Fatalf("stickers = %#v", stickers)
32+
}
33+
}
34+
35+
func TestSetStickerSetThumbnail(t *testing.T) {
36+
fid := documentFileID(t, 0x61)
37+
38+
inv := newMockInvoker()
39+
inv.reply(tg.StickersSetStickerSetThumbRequestTypeID, &tg.MessagesStickerSet{Set: tg.StickerSet{ShortName: "s"}})
40+
41+
if err := newMockBot(inv).SetStickerSetThumbnail(context.Background(), "s", FileID(fid), StickerFormatStatic); err != nil {
42+
t.Fatalf("SetStickerSetThumbnail: %v", err)
43+
}
44+
45+
var req tg.StickersSetStickerSetThumbRequest
46+
47+
inv.decode(t, tg.StickersSetStickerSetThumbRequestTypeID, &req)
48+
49+
if _, ok := req.GetThumb(); !ok {
50+
t.Fatal("thumb not set")
51+
}
52+
}
53+
54+
func TestSetCustomEmojiStickerSetThumbnail(t *testing.T) {
55+
inv := newMockInvoker()
56+
inv.reply(tg.StickersSetStickerSetThumbRequestTypeID, &tg.MessagesStickerSet{Set: tg.StickerSet{ShortName: "s"}})
57+
58+
if err := newMockBot(inv).SetCustomEmojiStickerSetThumbnail(context.Background(), "s", "777"); err != nil {
59+
t.Fatalf("SetCustomEmojiStickerSetThumbnail: %v", err)
60+
}
61+
62+
var req tg.StickersSetStickerSetThumbRequest
63+
64+
inv.decode(t, tg.StickersSetStickerSetThumbRequestTypeID, &req)
65+
66+
if id, ok := req.GetThumbDocumentID(); !ok || id != 777 {
67+
t.Fatalf("thumb document id = %d, ok=%v", id, ok)
68+
}
69+
}

0 commit comments

Comments
 (0)