feat: sync built-in sticker bot
This commit is contained in:
parent
7096625e13
commit
6867d201ed
60 changed files with 7063 additions and 144 deletions
330
internal/rpc/sticker_link_smoke_test.go
Normal file
330
internal/rpc/sticker_link_smoke_test.go
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gotd/td/bin"
|
||||
"github.com/gotd/td/clock"
|
||||
"github.com/gotd/td/tg"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
appaccount "telesrv/internal/app/account"
|
||||
botsapp "telesrv/internal/app/bots"
|
||||
appmessages "telesrv/internal/app/messages"
|
||||
apppolls "telesrv/internal/app/polls"
|
||||
appusers "telesrv/internal/app/users"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/memory"
|
||||
"telesrv/internal/web/stickerlinks"
|
||||
)
|
||||
|
||||
func TestCustomStickerPackLinkInstallAndSendSmoke(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
alice, _ := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550009001", FirstName: "Alice"})
|
||||
bob, _ := userStore.Create(ctx, domain.User{AccessHash: 12, Phone: "15550009002", FirstName: "Bob"})
|
||||
dialogStore := memory.NewDialogStore()
|
||||
messageStore := memory.NewMessageStore(dialogStore)
|
||||
pollStore := memory.NewPollStore()
|
||||
messageStore.AttachPollStore(pollStore)
|
||||
passwordStore := memory.NewPasswordStore()
|
||||
files := &fakeFiles{
|
||||
docs: map[int64]domain.Document{
|
||||
101: {
|
||||
ID: 101,
|
||||
AccessHash: 1101,
|
||||
DCID: 2,
|
||||
MimeType: "image/webp",
|
||||
Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrSticker}},
|
||||
},
|
||||
},
|
||||
photos: map[int64]domain.Photo{},
|
||||
sets: map[domain.StickerSetKind][]domain.StickerSet{},
|
||||
}
|
||||
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
||||
Account: appaccount.NewService(passwordStore, appaccount.WithUserStickerSets(passwordStore)),
|
||||
Users: appusers.NewService(userStore),
|
||||
Messages: appmessages.NewService(messageStore, dialogStore),
|
||||
Files: files,
|
||||
Polls: apppolls.NewService(pollStore),
|
||||
Sessions: &captureSessions{},
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
created, err := r.onStickersCreateStickerSet(WithUserID(ctx, alice.ID), &tg.StickersCreateStickerSetRequest{
|
||||
UserID: &tg.InputUserSelf{},
|
||||
Title: "Alice Fresh Pack",
|
||||
ShortName: "alice_fresh_pack",
|
||||
Stickers: []tg.InputStickerSetItem{{
|
||||
Document: &tg.InputDocument{ID: 101, AccessHash: 1101},
|
||||
Emoji: "🙂",
|
||||
Keywords: "fresh",
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create sticker set: %v", err)
|
||||
}
|
||||
createdFull, ok := created.(*tg.MessagesStickerSet)
|
||||
if !ok {
|
||||
t.Fatalf("created = %T, want *tg.MessagesStickerSet", created)
|
||||
}
|
||||
|
||||
web := stickerlinks.NewHandler(files, "https://telesrv.net")
|
||||
rr := httptest.NewRecorder()
|
||||
web.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/addstickers/alice_fresh_pack", nil))
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("sticker link status = %d body=%q, want 200", rr.Code, rr.Body.String())
|
||||
}
|
||||
body := rr.Body.String()
|
||||
for _, want := range []string{"https://telesrv.net/addstickers/alice_fresh_pack", "telesrv://addstickers?set=alice_fresh_pack", "tg://addstickers?set=alice_fresh_pack"} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Fatalf("sticker link body missing %q:\n%s", want, body)
|
||||
}
|
||||
}
|
||||
if strings.Contains(body, `window.location.href = "tg://`) {
|
||||
t.Fatalf("sticker link must auto-open telesrv://, not tg://:\n%s", body)
|
||||
}
|
||||
|
||||
preview, err := r.onMessagesGetStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesGetStickerSetRequest{
|
||||
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_fresh_pack"},
|
||||
Hash: 0,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bob preview sticker set: %v", err)
|
||||
}
|
||||
previewFull, ok := preview.(*tg.MessagesStickerSet)
|
||||
if !ok || previewFull.Set.ID != createdFull.Set.ID || len(previewFull.Documents) != 1 {
|
||||
t.Fatalf("preview = %T %+v, want created set with one document", preview, preview)
|
||||
}
|
||||
|
||||
if _, err := r.onMessagesInstallStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesInstallStickerSetRequest{
|
||||
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_fresh_pack"},
|
||||
}); err != nil {
|
||||
t.Fatalf("bob install sticker set: %v", err)
|
||||
}
|
||||
if got := installedStickerSetIDs(t, passwordStore, ctx, bob.ID, domain.StickerSetKindStickers, nil); len(got) != 1 || got[0] != createdFull.Set.ID {
|
||||
t.Fatalf("bob installed sets = %v, want [%d]", got, createdFull.Set.ID)
|
||||
}
|
||||
|
||||
if _, err := r.onMessagesSendMedia(WithUserID(ctx, bob.ID), &tg.MessagesSendMediaRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: alice.ID, AccessHash: alice.AccessHash},
|
||||
Media: &tg.InputMediaDocument{ID: &tg.InputDocument{ID: 101, AccessHash: 1101}},
|
||||
RandomID: 7001,
|
||||
}); err != nil {
|
||||
t.Fatalf("bob send sticker: %v", err)
|
||||
}
|
||||
|
||||
historyReq := &tg.MessagesGetHistoryRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: bob.ID, AccessHash: bob.AccessHash},
|
||||
Limit: 10,
|
||||
}
|
||||
var raw bin.Buffer
|
||||
if err := historyReq.Encode(&raw); err != nil {
|
||||
t.Fatalf("encode history request: %v", err)
|
||||
}
|
||||
enc, err := r.Dispatch(WithUserID(ctx, alice.ID), [8]byte{}, 0, &raw)
|
||||
if err != nil {
|
||||
t.Fatalf("alice get history: %v", err)
|
||||
}
|
||||
box, ok := enc.(*tg.MessagesMessagesBox)
|
||||
if !ok {
|
||||
t.Fatalf("history response = %T, want *tg.MessagesMessagesBox", enc)
|
||||
}
|
||||
messages, ok := box.Messages.(*tg.MessagesMessages)
|
||||
if !ok {
|
||||
t.Fatalf("history payload = %T, want *tg.MessagesMessages", box.Messages)
|
||||
}
|
||||
if len(messages.Messages) != 1 {
|
||||
t.Fatalf("history messages = %d, want 1", len(messages.Messages))
|
||||
}
|
||||
msg, ok := messages.Messages[0].(*tg.Message)
|
||||
if !ok {
|
||||
t.Fatalf("history message = %T, want *tg.Message", messages.Messages[0])
|
||||
}
|
||||
media, ok := msg.Media.(*tg.MessageMediaDocument)
|
||||
if !ok {
|
||||
t.Fatalf("history media = %T, want *tg.MessageMediaDocument", msg.Media)
|
||||
}
|
||||
if got := tgDocumentID(media.Document); got != 101 {
|
||||
t.Fatalf("history document id = %d, want 101", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStickersBotCreatePackLinkInstallIsolationSmoke(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
alice, _ := userStore.Create(ctx, domain.User{AccessHash: 21, Phone: "15550009101", FirstName: "Alice"})
|
||||
bob, _ := userStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550009102", FirstName: "Bob"})
|
||||
dialogStore := memory.NewDialogStore()
|
||||
messageStore := memory.NewMessageStore(dialogStore)
|
||||
pollStore := memory.NewPollStore()
|
||||
messageStore.AttachPollStore(pollStore)
|
||||
passwordStore := memory.NewPasswordStore()
|
||||
accountService := appaccount.NewService(passwordStore, appaccount.WithUserStickerSets(passwordStore))
|
||||
botStore := memory.NewBotStore(userStore)
|
||||
files := &fakeFiles{
|
||||
docs: map[int64]domain.Document{
|
||||
401: {
|
||||
ID: 401,
|
||||
AccessHash: 4401,
|
||||
DCID: 2,
|
||||
MimeType: "image/webp",
|
||||
Size: 4096,
|
||||
Attributes: []domain.DocumentAttribute{{Kind: domain.DocAttrFilename, FileName: "alice.webp"}},
|
||||
},
|
||||
},
|
||||
photos: map[int64]domain.Photo{},
|
||||
sets: map[domain.StickerSetKind][]domain.StickerSet{},
|
||||
}
|
||||
botsService := botsapp.NewService(userStore, botStore, messageStore,
|
||||
botsapp.WithStickerSetCreator(files),
|
||||
botsapp.WithUserStickerSets(accountService))
|
||||
messagesService := appmessages.NewService(messageStore, dialogStore,
|
||||
appmessages.WithBotResponder(botsService))
|
||||
r := New(Config{DC: 2, IP: "127.0.0.1", Port: 2398}, Deps{
|
||||
Account: accountService,
|
||||
Users: appusers.NewService(userStore),
|
||||
Messages: messagesService,
|
||||
Files: files,
|
||||
Polls: apppolls.NewService(pollStore),
|
||||
Sessions: &captureSessions{},
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
botsService.SetRouterHooks(r)
|
||||
|
||||
sendStickersBotText(t, r, alice, "/newpack", 9101)
|
||||
waitForStickersReply(t, messageStore, alice.ID, "sticker pack")
|
||||
sendStickersBotText(t, r, alice, "Alice Bot Pack", 9102)
|
||||
waitForStickersReply(t, messageStore, alice.ID, "Lottie JSON")
|
||||
sendStickersBotDocument(t, r, alice, 401, 4401, 9103)
|
||||
waitForStickersReply(t, messageStore, alice.ID, "emoji")
|
||||
sendStickersBotText(t, r, alice, "🙂", 9104)
|
||||
waitForStickersReply(t, messageStore, alice.ID, "Added")
|
||||
sendStickersBotText(t, r, alice, "/publish", 9105)
|
||||
waitForStickersReply(t, messageStore, alice.ID, "short name")
|
||||
sendStickersBotText(t, r, alice, "alice_bot_pack", 9106)
|
||||
waitForStickersReply(t, messageStore, alice.ID, "https://telesrv.net/addstickers/alice_bot_pack")
|
||||
|
||||
created := files.sets[domain.StickerSetKindStickers]
|
||||
if len(created) != 1 || created[0].ShortName != "alice_bot_pack" || created[0].CreatorUserID != alice.ID {
|
||||
t.Fatalf("created sets = %+v, want Alice alice_bot_pack", created)
|
||||
}
|
||||
setID := created[0].ID
|
||||
if got := installedStickerSetIDs(t, passwordStore, ctx, alice.ID, domain.StickerSetKindStickers, nil); len(got) != 1 || got[0] != setID {
|
||||
t.Fatalf("alice installed sets = %v, want [%d]", got, setID)
|
||||
}
|
||||
if got := installedStickerSetIDs(t, passwordStore, ctx, bob.ID, domain.StickerSetKindStickers, nil); len(got) != 0 {
|
||||
t.Fatalf("bob installed sets before link = %v, want empty", got)
|
||||
}
|
||||
if got := allStickerSetIDs(t, r, WithUserID(ctx, bob.ID), domain.StickerSetKindStickers); len(got) != 0 {
|
||||
t.Fatalf("bob getAllStickers before install = %v, want empty", got)
|
||||
}
|
||||
|
||||
web := stickerlinks.NewHandler(files, "https://telesrv.net")
|
||||
rr := httptest.NewRecorder()
|
||||
web.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/addstickers/alice_bot_pack", nil))
|
||||
if rr.Code != http.StatusOK || !strings.Contains(rr.Body.String(), "https://telesrv.net/addstickers/alice_bot_pack") {
|
||||
t.Fatalf("sticker bot link response = %d %q", rr.Code, rr.Body.String())
|
||||
}
|
||||
preview, err := r.onMessagesGetStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesGetStickerSetRequest{
|
||||
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_bot_pack"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bob preview bot-created sticker set: %v", err)
|
||||
}
|
||||
previewFull, ok := preview.(*tg.MessagesStickerSet)
|
||||
if !ok || previewFull.Set.ID != setID || previewFull.Set.InstalledDate != 0 {
|
||||
t.Fatalf("bob preview = %T %+v, want uninstalled created set", preview, preview)
|
||||
}
|
||||
if _, err := r.onMessagesInstallStickerSet(WithUserID(ctx, bob.ID), &tg.MessagesInstallStickerSetRequest{
|
||||
Stickerset: &tg.InputStickerSetShortName{ShortName: "alice_bot_pack"},
|
||||
}); err != nil {
|
||||
t.Fatalf("bob install bot-created sticker set: %v", err)
|
||||
}
|
||||
if got := installedStickerSetIDs(t, passwordStore, ctx, bob.ID, domain.StickerSetKindStickers, nil); len(got) != 1 || got[0] != setID {
|
||||
t.Fatalf("bob installed sets after link = %v, want [%d]", got, setID)
|
||||
}
|
||||
if got := allStickerSetIDs(t, r, WithUserID(ctx, alice.ID), domain.StickerSetKindStickers); len(got) != 1 || got[0] != setID {
|
||||
t.Fatalf("alice getAllStickers = %v, want [%d]", got, setID)
|
||||
}
|
||||
if got := allStickerSetIDs(t, r, WithUserID(ctx, bob.ID), domain.StickerSetKindStickers); len(got) != 1 || got[0] != setID {
|
||||
t.Fatalf("bob getAllStickers after install = %v, want [%d]", got, setID)
|
||||
}
|
||||
if got := allStickerSetIDs(t, r, WithUserID(ctx, bob.ID), domain.StickerSetKindEmoji); len(got) != 0 {
|
||||
t.Fatalf("bob getEmojiStickers = %v, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func sendStickersBotText(t *testing.T, r *Router, user domain.User, text string, randomID int64) {
|
||||
t.Helper()
|
||||
if _, err := r.onMessagesSendMessage(WithUserID(context.Background(), user.ID), &tg.MessagesSendMessageRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: domain.StickersBotUserID, AccessHash: domain.StickersBotAccessHash},
|
||||
Message: text,
|
||||
RandomID: randomID,
|
||||
}); err != nil {
|
||||
t.Fatalf("send @Stickers text %q: %v", text, err)
|
||||
}
|
||||
}
|
||||
|
||||
func sendStickersBotDocument(t *testing.T, r *Router, user domain.User, docID, accessHash, randomID int64) {
|
||||
t.Helper()
|
||||
if _, err := r.onMessagesSendMedia(WithUserID(context.Background(), user.ID), &tg.MessagesSendMediaRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: domain.StickersBotUserID, AccessHash: domain.StickersBotAccessHash},
|
||||
Media: &tg.InputMediaDocument{ID: &tg.InputDocument{ID: docID, AccessHash: accessHash}},
|
||||
RandomID: randomID,
|
||||
}); err != nil {
|
||||
t.Fatalf("send @Stickers document %d: %v", docID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForStickersReply(t *testing.T, messages *memory.MessageStore, userID int64, want string) string {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(time.Second)
|
||||
for {
|
||||
list, err := messages.ListByUser(context.Background(), userID, domain.MessageFilter{
|
||||
HasPeer: true,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: domain.StickersBotUserID},
|
||||
Limit: 100,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("list @Stickers history: %v", err)
|
||||
}
|
||||
for _, msg := range list.Messages {
|
||||
if msg.From.ID == domain.StickersBotUserID && strings.Contains(msg.Body, want) {
|
||||
return msg.Body
|
||||
}
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatalf("no @Stickers reply containing %q; history=%+v", want, list.Messages)
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func allStickerSetIDs(t *testing.T, r *Router, ctx context.Context, kind domain.StickerSetKind) []int64 {
|
||||
t.Helper()
|
||||
var (
|
||||
out tg.MessagesAllStickersClass
|
||||
err error
|
||||
)
|
||||
if kind == domain.StickerSetKindEmoji {
|
||||
out, err = r.onMessagesGetEmojiStickers(ctx, 0)
|
||||
} else {
|
||||
out, err = r.onMessagesGetAllStickers(ctx, 0)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("get sticker sets for kind %s: %v", kind, err)
|
||||
}
|
||||
full, ok := out.(*tg.MessagesAllStickers)
|
||||
if !ok {
|
||||
t.Fatalf("get sticker sets for kind %s = %T, want *tg.MessagesAllStickers", kind, out)
|
||||
}
|
||||
ids := make([]int64, 0, len(full.Sets))
|
||||
for _, set := range full.Sets {
|
||||
ids = append(ids, set.ID)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue