Improve support for inline queries

These changes make it possible to set custom options when responding to
inline queries, as described on
https://core.telegram.org/bots/api#answerinlinequery.

It also includes all the (non-cached) inline result types as described
at https://core.telegram.org/bots/api#inlinequeryresult.

Some remarks:
* The internals of sendCommand have changed. It now expects a
  JSON-serializable object. Instead of doing GET requests with
  URL-encoded query parameters it now POSTS JSON directly.
* Because of the above, sendFile() has changed as well. It now expects a
* `map[string]string` which it will internally convert to URL encoded
  form values.
* Respond has been deprecated in favor of the new AnswerInlineQuery
  function. It is only kept for backward compatibility.
* A dependency on https://github.com/mitchellh/hashstructure has been
  introduced in order to generate automatic IDs for inline results.
pull/50/head
Nick Groenen 8 years ago
parent 12182e36dc
commit 46992b037b

@ -16,7 +16,7 @@ import (
func main() {
bot, err := telebot.NewBot("SECRET TOKEN")
if err != nil {
return
log.Fatalln(err)
}
messages := make(chan telebot.Message)
@ -32,10 +32,14 @@ func main() {
```
## Inline mode
As of January 4, 2016, Telegram added inline mode support for bots. Telebot does support inline mode in a fancy manner. Here's a nice way to handle both incoming messages and inline queries:
As of January 4, 2016, Telegram added inline mode support for bots.
Telebot support inline mode in a fancy manner.
Here's a nice way to handle both incoming messages and inline queries:
```go
import (
"log"
"log"
"time"
"github.com/tucnak/telebot"
@ -44,42 +48,56 @@ import (
var bot *telebot.Bot
func main() {
if newBot, err := telebot.NewBot("SECRET TOKEN"); err != nil {
return
} else {
// shadowing, remember?
bot = newBot
}
bot, err := telebot.NewBot("SECRET TOKEN")
if err != nil {
log.Fatalln(err)
}
bot.Messages = make(chan telebot.Message, 1000)
bot.Queries = make(chan telebot.Query, 1000)
bot.Messages = make(chan telebot.Message, 1000)
bot.Queries = make(chan telebot.Query, 1000)
go messages()
go queries()
go messages()
go queries()
bot.Start(1 * time.Second)
}
func messages() {
for message := range bot.Messages {
// ...
}
for message := range bot.Messages {
// ...
}
}
func queries() {
for query := range bot.Queries {
log.Println("--- new query ---")
log.Println("from:", query.From)
log.Println("text:", query.Text)
// There you build a slice of let's say, article results:
results := []telebot.Result{...}
// And finally respond to the query:
if err := bot.Respond(query, results); err != nil {
log.Println("ouch:", err)
}
}
for query := range bot.Queries {
log.Println("--- new query ---")
log.Println("from:", query.From.Username)
log.Println("text:", query.Text)
// Create an article (a link) object to show in our results.
article := &telebot.InlineQueryResultArticle{
Title: "Telegram bot framework written in Go",
URL: "https://github.com/tucnak/telebot",
InputMessageContent: &telebot.InputTextMessageContent{
Text: "Telebot is a convenient wrapper to Telegram Bots API, written in Golang.",
DisablePreview: false,
},
}
// Build the list of results. In this instance, just our 1 article from above.
results := []telebot.InlineQueryResult{article}
// Build a response object to answer the query.
response := telebot.QueryResponse{
Results: results,
IsPersonal: true,
}
// And finally send the response.
if err := bot.AnswerInlineQuery(&query, &response); err != nil {
log.Println("Failed to respond to query:", err)
}
}
}
```

@ -8,17 +8,20 @@ import (
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
)
func sendCommand(method, token string, params url.Values) ([]byte, error) {
url := fmt.Sprintf("https://api.telegram.org/bot%s/%s?%s",
token, method, params.Encode())
func sendCommand(method, token string, payload interface{}) ([]byte, error) {
url := fmt.Sprintf("https://api.telegram.org/bot%s/%s", token, method)
var b bytes.Buffer
if err := json.NewEncoder(&b).Encode(payload); err != nil {
return []byte{}, err
}
resp, err := http.Get(url)
resp, err := http.Post(url, "application/json", &b)
if err != nil {
return []byte{}, err
}
@ -32,7 +35,7 @@ func sendCommand(method, token string, params url.Values) ([]byte, error) {
return json, nil
}
func sendFile(method, token, name, path string, params url.Values) ([]byte, error) {
func sendFile(method, token, name, path string, params map[string]string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return []byte{}, err
@ -50,10 +53,8 @@ func sendFile(method, token, name, path string, params url.Values) ([]byte, erro
return []byte{}, err
}
for field, values := range params {
if len(values) > 0 {
writer.WriteField(field, values[0])
}
for field, value := range params {
writer.WriteField(field, value)
}
if err = writer.Close(); err != nil {
@ -86,25 +87,25 @@ func sendFile(method, token, name, path string, params url.Values) ([]byte, erro
return json, nil
}
func embedSendOptions(params *url.Values, options *SendOptions) {
if params == nil || options == nil {
func embedSendOptions(params map[string]string, options *SendOptions) {
if options == nil {
return
}
if options.ReplyTo.ID != 0 {
params.Set("reply_to_message_id", strconv.Itoa(options.ReplyTo.ID))
params["reply_to_message_id"] = strconv.Itoa(options.ReplyTo.ID)
}
if options.DisableWebPagePreview {
params.Set("disable_web_page_preview", "true")
params["disable_web_page_preview"] = "true"
}
if options.DisableNotification {
params.Set("disable_notification", "true")
params["disable_notification"] = "true"
}
if options.ParseMode != ModeDefault {
params.Set("parse_mode", string(options.ParseMode))
params["parse_mode"] = string(options.ParseMode)
}
// Processing force_reply:
@ -115,13 +116,13 @@ func embedSendOptions(params *url.Values, options *SendOptions) {
hiddenKeyboard := options.ReplyMarkup.HideCustomKeyboard
if forceReply || customKeyboard || hiddenKeyboard || inlineKeyboard {
replyMarkup, _ := json.Marshal(options.ReplyMarkup)
params.Set("reply_markup", string(replyMarkup))
params["reply_markup"] = string(replyMarkup)
}
}
}
func getMe(token string) (User, error) {
meJSON, err := sendCommand("getMe", token, url.Values{})
meJSON, err := sendCommand("getMe", token, nil)
if err != nil {
return User{}, err
}
@ -145,9 +146,10 @@ func getMe(token string) (User, error) {
}
func getUpdates(token string, offset, timeout int) (upd []Update, err error) {
params := url.Values{}
params.Set("offset", strconv.Itoa(offset))
params.Set("timeout", strconv.Itoa(timeout))
params := map[string]string{
"offset": strconv.Itoa(offset),
"timeout": strconv.Itoa(timeout),
}
updatesJSON, err := sendCommand("getUpdates", token, params)
if err != nil {
return

120
bot.go

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"log"
"net/url"
"strconv"
"time"
)
@ -92,12 +91,13 @@ func (b *Bot) poll(
// SendMessage sends a text message to recipient.
func (b *Bot) SendMessage(recipient Recipient, message string, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params.Set("text", message)
params := map[string]string{
"chat_id": recipient.Destination(),
"text": message,
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
responseJSON, err := sendCommand("sendMessage", b.Token, params)
@ -124,10 +124,11 @@ func (b *Bot) SendMessage(recipient Recipient, message string, options *SendOpti
// ForwardMessage forwards a message to recipient.
func (b *Bot) ForwardMessage(recipient Recipient, message Message) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params.Set("from_chat_id", strconv.Itoa(message.Origin().ID))
params.Set("message_id", strconv.Itoa(message.ID))
params := map[string]string{
"chat_id": recipient.Destination(),
"from_chat_id": strconv.Itoa(message.Origin().ID),
"message_id": strconv.Itoa(message.ID),
}
responseJSON, err := sendCommand("forwardMessage", b.Token, params)
if err != nil {
@ -158,19 +159,20 @@ func (b *Bot) ForwardMessage(recipient Recipient, message Message) error {
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendPhoto(recipient Recipient, photo *Photo, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params.Set("caption", photo.Caption)
params := map[string]string{
"chat_id": recipient.Destination(),
"caption": photo.Caption,
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
var responseJSON []byte
var err error
if photo.Exists() {
params.Set("photo", photo.FileID)
params["photo"] = photo.FileID
responseJSON, err = sendCommand("sendPhoto", b.Token, params)
} else {
responseJSON, err = sendFile("sendPhoto", b.Token, "photo",
@ -211,18 +213,19 @@ func (b *Bot) SendPhoto(recipient Recipient, photo *Photo, options *SendOptions)
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendAudio(recipient Recipient, audio *Audio, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params := map[string]string{
"chat_id": recipient.Destination(),
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
var responseJSON []byte
var err error
if audio.Exists() {
params.Set("audio", audio.FileID)
params["audio"] = audio.FileID
responseJSON, err = sendCommand("sendAudio", b.Token, params)
} else {
responseJSON, err = sendFile("sendAudio", b.Token, "audio",
@ -262,18 +265,19 @@ func (b *Bot) SendAudio(recipient Recipient, audio *Audio, options *SendOptions)
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendDocument(recipient Recipient, doc *Document, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params := map[string]string{
"chat_id": recipient.Destination(),
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
var responseJSON []byte
var err error
if doc.Exists() {
params.Set("document", doc.FileID)
params["document"] = doc.FileID
responseJSON, err = sendCommand("sendDocument", b.Token, params)
} else {
responseJSON, err = sendFile("sendDocument", b.Token, "document",
@ -313,18 +317,19 @@ func (b *Bot) SendDocument(recipient Recipient, doc *Document, options *SendOpti
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendSticker(recipient Recipient, sticker *Sticker, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params := map[string]string{
"chat_id": recipient.Destination(),
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
var responseJSON []byte
var err error
if sticker.Exists() {
params.Set("sticker", sticker.FileID)
params["sticker"] = sticker.FileID
responseJSON, err = sendCommand("sendSticker", b.Token, params)
} else {
responseJSON, err = sendFile("sendSticker", b.Token, "sticker",
@ -364,18 +369,19 @@ func (b *Bot) SendSticker(recipient Recipient, sticker *Sticker, options *SendOp
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendVideo(recipient Recipient, video *Video, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params := map[string]string{
"chat_id": recipient.Destination(),
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
var responseJSON []byte
var err error
if video.Exists() {
params.Set("video", video.FileID)
params["video"] = video.FileID
responseJSON, err = sendCommand("sendVideo", b.Token, params)
} else {
responseJSON, err = sendFile("sendVideo", b.Token, "video",
@ -415,13 +421,14 @@ func (b *Bot) SendVideo(recipient Recipient, video *Video, options *SendOptions)
// again, won't issue a new upload, but would make a use
// of existing file on Telegram servers.
func (b *Bot) SendLocation(recipient Recipient, geo *Location, options *SendOptions) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params.Set("latitude", fmt.Sprintf("%f", geo.Latitude))
params.Set("longitude", fmt.Sprintf("%f", geo.Longitude))
params := map[string]string{
"chat_id": recipient.Destination(),
"latitude": fmt.Sprintf("%f", geo.Latitude),
"longitude": fmt.Sprintf("%f", geo.Longitude),
}
if options != nil {
embedSendOptions(&params, options)
embedSendOptions(params, options)
}
responseJSON, err := sendCommand("sendLocation", b.Token, params)
@ -457,9 +464,10 @@ func (b *Bot) SendLocation(recipient Recipient, geo *Location, options *SendOpti
// Currently, Telegram supports only a narrow range of possible
// actions, these are aligned as constants of this package.
func (b *Bot) SendChatAction(recipient Recipient, action string) error {
params := url.Values{}
params.Set("chat_id", recipient.Destination())
params.Set("action", action)
params := map[string]string{
"chat_id": recipient.Destination(),
"action": action,
}
responseJSON, err := sendCommand("sendChatAction", b.Token, params)
if err != nil {
@ -484,12 +492,14 @@ func (b *Bot) SendChatAction(recipient Recipient, action string) error {
}
// Respond publishes a set of responses for an inline query.
// This function is deprecated in favor of AnswerInlineQuery.
func (b *Bot) Respond(query Query, results []Result) error {
params := url.Values{}
params.Set("inline_query_id", query.ID)
params := map[string]string{
"inline_query_id": query.ID,
}
if res, err := json.Marshal(results); err == nil {
params.Set("results", string(res))
params["results"] = string(res)
} else {
return err
}
@ -515,3 +525,31 @@ func (b *Bot) Respond(query Query, results []Result) error {
return nil
}
// AnswerInlineQuery sends a response for a given inline query. A query can
// only be responded to once, subsequent attempts to respond to the same query
// will result in an error.
func (b *Bot) AnswerInlineQuery(query *Query, response *QueryResponse) error {
response.QueryID = query.ID
responseJSON, err := sendCommand("answerInlineQuery", b.Token, response)
if err != nil {
return err
}
var responseRecieved struct {
Ok bool
Description string
}
err = json.Unmarshal(responseJSON, &responseRecieved)
if err != nil {
return err
}
if !responseRecieved.Ok {
return fmt.Errorf("telebot: %s", responseRecieved.Description)
}
return nil
}

@ -1,15 +1,89 @@
package telebot
import (
"github.com/mitchellh/hashstructure"
"hash/fnv"
"strconv"
)
// inlineQueryHashOptions sets the HashOptions to be used when hashing
// an inline query result (used to generate IDs).
var inlineQueryHashOptions = &hashstructure.HashOptions{
Hasher: fnv.New64(),
}
// Query is an incoming inline query. When the user sends
// an empty query, your bot could return some default or
// trending results.
type Query struct {
ID string `json:"id"`
From User `json:"from"`
// Unique identifier for this query.
ID string `json:"id"`
// Sender.
From User `json:"from"`
// (Optional) Sender location, only for bots that request user location.
Location Location `json:"location"`
// Text of the query (up to 512 characters).
Text string `json:"query"`
// Offset of the results to be returned, can be controlled by the bot.
Offset string `json:"offset"`
}
// QueryResponse builds a response to an inline Query.
// See also: https://core.telegram.org/bots/api#answerinlinequery
type QueryResponse struct {
// The ID of the query to which this is a response.
// It is not necessary to specify this field manually.
QueryID string `json:"inline_query_id"`
// The results for the inline query.
Results []InlineQueryResult `json:"results"`
// (Optional) The maximum amount of time in seconds that the result
// of the inline query may be cached on the server.
CacheTime int `json:"cache_time,omitempty"`
// (Optional) Pass True, 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.
IsPersonal bool `json:"is_personal"`
// (Optional) 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 dont support
// pagination. Offset length cant exceed 64 bytes.
NextOffset string `json:"next_offset"`
// (Optional) 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 the parameter switch_pm_parameter.
SwitchPMText string `json:"switch_pm_text,omitempty"`
// (Optional) Parameter for the start message sent to the bot when user
// presses the switch button.
SwitchPMParameter string `json:"switch_pm_parameter,omitempty"`
}
// InlineQueryResult represents one result of an inline query.
type InlineQueryResult interface {
MarshalJSON() ([]byte, error)
id() (string, error)
}
// hashInlineQueryResult calculates the 64-bit FNV-1 hash of an
// inline query result.
func hashInlineQueryResult(result InlineQueryResult) (string, error) {
hash, err := hashstructure.Hash(result, inlineQueryHashOptions)
if err != nil {
return "", err
}
return strconv.FormatUint(hash, 16), nil
}
// This object represents one result of an inline query.
// Deprecated interface type, superseded by InlineQueryResult.
type Result interface {
MarshalJSON() ([]byte, error)
}

@ -8,6 +8,7 @@ import (
)
// ArticleResult represents a link to an article or web page.
// Deprecated, use InlineQueryResultArticle instead.
type ArticleResult struct {
// [Required!] Title of the result.
Title string

@ -0,0 +1,690 @@
package telebot
import (
"encoding/json"
)
// InlineQueryResultArticle represents a link to an article or web page.
// See also: https://core.telegram.org/bots/api#inlinequeryresultarticle
type InlineQueryResultArticle struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// Title of the result.
Title string `json:"title"`
// Message text. Shortcut (and mutually exclusive to) specifying
// InputMessageContent.
Text string `json:"message_text,omitempty"`
// Content of the message to be sent.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. URL of the result.
URL string `json:"url,omitempty"`
// Optional. Pass True, if you don't want the URL to be shown in the message.
HideURL bool `json:"hide_url,omitempty"`
// Optional. Short description of the result.
Description string `json:"description,omitempty"`
// Optional. Url of the thumbnail for the result.
ThumbURL string `json:"thumb_url,omitempty"`
// Optional. Thumbnail width.
ThumbWidth int `json:"thumb_width,omitempty"`
// Optional. Thumbnail height.
ThumbHeight int `json:"thumb_height,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultArticle InlineQueryResultArticle
func (r *InlineQueryResultArticle) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultArticle
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultArticle: wrappedInlineQueryResultArticle(*r),
ID: id,
Type: "article",
})
}
func (r *InlineQueryResultArticle) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultAudio represents a link to an mp3 audio file.
type InlineQueryResultAudio struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// A valid URL for the audio file.
AudioURL string `json:"audio_url"`
// Title.
Title string `json:"title"`
// Optional. Performer.
Performer string `json:"performer,omitempty"`
// Optional. Audio duration in seconds.
Duration int `json:"audio_duration,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultAudio InlineQueryResultAudio
func (r *InlineQueryResultAudio) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultAudio
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultAudio: wrappedInlineQueryResultAudio(*r),
ID: id,
Type: "audio",
})
}
func (r *InlineQueryResultAudio) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultContact represents a contact with a phone number.
// See also: https://core.telegram.org/bots/api#inlinequeryresultcontact
type InlineQueryResultContact struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// Contact's phone number.
PhoneNumber string `json:"phone_number"`
// Contact's first name.
FirstName string `json:"first_name"`
// Optional. Contact's last name.
LastName string `json:"last_name,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
// Optional. Url of the thumbnail for the result.
ThumbURL string `json:"thumb_url,omitempty"`
// Optional. Thumbnail width.
ThumbWidth int `json:"thumb_width,omitempty"`
// Optional. Thumbnail height.
ThumbHeight int `json:"thumb_height,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultContact InlineQueryResultContact
func (r *InlineQueryResultContact) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultContact
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultContact: wrappedInlineQueryResultContact(*r),
ID: id,
Type: "contact",
})
}
func (r *InlineQueryResultContact) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultDocument represents a link to a file.
// See also: https://core.telegram.org/bots/api#inlinequeryresultdocument
type InlineQueryResultDocument struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// Title for the result.
Title string `json:"title"`
// A valid URL for the file
DocumentURL string `json:"document_url"`
// Mime type of the content of the file, either “application/pdf” or
// “application/zip”.
MimeType string `json:"mime_type"`
// Optional. Caption of the document to be sent, 0-200 characters.
Caption string `json:"caption,omitempty"`
// Optional. Short description of the result.
Description string `json:"description,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
// Optional. URL of the thumbnail (jpeg only) for the file.
ThumbURL string `json:"thumb_url,omitempty"`
// Optional. Thumbnail width.
ThumbWidth int `json:"thumb_width,omitempty"`
// Optional. Thumbnail height.
ThumbHeight int `json:"thumb_height,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultDocument InlineQueryResultDocument
func (r *InlineQueryResultDocument) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultDocument
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultDocument: wrappedInlineQueryResultDocument(*r),
ID: id,
Type: "document",
})
}
func (r *InlineQueryResultDocument) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultGif represents a link to an animated GIF file.
// See also: https://core.telegram.org/bots/api#inlinequeryresultgif
type InlineQueryResultGif struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// A valid URL for the GIF file. File size must not exceed 1MB.
GifURL string `json:"gif_url"`
// URL of the static thumbnail for the result (jpeg or gif).
ThumbURL string `json:"thumb_url"`
// Optional. Width of the GIF.
GifWidth int `json:"gif_width,omitempty"`
// Optional. Height of the GIF.
GifHeight int `json:"gif_height,omitempty"`
// Optional. Title for the result.
Title string `json:"title,omitempty"`
// Optional. Caption of the GIF file to be sent, 0-200 characters.
Caption string `json:"caption,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultGif InlineQueryResultGif
func (r *InlineQueryResultGif) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultGif
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultGif: wrappedInlineQueryResultGif(*r),
ID: id,
Type: "gif",
})
}
func (r *InlineQueryResultGif) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultLocation represents a location on a map.
// See also: https://core.telegram.org/bots/api#inlinequeryresultlocation
type InlineQueryResultLocation struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// Latitude of the location in degrees.
Latitude float32 `json:"latitude"`
// Longitude of the location in degrees.
Longitude float32 `json:"longitude"`
// Location title.
Title string `json:"title"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
// Optional. Url of the thumbnail for the result.
ThumbURL string `json:"thumb_url,omitempty"`
// Optional. Thumbnail width.
ThumbWidth int `json:"thumb_width,omitempty"`
// Optional. Thumbnail height.
ThumbHeight int `json:"thumb_height,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultLocation InlineQueryResultLocation
func (r *InlineQueryResultLocation) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultLocation
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultLocation: wrappedInlineQueryResultLocation(*r),
ID: id,
Type: "location",
})
}
func (r *InlineQueryResultLocation) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultMpeg4Gif represents a link to a video animation
// (H.264/MPEG-4 AVC video without sound).
// See also: https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif
type InlineQueryResultMpeg4Gif struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// A valid URL for the MP4 file.
URL string `json:"mpeg4_url"`
// Optional. Video width.
Width int `json:"mpeg4_width,omitempty"`
// Optional. Video height.
Height int `json:"mpeg4_height,omitempty"`
// URL of the static thumbnail (jpeg or gif) for the result.
ThumbURL string `json:"thumb_url,omitempty"`
// Optional. Title for the result.
Title string `json:"title,omitempty"`
// Optional. Caption of the MPEG-4 file to be sent, 0-200 characters.
Caption string `json:"caption,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultMpeg4Gif InlineQueryResultMpeg4Gif
func (r *InlineQueryResultMpeg4Gif) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultMpeg4Gif
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultMpeg4Gif: wrappedInlineQueryResultMpeg4Gif(*r),
ID: id,
Type: "mpeg4_gif",
})
}
func (r *InlineQueryResultMpeg4Gif) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultPhoto represents a link to a photo.
// See also: https://core.telegram.org/bots/api#inlinequeryresultphoto
type InlineQueryResultPhoto struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// A valid URL of the photo. Photo must be in jpeg format.
// Photo size must not exceed 5MB.
PhotoURL string `json:"photo_url"`
// URL of the thumbnail for the photo.
ThumbURL string `json:"thumb_url"`
// Optional. Width of the photo.
PhotoWidth int `json:"photo_width,omitempty"`
// Optional. Height of the photo.
PhotoHeight int `json:"photo_height,omitempty"`
// Optional. Title for the result.
Title string `json:"title,omitempty"`
// Optional. Short description of the result.
Description string `json:"description,omitempty"`
// Optional. Caption of the photo to be sent, 0-200 characters.
Caption string `json:"caption,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultPhoto InlineQueryResultPhoto
func (r *InlineQueryResultPhoto) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultPhoto
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultPhoto: wrappedInlineQueryResultPhoto(*r),
ID: id,
Type: "photo",
})
}
func (r *InlineQueryResultPhoto) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultVenue represents a venue.
// See also: https://core.telegram.org/bots/api#inlinequeryresultvenue
type InlineQueryResultVenue struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// Latitude of the venue location in degrees.
Latitude float32 `json:"latitude"`
// Longitude of the venue location in degrees.
Longitude float32 `json:"longitude"`
// Title of the venue.
Title string `json:"title"`
// Address of the venue.
Address string `json:"address"`
// Optional. Foursquare identifier of the venue if known.
FoursquareID string `json:"foursquare_id,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
// Optional. Url of the thumbnail for the result.
ThumbURL string `json:"thumb_url,omitempty"`
// Optional. Thumbnail width.
ThumbWidth int `json:"thumb_width,omitempty"`
// Optional. Thumbnail height.
ThumbHeight int `json:"thumb_height,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultVenue InlineQueryResultVenue
func (r *InlineQueryResultVenue) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultVenue
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultVenue: wrappedInlineQueryResultVenue(*r),
ID: id,
Type: "venue",
})
}
func (r *InlineQueryResultVenue) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultVideo represents a link to a page containing an embedded
// video player or a video file.
// See also: https://core.telegram.org/bots/api#inlinequeryresultvideo
type InlineQueryResultVideo struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// A valid URL for the embedded video player or video file.
VideoURL string `json:"video_url"`
// Mime type of the content of video url, “text/html” or “video/mp4”.
MimeType string `json:"mime_type"`
// URL of the thumbnail (jpeg only) for the video.
ThumbURL string `json:"thumb_url"`
// Title for the result.
Title string `json:"title"`
// Optional. Caption of the video to be sent, 0-200 characters.
Caption string `json:"caption,omitempty"`
// Optional. Video width.
VideoWidth int `json:"video_width,omitempty"`
// Optional. Video height.
VideoHeight int `json:"video_height,omitempty"`
// Optional. Video duration in seconds.
VideoDuration int `json:"video_duration,omitempty"`
// Optional. Short description of the result.
Description string `json:"description,omitempty"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultVideo InlineQueryResultVideo
func (r *InlineQueryResultVideo) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultVideo
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultVideo: wrappedInlineQueryResultVideo(*r),
ID: id,
Type: "video",
})
}
func (r *InlineQueryResultVideo) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}
// InlineQueryResultVoice represents a link to a voice recording in a
// .ogg container encoded with OPUS.
// See also: https://core.telegram.org/bots/api#inlinequeryresultvoice
type InlineQueryResultVoice struct {
// Unique identifier for this result, 1-64 Bytes.
// If left unspecified, a 64-bit FNV-1 hash will be calculated
// from the other fields and used automatically.
ID string `json:"id",hash:"ignore"`
// A valid URL for the voice recording.
VoiceURL string `json:"voice_url"`
// Recording title.
Title string `json:"title"`
// Optional. Recording duration in seconds.
VoiceDuration int `json:"voice_duration"`
// Optional. Inline keyboard attached to the message.
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// Optional. Content of the message to be sent instead of the audio.
InputMessageContent InputMessageContent `json:"input_message_content,omitempty"`
}
// Used to avoid endless recursion in MarshalJSON.
type wrappedInlineQueryResultVoice InlineQueryResultVoice
func (r *InlineQueryResultVoice) MarshalJSON() ([]byte, error) {
id, err := r.id()
if err != nil {
return nil, err
}
return json.Marshal(struct {
wrappedInlineQueryResultVoice
Type string `json:"type"`
ID string `json:"id",hash:"ignore"`
}{
wrappedInlineQueryResultVoice: wrappedInlineQueryResultVoice(*r),
ID: id,
Type: "voice",
})
}
func (r *InlineQueryResultVoice) id() (string, error) {
if r.ID == "" {
return hashInlineQueryResult(r)
} else {
return r.ID, nil
}
}

@ -0,0 +1,84 @@
package telebot
// InputMessageContent objects represent the content of a message to be sent
// as a result of an inline query.
// See also: https://core.telegram.org/bots/api#inputmessagecontent
type InputMessageContent interface {
IsInputMessageContent() bool
}
// InputTextMessageContent represents the content of a text message to be
// sent as the result of an inline query.
// See also: https://core.telegram.org/bots/api#inputtextmessagecontent
type InputTextMessageContent struct {
// Text of the message to be sent, 1-4096 characters.
Text string `json:"message_text"`
// Optional. Send Markdown or HTML, if you want Telegram apps to show
// bold, italic, fixed-width text or inline URLs in your bot's message.
ParseMode string `json:"parse_mode,omitempty"`
// Optional. Disables link previews for links in the sent message.
DisablePreview bool `json:"disable_web_page_preview`
}
func (input *InputTextMessageContent) IsInputMessageContent() bool {
return true
}
// InputLocationMessageContent represents the content of a location message
// to be sent as the result of an inline query.
// See also: https://core.telegram.org/bots/api#inputlocationmessagecontent
type InputLocationMessageContent struct {
// Latitude of the location in degrees.
Latitude float32 `json:"latitude"`
// Longitude of the location in degrees.
Longitude float32 `json:"longitude"`
}
func (input *InputLocationMessageContent) IsInputMessageContent() bool {
return true
}
// InputVenueMessageContent represents the content of a venue message to
// be sent as the result of an inline query.
// See also: https://core.telegram.org/bots/api#inputvenuemessagecontent
type InputVenueMessageContent struct {
// Latitude of the location in degrees.
Latitude float32 `json:"latitude"`
// Longitude of the location in degrees.
Longitude float32 `json:"longitude"`
// Name of the venue.
Title string `json:"title"`
// Address of the venue.
Address string `json:"address"`
// Optional. Foursquare identifier of the venue, if known.
FoursquareID string `json:"foursquare_id,omitempty"`
}
func (input *InputVenueMessageContent) IsInputMessageContent() bool {
return true
}
// InputContactMessageContent represents the content of a contact
// message to be sent as the result of an inline query.
// See also: https://core.telegram.org/bots/api#inputcontactmessagecontent
type InputContactMessageContent struct {
// Contact's phone number.
PhoneNumber string `json:"phone_number"`
// Contact's first name.
FirstName string `json:"first_name"`
// Optional. Contact's last name.
LastName string `json:"last_name,omitempty"`
}
func (input *InputContactMessageContent) IsInputMessageContent() bool {
return true
}

@ -138,6 +138,13 @@ type KeyboardButton struct {
InlineQuery string `json:"switch_inline_query,omitempty"`
}
// InlineKeyboardMarkup represents an inline keyboard that appears right next
// to the message it belongs to.
type InlineKeyboardMarkup struct {
// Array of button rows, each represented by an Array of KeyboardButton objects.
InlineKeyboard [][]KeyboardButton `json:"inline_keyboard,omitempty"`
}
// Contact object represents a contact to Telegram user
type Contact struct {
UserID int `json:"user_id"`

Loading…
Cancel
Save