move gotd fork into repo. (#111)
- update to latest telegram layer - remove some references to fields in tg.Entities that don't exist in the schema - originally added here: https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e - referenced here - https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3 - https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// ArticleResultBuilder is article result option builder.
|
||||
type ArticleResultBuilder struct {
|
||||
result *tg.InputBotInlineResult
|
||||
msg MessageOption
|
||||
}
|
||||
|
||||
func (b *ArticleResultBuilder) apply(r *resultPageBuilder) error {
|
||||
m, err := b.msg.apply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := tg.InputBotInlineResult{
|
||||
ID: b.result.ID,
|
||||
Type: b.result.Type,
|
||||
Title: b.result.Title,
|
||||
Description: b.result.Description,
|
||||
URL: b.result.URL,
|
||||
Thumb: b.result.Thumb,
|
||||
Content: b.result.Content,
|
||||
}
|
||||
if t.ID == "" {
|
||||
t.ID, err = r.generateID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t.SendMessage = m
|
||||
r.results = append(r.results, &t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID sets ID of result.
|
||||
// Should not be empty, so if id is not provided, random will be used.
|
||||
func (b *ArticleResultBuilder) ID(id string) *ArticleResultBuilder {
|
||||
b.result.ID = id
|
||||
return b
|
||||
}
|
||||
|
||||
// Title sets Result description.
|
||||
func (b *ArticleResultBuilder) Title(title string) *ArticleResultBuilder {
|
||||
b.result.SetTitle(title)
|
||||
return b
|
||||
}
|
||||
|
||||
// Description sets Result description.
|
||||
func (b *ArticleResultBuilder) Description(description string) *ArticleResultBuilder {
|
||||
b.result.SetDescription(description)
|
||||
return b
|
||||
}
|
||||
|
||||
// URL sets URL of result.
|
||||
func (b *ArticleResultBuilder) URL(url string) *ArticleResultBuilder {
|
||||
b.result.SetURL(url)
|
||||
return b
|
||||
}
|
||||
|
||||
// Thumb sets Thumbnail for result.
|
||||
func (b *ArticleResultBuilder) Thumb(thumb tg.InputWebDocument) *ArticleResultBuilder {
|
||||
b.result.SetThumb(thumb)
|
||||
return b
|
||||
}
|
||||
|
||||
// Content sets Result contents.
|
||||
func (b *ArticleResultBuilder) Content(content tg.InputWebDocument) *ArticleResultBuilder {
|
||||
b.result.SetContent(content)
|
||||
return b
|
||||
}
|
||||
|
||||
// Article creates article result option builder.
|
||||
func Article(title string, msg MessageOption) *ArticleResultBuilder {
|
||||
return &ArticleResultBuilder{
|
||||
result: &tg.InputBotInlineResult{
|
||||
Type: ArticleType,
|
||||
Title: title,
|
||||
},
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
func TestArticle(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder, mock := testBuilder(t)
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(10), v.QueryID)
|
||||
|
||||
for i := range v.Results {
|
||||
r, ok := v.Results[i].(*tg.InputBotInlineResult)
|
||||
require.True(t, ok)
|
||||
require.NotZero(t, r.ID)
|
||||
require.Equal(t, r.Title, r.Type)
|
||||
require.Equal(t, r.Description, r.Title)
|
||||
require.Equal(t, r.URL, r.Description)
|
||||
}
|
||||
}).ThenTrue()
|
||||
_, err := builder.Set(ctx,
|
||||
Article(ArticleType, MessageText("article")).
|
||||
Description(ArticleType).URL(ArticleType),
|
||||
Article(ArticleType, MediaAuto("article")).ID("10").Title(ArticleType).
|
||||
Description(ArticleType).URL(ArticleType),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.Expect().ThenRPCErr(testRPCError())
|
||||
_, err = builder.Set(ctx,
|
||||
Article(ArticleType, MessageText("article")),
|
||||
)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// Package inline contains inline query results builder.
|
||||
package inline
|
||||
@@ -0,0 +1,99 @@
|
||||
package inline
|
||||
|
||||
import "go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
|
||||
// DocumentResultBuilder is document result option builder.
|
||||
type DocumentResultBuilder struct {
|
||||
result *tg.InputBotInlineResultDocument
|
||||
msg MessageOption
|
||||
}
|
||||
|
||||
func (b *DocumentResultBuilder) apply(r *resultPageBuilder) error {
|
||||
m, err := b.msg.apply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := tg.InputBotInlineResultDocument{
|
||||
ID: b.result.ID,
|
||||
Type: b.result.Type,
|
||||
Title: b.result.Title,
|
||||
Description: b.result.Description,
|
||||
Document: b.result.Document,
|
||||
}
|
||||
if t.ID == "" {
|
||||
t.ID, err = r.generateID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t.SendMessage = m
|
||||
r.results = append(r.results, &t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID sets ID of result.
|
||||
// Should not be empty, so if id is not provided, random will be used.
|
||||
func (b *DocumentResultBuilder) ID(id string) *DocumentResultBuilder {
|
||||
b.result.ID = id
|
||||
return b
|
||||
}
|
||||
|
||||
// Title sets Result description.
|
||||
func (b *DocumentResultBuilder) Title(title string) *DocumentResultBuilder {
|
||||
b.result.SetTitle(title)
|
||||
return b
|
||||
}
|
||||
|
||||
// Description sets Result description.
|
||||
func (b *DocumentResultBuilder) Description(description string) *DocumentResultBuilder {
|
||||
b.result.SetDescription(description)
|
||||
return b
|
||||
}
|
||||
|
||||
// Document creates document result option builder.
|
||||
func Document(doc tg.InputDocumentClass, typ string, msg MessageOption) *DocumentResultBuilder {
|
||||
return &DocumentResultBuilder{
|
||||
result: &tg.InputBotInlineResultDocument{
|
||||
Type: typ,
|
||||
Document: doc,
|
||||
},
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// Video creates video result option builder.
|
||||
func Video(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, VideoType, msg)
|
||||
}
|
||||
|
||||
// Audio creates audio result option builder.
|
||||
func Audio(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, AudioType, msg)
|
||||
}
|
||||
|
||||
// File creates document result option builder.
|
||||
func File(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, DocumentType, msg)
|
||||
}
|
||||
|
||||
// GIF creates gif result option builder.
|
||||
func GIF(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, GIFType, msg)
|
||||
}
|
||||
|
||||
// MPEG4GIF creates mpeg4gif result option builder.
|
||||
func MPEG4GIF(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, MPEG4GIFType, msg)
|
||||
}
|
||||
|
||||
// Voice creates voice result option builder.
|
||||
func Voice(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, VoiceType, msg)
|
||||
}
|
||||
|
||||
// Sticker creates sticker result option builder.
|
||||
func Sticker(doc tg.InputDocumentClass, msg MessageOption) *DocumentResultBuilder {
|
||||
return Document(doc, StickerType, msg)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
func TestDocument(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder, mock := testBuilder(t)
|
||||
doc := &tg.InputDocument{ID: 10, AccessHash: 10, FileReference: []byte{10}}
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(10), v.QueryID)
|
||||
|
||||
for i := range v.Results {
|
||||
r, ok := v.Results[i].(*tg.InputBotInlineResultDocument)
|
||||
require.True(t, ok)
|
||||
require.NotZero(t, r.ID)
|
||||
require.Equal(t, doc, r.Document)
|
||||
require.Equal(t, r.Title, r.Type)
|
||||
require.Equal(t, r.Description, r.Title)
|
||||
}
|
||||
}).ThenTrue()
|
||||
_, err := builder.Set(ctx,
|
||||
Video(doc, MessageText("video")).Title(VideoType).
|
||||
Description(VideoType),
|
||||
File(doc, MessageText("file")).ID("10").Title(DocumentType).
|
||||
Description(DocumentType),
|
||||
Audio(doc, MessageText("audio")).ID("10").Title(AudioType).
|
||||
Description(AudioType),
|
||||
GIF(doc, MessageText("gif")).ID("10").Title(GIFType).
|
||||
Description(GIFType),
|
||||
MPEG4GIF(doc, MessageText("mpeg4gif")).ID("10").Title(MPEG4GIFType).
|
||||
Description(MPEG4GIFType),
|
||||
Voice(doc, MessageText("voice")).ID("10").Title(VoiceType).
|
||||
Description(VoiceType),
|
||||
Sticker(doc, MessageText("sticker")).ID("10").Title(StickerType).
|
||||
Description(StickerType),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.Expect().ThenRPCErr(testRPCError())
|
||||
_, err = builder.Set(ctx,
|
||||
Video(doc, MessageText("video")).Title(VideoType).
|
||||
Description(VideoType),
|
||||
)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// GameResultBuilder is game result option builder.
|
||||
type GameResultBuilder struct {
|
||||
result *tg.InputBotInlineResultGame
|
||||
msg MessageOption
|
||||
}
|
||||
|
||||
func (b *GameResultBuilder) apply(r *resultPageBuilder) error {
|
||||
m, err := b.msg.apply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := tg.InputBotInlineResultGame{
|
||||
ID: b.result.ID,
|
||||
ShortName: b.result.ShortName,
|
||||
}
|
||||
if t.ID == "" {
|
||||
t.ID, err = r.generateID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t.SendMessage = m
|
||||
r.results = append(r.results, &t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID sets ID of result.
|
||||
// Should not be empty, so if id is not provided, random will be used.
|
||||
func (b *GameResultBuilder) ID(id string) *GameResultBuilder {
|
||||
b.result.ID = id
|
||||
return b
|
||||
}
|
||||
|
||||
// Game creates game result option builder.
|
||||
func Game(shortName string, msg MessageOption) *GameResultBuilder {
|
||||
return &GameResultBuilder{
|
||||
result: &tg.InputBotInlineResultGame{
|
||||
ShortName: shortName,
|
||||
},
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
func TestGame(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder, mock := testBuilder(t)
|
||||
gameName := "game"
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(10), v.QueryID)
|
||||
|
||||
for i := range v.Results {
|
||||
r, ok := v.Results[i].(*tg.InputBotInlineResultGame)
|
||||
require.True(t, ok)
|
||||
require.NotZero(t, r.ID)
|
||||
require.Equal(t, gameName, r.ShortName)
|
||||
}
|
||||
}).ThenTrue()
|
||||
_, err := builder.Set(ctx,
|
||||
Game(gameName, MessageText("game")),
|
||||
Game(gameName, MessageText("game")).ID("10"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.Expect().ThenRPCErr(testRPCError())
|
||||
_, err = builder.Set(ctx,
|
||||
Game(gameName, MessageText("game")),
|
||||
Game(gameName, MessageText("game")).ID("10"),
|
||||
)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// ResultBuilder is inline result builder.
|
||||
type ResultBuilder struct {
|
||||
raw *tg.Client
|
||||
random io.Reader
|
||||
// Set this flag if the results are composed of media files
|
||||
gallery bool
|
||||
// Set this flag if results may be cached on the server side only for the user that sent
|
||||
// the query. By default, results may be returned to any user who sends the same query
|
||||
private bool
|
||||
// Unique identifier for the answered query
|
||||
queryID int64
|
||||
// The maximum amount of time in seconds that the result of the inline query may be
|
||||
// cached on the server. Defaults to 300.
|
||||
cacheTime int
|
||||
// Pass the offset that a client should send in the next query with the same text to
|
||||
// receive more results. Pass an empty string if there are no more results or if you
|
||||
// don‘t support pagination. Offset length can’t exceed 64 bytes.
|
||||
nextOffset string
|
||||
// If passed, clients will display a button with specified text that switches the user to
|
||||
// a private chat with the bot and sends the bot a start message with a certain parameter.
|
||||
switchPm tg.InlineBotSwitchPM
|
||||
// If passed, clients will display a button on top of the remaining inline result list
|
||||
// with the specified text, that switches the user to the specified bot web app.
|
||||
switchWebview tg.InlineBotWebView
|
||||
}
|
||||
|
||||
// New creates new ResultBuilder.
|
||||
func New(raw *tg.Client, random io.Reader, queryID int64) *ResultBuilder {
|
||||
return &ResultBuilder{raw: raw, random: random, queryID: queryID}
|
||||
}
|
||||
|
||||
// Gallery sets flag if the results are composed of media files.
|
||||
func (r *ResultBuilder) Gallery(gallery bool) *ResultBuilder {
|
||||
r.gallery = gallery
|
||||
return r
|
||||
}
|
||||
|
||||
// Private sets flag if results may be cached on the server side only for the user that sent
|
||||
// the query. By default, results may be returned to any user who sends the same query.
|
||||
func (r *ResultBuilder) Private(private bool) *ResultBuilder {
|
||||
r.private = private
|
||||
return r
|
||||
}
|
||||
|
||||
// CacheTime sets the maximum amount of time that the result of the inline query may be
|
||||
// cached on the server. Server's default is 300 seconds.
|
||||
func (r *ResultBuilder) CacheTime(cacheTime time.Duration) *ResultBuilder {
|
||||
return r.CacheTimeSeconds(int(cacheTime.Seconds()))
|
||||
}
|
||||
|
||||
// CacheTimeSeconds sets the maximum amount of time in seconds that the result of the inline query may be
|
||||
// cached on the server. Server's default is 300.
|
||||
func (r *ResultBuilder) CacheTimeSeconds(cacheTime int) *ResultBuilder {
|
||||
r.cacheTime = cacheTime
|
||||
return r
|
||||
}
|
||||
|
||||
// NextOffset sets offset that a client should send in the next query with the same text to
|
||||
// receive more results. Pass an empty string if there are no more results or if you
|
||||
// don‘t support pagination. Offset length can’t exceed 64 bytes.
|
||||
func (r *ResultBuilder) NextOffset(nextOffset string) *ResultBuilder {
|
||||
r.nextOffset = nextOffset
|
||||
return r
|
||||
}
|
||||
|
||||
// SwitchPM sets SwitchPm field.
|
||||
//
|
||||
// If passed, clients will display a button with specified text that switches the user to
|
||||
// a private chat with the bot and sends the bot a start message with a certain parameter.
|
||||
func (r *ResultBuilder) SwitchPM(text, startParam string) *ResultBuilder {
|
||||
r.switchPm = tg.InlineBotSwitchPM{
|
||||
Text: text,
|
||||
StartParam: startParam,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// SwitchWebview sets SwitchWebview field.
|
||||
//
|
||||
// If passed, clients will display a button on top of the remaining inline result list
|
||||
// with the specified text, that switches the user to the specified bot web app.
|
||||
func (r *ResultBuilder) SwitchWebview(text, url string) *ResultBuilder {
|
||||
r.switchWebview = tg.InlineBotWebView{
|
||||
Text: text,
|
||||
URL: url,
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Set sets inline results for given query.
|
||||
func (r *ResultBuilder) Set(ctx context.Context, opts ...ResultOption) (bool, error) {
|
||||
res := resultPageBuilder{
|
||||
results: nil,
|
||||
random: r.random,
|
||||
}
|
||||
|
||||
for idx, opt := range opts {
|
||||
if err := opt.apply(&res); err != nil {
|
||||
return false, errors.Wrapf(err, "apply %d option", idx+1)
|
||||
}
|
||||
}
|
||||
|
||||
ok, err := r.raw.MessagesSetInlineBotResults(ctx, &tg.MessagesSetInlineBotResultsRequest{
|
||||
Private: r.private,
|
||||
QueryID: r.queryID,
|
||||
Results: res.results,
|
||||
CacheTime: r.cacheTime,
|
||||
NextOffset: r.nextOffset,
|
||||
SwitchPm: r.switchPm,
|
||||
Gallery: r.gallery,
|
||||
SwitchWebview: r.switchWebview,
|
||||
})
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "set inline results")
|
||||
}
|
||||
|
||||
return ok, nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tgerr"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tgmock"
|
||||
)
|
||||
|
||||
func testBuilder(t *testing.T) (*ResultBuilder, *tgmock.Mock) {
|
||||
mock := tgmock.New(t)
|
||||
sender := New(tg.NewClient(mock), rand.Reader, 10)
|
||||
return sender, mock
|
||||
}
|
||||
|
||||
func testRPCError() *tgerr.Error {
|
||||
return &tgerr.Error{
|
||||
Code: 1337,
|
||||
Message: "TEST_ERROR",
|
||||
Type: "TEST_ERROR",
|
||||
}
|
||||
}
|
||||
|
||||
func TestResultBuilder_Set(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder, mock := testBuilder(t)
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.True(t, v.Gallery)
|
||||
}).ThenTrue()
|
||||
_, err := builder.Gallery(true).Set(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.True(t, v.Private)
|
||||
}).ThenTrue()
|
||||
_, err = builder.Private(true).Set(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 1, v.CacheTime)
|
||||
}).ThenTrue()
|
||||
_, err = builder.CacheTime(time.Second).Set(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "offset", v.NextOffset)
|
||||
}).ThenTrue()
|
||||
_, err = builder.NextOffset("offset").Set(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/entity"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/markup"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/styling"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// MessageMediaAutoBuilder is a builder of inline result text message.
|
||||
type MessageMediaAutoBuilder struct {
|
||||
message *tg.InputBotInlineMessageMediaAuto
|
||||
options []styling.StyledTextOption
|
||||
}
|
||||
|
||||
func (b *MessageMediaAutoBuilder) apply() (tg.InputBotInlineMessageClass, error) {
|
||||
tb := entity.Builder{}
|
||||
if err := styling.Perform(&tb, b.options...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg, entities := tb.Complete()
|
||||
r := *b.message
|
||||
|
||||
r.Message = msg
|
||||
r.Entities = entities
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// MediaAuto creates new message text option builder.
|
||||
func MediaAuto(msg string) *MessageMediaAutoBuilder {
|
||||
return MediaAutoStyled(styling.Plain(msg))
|
||||
}
|
||||
|
||||
// MediaAutoStyled creates new message text option builder.
|
||||
func MediaAutoStyled(texts ...styling.StyledTextOption) *MessageMediaAutoBuilder {
|
||||
return &MessageMediaAutoBuilder{
|
||||
message: &tg.InputBotInlineMessageMediaAuto{},
|
||||
options: texts,
|
||||
}
|
||||
}
|
||||
|
||||
// Markup sets reply markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageMediaAutoBuilder) Markup(m tg.ReplyMarkupClass) *MessageMediaAutoBuilder {
|
||||
b.message.ReplyMarkup = m
|
||||
return b
|
||||
}
|
||||
|
||||
// Row sets single row keyboard markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageMediaAutoBuilder) Row(
|
||||
buttons ...tg.KeyboardButtonClass,
|
||||
) *MessageMediaAutoBuilder {
|
||||
return b.Markup(markup.InlineRow(buttons...))
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/markup"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// MessageGameBuilder is a builder of inline result game message.
|
||||
type MessageGameBuilder struct {
|
||||
message *tg.InputBotInlineMessageGame
|
||||
}
|
||||
|
||||
// nolint:unparam
|
||||
func (b *MessageGameBuilder) apply() (tg.InputBotInlineMessageClass, error) {
|
||||
r := *b.message
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// MessageGame creates new message option builder.
|
||||
func MessageGame() *MessageGameBuilder {
|
||||
return &MessageGameBuilder{
|
||||
message: &tg.InputBotInlineMessageGame{},
|
||||
}
|
||||
}
|
||||
|
||||
// Markup sets reply markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageGameBuilder) Markup(m tg.ReplyMarkupClass) *MessageGameBuilder {
|
||||
b.message.ReplyMarkup = m
|
||||
return b
|
||||
}
|
||||
|
||||
// Row sets single row keyboard markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageGameBuilder) Row(buttons ...tg.KeyboardButtonClass) *MessageGameBuilder {
|
||||
return b.Markup(markup.InlineRow(buttons...))
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/markup"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// MessageMediaGeoBuilder is a builder of inline result geo message.
|
||||
type MessageMediaGeoBuilder struct {
|
||||
message *tg.InputBotInlineMessageMediaGeo
|
||||
}
|
||||
|
||||
// nolint:unparam
|
||||
func (b *MessageMediaGeoBuilder) apply() (tg.InputBotInlineMessageClass, error) {
|
||||
r := *b.message
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// MessageGeo creates new message geo option builder.
|
||||
func MessageGeo(point tg.InputGeoPointClass) *MessageMediaGeoBuilder {
|
||||
return &MessageMediaGeoBuilder{
|
||||
message: &tg.InputBotInlineMessageMediaGeo{
|
||||
GeoPoint: point,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Heading sets for live locations¹, a direction in which the location moves, in degrees; 1-360.
|
||||
//
|
||||
// Links:
|
||||
// 1. https://core.telegram.org/api/live-location
|
||||
func (b *MessageMediaGeoBuilder) Heading(heading int) *MessageMediaGeoBuilder {
|
||||
b.message.Heading = heading
|
||||
return b
|
||||
}
|
||||
|
||||
// Period sets validity period.
|
||||
func (b *MessageMediaGeoBuilder) Period(dur time.Duration) *MessageMediaGeoBuilder {
|
||||
return b.PeriodSeconds(int(dur.Seconds()))
|
||||
}
|
||||
|
||||
// PeriodSeconds sets validity period in seconds.
|
||||
func (b *MessageMediaGeoBuilder) PeriodSeconds(period int) *MessageMediaGeoBuilder {
|
||||
b.message.Period = period
|
||||
return b
|
||||
}
|
||||
|
||||
// ProximityNotificationRadius sets for live locations¹, a maximum distance to another chat member for proximity
|
||||
// alerts, in meters (0-100000)
|
||||
//
|
||||
// Links:
|
||||
// 1. https://core.telegram.org/api/live-location
|
||||
func (b *MessageMediaGeoBuilder) ProximityNotificationRadius(radius int) *MessageMediaGeoBuilder {
|
||||
b.message.ProximityNotificationRadius = radius
|
||||
return b
|
||||
}
|
||||
|
||||
// Markup sets reply markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageMediaGeoBuilder) Markup(m tg.ReplyMarkupClass) *MessageMediaGeoBuilder {
|
||||
b.message.ReplyMarkup = m
|
||||
return b
|
||||
}
|
||||
|
||||
// Row sets single row keyboard markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageMediaGeoBuilder) Row(buttons ...tg.KeyboardButtonClass) *MessageMediaGeoBuilder {
|
||||
return b.Markup(markup.InlineRow(buttons...))
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/entity"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/markup"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/telegram/message/styling"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// MessageTextBuilder is a builder of inline result text message.
|
||||
type MessageTextBuilder struct {
|
||||
message *tg.InputBotInlineMessageText
|
||||
options []styling.StyledTextOption
|
||||
}
|
||||
|
||||
func (b *MessageTextBuilder) apply() (tg.InputBotInlineMessageClass, error) {
|
||||
tb := entity.Builder{}
|
||||
if err := styling.Perform(&tb, b.options...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg, entities := tb.Complete()
|
||||
r := *b.message
|
||||
|
||||
r.Message = msg
|
||||
r.Entities = entities
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// MessageText creates new message text option builder.
|
||||
func MessageText(msg string) *MessageTextBuilder {
|
||||
return MessageStyledText(styling.Plain(msg))
|
||||
}
|
||||
|
||||
// MessageStyledText creates new message text option builder.
|
||||
func MessageStyledText(texts ...styling.StyledTextOption) *MessageTextBuilder {
|
||||
return &MessageTextBuilder{
|
||||
message: &tg.InputBotInlineMessageText{},
|
||||
options: texts,
|
||||
}
|
||||
}
|
||||
|
||||
// NoWebpage sets flag to disable generation of the webpage preview.
|
||||
func (b *MessageTextBuilder) NoWebpage() *MessageTextBuilder {
|
||||
b.message.NoWebpage = true
|
||||
return b
|
||||
}
|
||||
|
||||
// Markup sets reply markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageTextBuilder) Markup(m tg.ReplyMarkupClass) *MessageTextBuilder {
|
||||
b.message.ReplyMarkup = m
|
||||
return b
|
||||
}
|
||||
|
||||
// Row sets single row keyboard markup for sending bot buttons.
|
||||
// NB: markup will not be used, if you send multiple media attachments.
|
||||
func (b *MessageTextBuilder) Row(buttons ...tg.KeyboardButtonClass) *MessageTextBuilder {
|
||||
return b.Markup(markup.InlineRow(buttons...))
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/crypto"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
type resultPageBuilder struct {
|
||||
results []tg.InputBotInlineResultClass
|
||||
random io.Reader
|
||||
}
|
||||
|
||||
func (r *resultPageBuilder) generateID() (string, error) {
|
||||
n, err := crypto.RandInt64(r.random)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strconv.FormatInt(n, 10), nil
|
||||
}
|
||||
|
||||
// ResultOption is an option of inline result.
|
||||
type ResultOption interface {
|
||||
apply(r *resultPageBuilder) error
|
||||
}
|
||||
|
||||
// MessageOption is an option of inline result message.
|
||||
type MessageOption interface {
|
||||
apply() (tg.InputBotInlineMessageClass, error)
|
||||
}
|
||||
|
||||
type messageOptionFunc func() (tg.InputBotInlineMessageClass, error)
|
||||
|
||||
func (m messageOptionFunc) apply() (tg.InputBotInlineMessageClass, error) {
|
||||
return m()
|
||||
}
|
||||
|
||||
// ResultMessage creates new MessageOption from given message object.
|
||||
func ResultMessage(r tg.InputBotInlineMessageClass) MessageOption {
|
||||
return messageOptionFunc(func() (tg.InputBotInlineMessageClass, error) {
|
||||
return r, nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// PhotoResultBuilder is photo result option builder.
|
||||
type PhotoResultBuilder struct {
|
||||
result *tg.InputBotInlineResultPhoto
|
||||
msg MessageOption
|
||||
}
|
||||
|
||||
func (b *PhotoResultBuilder) apply(r *resultPageBuilder) error {
|
||||
m, err := b.msg.apply()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := tg.InputBotInlineResultPhoto{
|
||||
ID: b.result.ID,
|
||||
Type: b.result.Type,
|
||||
Photo: b.result.Photo,
|
||||
}
|
||||
if t.ID == "" {
|
||||
t.ID, err = r.generateID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
t.SendMessage = m
|
||||
r.results = append(r.results, &t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID sets ID of result.
|
||||
// Should not be empty, so if id is not provided, random will be used.
|
||||
func (b *PhotoResultBuilder) ID(id string) *PhotoResultBuilder {
|
||||
b.result.ID = id
|
||||
return b
|
||||
}
|
||||
|
||||
// Photo creates game result option builder.
|
||||
func Photo(photo tg.InputPhotoClass, msg MessageOption) *PhotoResultBuilder {
|
||||
return &PhotoResultBuilder{
|
||||
result: &tg.InputBotInlineResultPhoto{
|
||||
Type: PhotoType,
|
||||
Photo: photo,
|
||||
},
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package inline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/bin"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
func TestPhoto(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
builder, mock := testBuilder(t)
|
||||
photo := &tg.InputPhoto{
|
||||
ID: 10,
|
||||
AccessHash: 10,
|
||||
FileReference: []byte{10},
|
||||
}
|
||||
|
||||
mock.ExpectFunc(func(b bin.Encoder) {
|
||||
v, ok := b.(*tg.MessagesSetInlineBotResultsRequest)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, int64(10), v.QueryID)
|
||||
|
||||
for i := range v.Results {
|
||||
r, ok := v.Results[i].(*tg.InputBotInlineResultPhoto)
|
||||
require.True(t, ok)
|
||||
require.NotZero(t, r.ID)
|
||||
require.Equal(t, photo, r.Photo)
|
||||
}
|
||||
}).ThenTrue()
|
||||
_, err := builder.Set(ctx,
|
||||
Photo(photo, MessageText("photo")),
|
||||
Photo(photo, MessageText("photo")).ID("10"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
mock.Expect().ThenRPCErr(testRPCError())
|
||||
_, err = builder.Set(ctx,
|
||||
Photo(photo, MessageGeo(&tg.InputGeoPoint{
|
||||
Lat: 10,
|
||||
Long: 42,
|
||||
AccuracyRadius: 1337,
|
||||
})),
|
||||
Photo(photo, MessageGame()).ID("10"),
|
||||
)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package inline
|
||||
|
||||
const (
|
||||
// PhotoType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultphoto.
|
||||
PhotoType = "photo"
|
||||
// ArticleType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultarticle.
|
||||
ArticleType = "article"
|
||||
|
||||
// VideoType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultvideo.
|
||||
VideoType = "video"
|
||||
// AudioType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultaudio.
|
||||
AudioType = "audio"
|
||||
// DocumentType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultdocument.
|
||||
DocumentType = "document"
|
||||
// GIFType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultgif.
|
||||
GIFType = "gif"
|
||||
// MPEG4GIFType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif.
|
||||
MPEG4GIFType = "mpeg4_gif"
|
||||
// VoiceType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultvoice.
|
||||
VoiceType = "voice"
|
||||
// StickerType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultsticker.
|
||||
StickerType = "sticker"
|
||||
|
||||
// LocationType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultlocation.
|
||||
LocationType = "location"
|
||||
// VenueType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultvenue.
|
||||
VenueType = "venue"
|
||||
// ContactType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultcontact.
|
||||
ContactType = "contact"
|
||||
// GameType is a type string of inline result.
|
||||
// See https://core.telegram.org/bots/api#inlinequeryresultgame.
|
||||
GameType = "game"
|
||||
)
|
||||
Reference in New Issue
Block a user