Major documentation and code cleanup.

All documention is now less than 80 characters wide. Old methods now
show deprecated warnings. The Values/Params/Method functions are now
private. Types and configs have required and optional comments on
them. Simplified some function logic.
bot-api-6.1
Syfaro 2016-01-03 16:54:24 -06:00
parent 1ae7803be0
commit e8dfdeeeb9
5 changed files with 414 additions and 306 deletions

View File

@ -4,17 +4,22 @@
[![Travis](https://travis-ci.org/Syfaro/telegram-bot-api.svg)](https://travis-ci.org/Syfaro/telegram-bot-api)
All methods have been added, and all features should be available.
If you want a feature that hasn't been added yet or something is broken, open an issue and I'll see what I can do.
If you want a feature that hasn't been added yet or something is broken,
open an issue and I'll see what I can do.
All methods are fairly self explanatory, and reading the godoc page should explain everything. If something isn't clear, open an issue or submit a pull request.
All methods are fairly self explanatory, and reading the godoc page should
explain everything. If something isn't clear, open an issue or submit
a pull request.
The scope of this project is just to provide a wrapper around the API without any additional features. There are other projects for creating something with plugins and command handlers without having to design all that yourself.
Note to previous users, there was just a large change that broke some methods. The main changes are that all the `Send*` functions have been replaced with a single `Send`, and `UpdatesChan` was renamed `GetUpdatesChan` and returns `(chan, err)` instead of storing the chan in `Updates`.
The scope of this project is just to provide a wrapper around the API
without any additional features. There are other projects for creating
something with plugins and command handlers without having to design
all that yourself.
## Example
This is a very simple bot that just displays any gotten updates, then replies it to that chat.
This is a very simple bot that just displays any gotten updates,
then replies it to that chat.
```go
package main
@ -50,7 +55,8 @@ func main() {
}
```
If you need to use webhooks for some reason (such as running on Google App Engine), you may use a slightly different method.
If you need to use webhooks (if you wish to run on Google App Engine),
you may use a slightly different method.
```go
package main
@ -85,8 +91,12 @@ func main() {
}
```
If you need, you may generate a self signed certficate, as this requires HTTPS / TLS. The above example tells Telegram that this is your certificate and that it should be trusted, even though it is not properly signed.
If you need, you may generate a self signed certficate, as this requires
HTTPS / TLS. The above example tells Telegram that this is your
certificate and that it should be trusted, even though it is not
properly signed.
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes
Now that [Let's Encrypt](https://letsencrypt.org) has entered public beta, you may wish to generate your free TLS certificate there.
Now that [Let's Encrypt](https://letsencrypt.org) has entered public beta,
you may wish to generate your free TLS certificate there.

99
bot.go
View File

@ -1,4 +1,5 @@
// Package tgbotapi has bindings for interacting with the Telegram Bot API.
// Package tgbotapi has functions and types used for interacting with
// the Telegram Bot API.
package tgbotapi
import (
@ -17,7 +18,7 @@ import (
"time"
)
// BotAPI has methods for interacting with all of Telegram's Bot API endpoints.
// BotAPI allows you to interact with the Telegram Bot API.
type BotAPI struct {
Token string `json:"token"`
Debug bool `json:"debug"`
@ -26,13 +27,16 @@ type BotAPI struct {
}
// NewBotAPI creates a new BotAPI instance.
// Requires a token, provided by @BotFather on Telegram
//
// It requires a token, provided by @BotFather on Telegram.
func NewBotAPI(token string) (*BotAPI, error) {
return NewBotAPIWithClient(token, &http.Client{})
}
// NewBotAPIWithClient creates a new BotAPI instance passing an http.Client.
// Requires a token, provided by @BotFather on Telegram
// NewBotAPIWithClient creates a new BotAPI instance
// and allows you to pass a http.Client.
//
// It requires a token, provided by @BotFather on Telegram.
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
bot := &BotAPI{
Token: token,
@ -50,9 +54,10 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
}
// MakeRequest makes a request to a specific endpoint with our token.
// All requests are POSTs because Telegram doesn't care, and it's easier.
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
resp, err := bot.Client.PostForm(fmt.Sprintf(APIEndpoint, bot.Token, endpoint), params)
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
resp, err := bot.Client.PostForm(method, params)
if err != nil {
return APIResponse{}, err
}
@ -81,6 +86,7 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
return apiResp, nil
}
// makeMessageRequest makes a request to a method that returns a Message.
func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) {
resp, err := bot.MakeRequest(endpoint, params)
if err != nil {
@ -98,7 +104,11 @@ func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Messa
// UploadFile makes a request to the API with a file.
//
// Requires the parameter to hold the file not be in the params.
// File should be a string to a file path, a FileBytes struct, or a FileReader struct.
// File should be a string to a file path, a FileBytes struct,
// or a FileReader struct.
//
// Note that if your FileReader has a size set to -1, it will read
// the file into memory to calculate a size.
func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
ms := multipartstreamer.New()
ms.WriteFields(params)
@ -139,7 +149,9 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna
return APIResponse{}, errors.New("bad file type")
}
req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil)
method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
req, err := http.NewRequest("POST", method, nil)
if err != nil {
return APIResponse{}, err
}
@ -173,7 +185,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna
// GetFileDirectURL returns direct URL to file
//
// Requires fileID
// It requires the FileID.
func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
file, err := bot.GetFile(FileConfig{fileID})
@ -186,7 +198,9 @@ func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
// GetMe fetches the currently authenticated bot.
//
// There are no parameters for this method.
// This method is called upon creation to validate the token,
// and so you may get this data from BotAPI.Self without the need for
// another request.
func (bot *BotAPI) GetMe() (User, error) {
resp, err := bot.MakeRequest("getMe", nil)
if err != nil {
@ -201,16 +215,16 @@ func (bot *BotAPI) GetMe() (User, error) {
return user, nil
}
// IsMessageToMe returns true if message directed to this bot
// IsMessageToMe returns true if message directed to this bot.
//
// Requires message
// It requires the Message.
func (bot *BotAPI) IsMessageToMe(message Message) bool {
return strings.Contains(message.Text, "@"+bot.Self.UserName)
}
// Send will send event(Message, Photo, Audio, ChatAction, anything) to Telegram
// Send will send a Chattable item to Telegram.
//
// Requires Chattable
// It requires the Chattable to send.
func (bot *BotAPI) Send(c Chattable) (Message, error) {
switch c.(type) {
case Fileable:
@ -220,6 +234,9 @@ func (bot *BotAPI) Send(c Chattable) (Message, error) {
}
}
// debugLog checks if the bot is currently running in debug mode, and if
// so will display information about the request and response in the
// debug log.
func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
if bot.Debug {
log.Printf("%s req : %+v\n", context, v)
@ -227,8 +244,9 @@ func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
}
}
// sendExisting will send a Message with an existing file to Telegram.
func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
v, err := config.Values()
v, err := config.values()
if err != nil {
return Message{}, err
@ -242,15 +260,16 @@ func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error)
return message, nil
}
// uploadAndSend will send a Message with a new file to Telegram.
func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
params, err := config.Params()
params, err := config.params()
if err != nil {
return Message{}, err
}
file := config.GetFile()
file := config.getFile()
resp, err := bot.UploadFile(method, params, config.Name(), file)
resp, err := bot.UploadFile(method, params, config.name(), file)
if err != nil {
return Message{}, err
}
@ -258,28 +277,29 @@ func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error
var message Message
json.Unmarshal(resp.Result, &message)
if bot.Debug {
log.Printf("%s resp: %+v\n", method, message)
}
bot.debugLog(method, nil, message)
return message, nil
}
// sendFile determines if the file is using an existing file or uploading
// a new file, then sends it as needed.
func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
if config.UseExistingFile() {
return bot.sendExisting(config.Method(), config)
if config.useExistingFile() {
return bot.sendExisting(config.method(), config)
}
return bot.uploadAndSend(config.Method(), config)
return bot.uploadAndSend(config.method(), config)
}
// sendChattable sends a Chattable.
func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
v, err := config.Values()
v, err := config.values()
if err != nil {
return Message{}, err
}
message, err := bot.makeMessageRequest(config.Method(), v)
message, err := bot.makeMessageRequest(config.method(), v)
if err != nil {
return Message{}, err
@ -290,7 +310,7 @@ func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
// GetUserProfilePhotos gets a user's profile photos.
//
// Requires UserID.
// It requires UserID.
// Offset and Limit are optional.
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
v := url.Values{}
@ -315,7 +335,7 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro
return profilePhotos, nil
}
// GetFile returns a file_id required to download a file.
// GetFile returns a File which can download a file from Telegram.
//
// Requires FileID.
func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
@ -339,8 +359,9 @@ func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
// If a WebHook is set, this will not return any data!
//
// Offset, Limit, and Timeout are optional.
// To not get old items, set Offset to one higher than the previous item.
// Set Timeout to a large number to reduce requests and get responses instantly.
// To avoid stale items, set Offset to one higher than the previous item.
// Set Timeout to a large number to reduce requests so you can get updates
// instantly instead of having to wait between requests.
func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
v := url.Values{}
if config.Offset > 0 {
@ -361,24 +382,22 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
var updates []Update
json.Unmarshal(resp.Result, &updates)
if bot.Debug {
log.Printf("getUpdates: %+v\n", updates)
}
bot.debugLog("getUpdates", v, updates)
return updates, nil
}
// RemoveWebhook removes webhook
//
// There are no parameters for this method.
// RemoveWebhook unsets the webhook.
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
return bot.MakeRequest("setWebhook", url.Values{})
}
// SetWebhook sets a webhook.
//
// If this is set, GetUpdates will not get any data!
//
// Requires URL OR to set Clear to true.
// If you do not have a legitmate TLS certificate, you need to include
// your self signed certificate with the config.
func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
if config.Certificate == nil {
v := url.Values{}
@ -406,8 +425,6 @@ func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
}
// GetUpdatesChan starts and returns a channel for getting updates.
//
// Requires UpdateConfig
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (<-chan Update, error) {
updatesChan := make(chan Update, 100)
@ -453,6 +470,8 @@ func (bot *BotAPI) ListenForWebhook(pattern string) (<-chan Update, http.Handler
}
// AnswerInlineQuery sends a response to an inline query.
//
// Note that you must respond to an inline query within 30 seconds.
func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) {
v := url.Values{}

View File

@ -3,15 +3,17 @@ package tgbotapi
import (
"encoding/json"
"io"
"log"
"net/url"
"strconv"
)
// Telegram constants
const (
// APIEndpoint is the endpoint for all API methods, with formatting for Sprintf
// APIEndpoint is the endpoint for all API methods,
// with formatting for Sprintf.
APIEndpoint = "https://api.telegram.org/bot%s/%s"
// FileEndpoint is the endpoint for downloading a file from Telegram
// FileEndpoint is the endpoint for downloading a file from Telegram.
FileEndpoint = "https://api.telegram.org/file/bot%s/%s"
)
@ -38,31 +40,31 @@ const (
ModeMarkdown = "Markdown"
)
// Chattable represents any event in chat(MessageConfig, PhotoConfig, ChatActionConfig and others)
// Chattable is any config type that can be sent.
type Chattable interface {
Values() (url.Values, error)
Method() string
values() (url.Values, error)
method() string
}
// Fileable represents any file event(PhotoConfig, DocumentConfig, AudioConfig, VoiceConfig, VideoConfig, StickerConfig)
// Fileable is any config type that can be sent that includes a file.
type Fileable interface {
Chattable
Params() (map[string]string, error)
Name() string
GetFile() interface{}
UseExistingFile() bool
params() (map[string]string, error)
name() string
getFile() interface{}
useExistingFile() bool
}
// BaseChat is base struct for all chat events (Message, Photo and so on)
// BaseChat is base type for all chat config types.
type BaseChat struct {
ChatID int
ChatID int // required
ChannelUsername string
ReplyToMessageID int
ReplyMarkup interface{}
}
// Values returns url.Values representation of BaseChat
func (chat *BaseChat) Values() (url.Values, error) {
// values returns url.Values representation of BaseChat
func (chat *BaseChat) values() (url.Values, error) {
v := url.Values{}
if chat.ChannelUsername != "" {
v.Add("chat_id", chat.ChannelUsername)
@ -86,7 +88,7 @@ func (chat *BaseChat) Values() (url.Values, error) {
return v, nil
}
// BaseFile is base struct for all file events (PhotoConfig, DocumentConfig, AudioConfig, VoiceConfig, VideoConfig, StickerConfig)
// BaseFile is a base type for all file config types.
type BaseFile struct {
BaseChat
FilePath string
@ -97,8 +99,8 @@ type BaseFile struct {
FileSize int
}
// Params returns map[string]string representation of BaseFile
func (file BaseFile) Params() (map[string]string, error) {
// params returns a map[string]string representation of BaseFile.
func (file BaseFile) params() (map[string]string, error) {
params := make(map[string]string)
if file.ChannelUsername != "" {
@ -120,7 +122,7 @@ func (file BaseFile) Params() (map[string]string, error) {
params["reply_markup"] = string(data)
}
if len(file.MimeType) > 0 {
if file.MimeType != "" {
params["mime_type"] = file.MimeType
}
@ -131,20 +133,22 @@ func (file BaseFile) Params() (map[string]string, error) {
return params, nil
}
// GetFile returns abstract representation of File inside BaseFile
func (file BaseFile) GetFile() interface{} {
// getFile returns the file.
func (file BaseFile) getFile() interface{} {
var result interface{}
if file.FilePath == "" {
result = file.File
} else {
log.Println("FilePath is deprecated.")
log.Println("Please use BaseFile.File instead.")
result = file.FilePath
}
return result
}
// UseExistingFile returns true if BaseFile contains already uploaded file by FileID
func (file BaseFile) UseExistingFile() bool {
// useExistingFile returns if the BaseFile has already been uploaded.
func (file BaseFile) useExistingFile() bool {
return file.UseExisting
}
@ -156,9 +160,9 @@ type MessageConfig struct {
DisableWebPagePreview bool
}
// Values returns url.Values representation of MessageConfig
func (config MessageConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of MessageConfig.
func (config MessageConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add("text", config.Text)
v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
if config.ParseMode != "" {
@ -168,29 +172,29 @@ func (config MessageConfig) Values() (url.Values, error) {
return v, nil
}
// Method returns Telegram API method name for sending Message
func (config MessageConfig) Method() string {
return "SendMessage"
// method returns Telegram API method name for sending Message.
func (config MessageConfig) method() string {
return "sendMessage"
}
// ForwardConfig contains information about a ForwardMessage request.
type ForwardConfig struct {
BaseChat
FromChatID int
FromChatID int // required
FromChannelUsername string
MessageID int
MessageID int // required
}
// Values returns url.Values representation of ForwardConfig
func (config ForwardConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of ForwardConfig.
func (config ForwardConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add("from_chat_id", strconv.Itoa(config.FromChatID))
v.Add("message_id", strconv.Itoa(config.MessageID))
return v, nil
}
// Method returns Telegram API method name for sending Forward
func (config ForwardConfig) Method() string {
// method returns Telegram API method name for sending Forward.
func (config ForwardConfig) method() string {
return "forwardMessage"
}
@ -200,9 +204,9 @@ type PhotoConfig struct {
Caption string
}
// Params returns map[string]string representation of PhotoConfig
func (config PhotoConfig) Params() (map[string]string, error) {
params, _ := config.BaseFile.Params()
// Params returns a map[string]string representation of PhotoConfig.
func (config PhotoConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
if config.Caption != "" {
params["caption"] = config.Caption
@ -211,25 +215,25 @@ func (config PhotoConfig) Params() (map[string]string, error) {
return params, nil
}
// Values returns url.Values representation of PhotoConfig
func (config PhotoConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// Values returns a url.Values representation of PhotoConfig.
func (config PhotoConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add(config.Name(), config.FileID)
v.Add(config.name(), config.FileID)
if config.Caption != "" {
v.Add("caption", config.Caption)
}
return v, nil
}
// Name return field name for uploading file
func (config PhotoConfig) Name() string {
// name returns the field name for the Photo.
func (config PhotoConfig) name() string {
return "photo"
}
// Method returns Telegram API method name for sending Photo
func (config PhotoConfig) Method() string {
return "SendPhoto"
// method returns Telegram API method name for sending Photo.
func (config PhotoConfig) method() string {
return "sendPhoto"
}
// AudioConfig contains information about a SendAudio request.
@ -240,11 +244,11 @@ type AudioConfig struct {
Title string
}
// Values returns url.Values representation of AudioConfig
func (config AudioConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of AudioConfig.
func (config AudioConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add(config.Name(), config.FileID)
v.Add(config.name(), config.FileID)
if config.Duration != 0 {
v.Add("duration", strconv.Itoa(config.Duration))
}
@ -259,9 +263,9 @@ func (config AudioConfig) Values() (url.Values, error) {
return v, nil
}
// Params returns map[string]string representation of AudioConfig
func (config AudioConfig) Params() (map[string]string, error) {
params, _ := config.BaseFile.Params()
// params returns a map[string]string representation of AudioConfig.
func (config AudioConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
if config.Duration != 0 {
params["duration"] = strconv.Itoa(config.Duration)
@ -277,14 +281,14 @@ func (config AudioConfig) Params() (map[string]string, error) {
return params, nil
}
// Name return field name for uploading file
func (config AudioConfig) Name() string {
// name returns the field name for the Audio.
func (config AudioConfig) name() string {
return "audio"
}
// Method returns Telegram API method name for sending Audio
func (config AudioConfig) Method() string {
return "SendAudio"
// method returns Telegram API method name for sending Audio.
func (config AudioConfig) method() string {
return "sendAudio"
}
// DocumentConfig contains information about a SendDocument request.
@ -292,29 +296,29 @@ type DocumentConfig struct {
BaseFile
}
// Values returns url.Values representation of DocumentConfig
func (config DocumentConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of DocumentConfig.
func (config DocumentConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add(config.Name(), config.FileID)
v.Add(config.name(), config.FileID)
return v, nil
}
// Params returns map[string]string representation of DocumentConfig
func (config DocumentConfig) Params() (map[string]string, error) {
params, _ := config.BaseFile.Params()
// params returns a map[string]string representation of DocumentConfig.
func (config DocumentConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
return params, nil
}
// Name return field name for uploading file
func (config DocumentConfig) Name() string {
// name returns the field name for the Document.
func (config DocumentConfig) name() string {
return "document"
}
// Method returns Telegram API method name for sending Document
func (config DocumentConfig) Method() string {
// method returns Telegram API method name for sending Document.
func (config DocumentConfig) method() string {
return "sendDocument"
}
@ -323,29 +327,29 @@ type StickerConfig struct {
BaseFile
}
// Values returns url.Values representation of StickerConfig
func (config StickerConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of StickerConfig.
func (config StickerConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add(config.Name(), config.FileID)
v.Add(config.name(), config.FileID)
return v, nil
}
// Params returns map[string]string representation of StickerConfig
func (config StickerConfig) Params() (map[string]string, error) {
params, _ := config.BaseFile.Params()
// params returns a map[string]string representation of StickerConfig.
func (config StickerConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
return params, nil
}
// Name return field name for uploading file
func (config StickerConfig) Name() string {
// name returns the field name for the Sticker.
func (config StickerConfig) name() string {
return "sticker"
}
// Method returns Telegram API method name for sending Sticker
func (config StickerConfig) Method() string {
// method returns Telegram API method name for sending Sticker.
func (config StickerConfig) method() string {
return "sendSticker"
}
@ -356,11 +360,11 @@ type VideoConfig struct {
Caption string
}
// Values returns url.Values representation of VideoConfig
func (config VideoConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of VideoConfig.
func (config VideoConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add(config.Name(), config.FileID)
v.Add(config.name(), config.FileID)
if config.Duration != 0 {
v.Add("duration", strconv.Itoa(config.Duration))
}
@ -371,20 +375,20 @@ func (config VideoConfig) Values() (url.Values, error) {
return v, nil
}
// Params returns map[string]string representation of VideoConfig
func (config VideoConfig) Params() (map[string]string, error) {
params, _ := config.BaseFile.Params()
// params returns a map[string]string representation of VideoConfig.
func (config VideoConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
return params, nil
}
// Name return field name for uploading file
func (config VideoConfig) Name() string {
// name returns the field name for the Video.
func (config VideoConfig) name() string {
return "video"
}
// Method returns Telegram API method name for sending Video
func (config VideoConfig) Method() string {
// method returns Telegram API method name for sending Video.
func (config VideoConfig) method() string {
return "sendVideo"
}
@ -394,11 +398,11 @@ type VoiceConfig struct {
Duration int
}
// Values returns url.Values representation of VoiceConfig
func (config VoiceConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of VoiceConfig.
func (config VoiceConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add(config.Name(), config.FileID)
v.Add(config.name(), config.FileID)
if config.Duration != 0 {
v.Add("duration", strconv.Itoa(config.Duration))
}
@ -406,9 +410,9 @@ func (config VoiceConfig) Values() (url.Values, error) {
return v, nil
}
// Params returns map[string]string representation of VoiceConfig
func (config VoiceConfig) Params() (map[string]string, error) {
params, _ := config.BaseFile.Params()
// params returns a map[string]string representation of VoiceConfig.
func (config VoiceConfig) params() (map[string]string, error) {
params, _ := config.BaseFile.params()
if config.Duration != 0 {
params["duration"] = strconv.Itoa(config.Duration)
@ -417,26 +421,26 @@ func (config VoiceConfig) Params() (map[string]string, error) {
return params, nil
}
// Name return field name for uploading file
func (config VoiceConfig) Name() string {
// name returns the field name for the Voice.
func (config VoiceConfig) name() string {
return "voice"
}
// Method returns Telegram API method name for sending Voice
func (config VoiceConfig) Method() string {
// method returns Telegram API method name for sending Voice.
func (config VoiceConfig) method() string {
return "sendVoice"
}
// LocationConfig contains information about a SendLocation request.
type LocationConfig struct {
BaseChat
Latitude float64
Longitude float64
Latitude float64 // required
Longitude float64 // required
}
// Values returns url.Values representation of LocationConfig
func (config LocationConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of LocationConfig.
func (config LocationConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
@ -444,37 +448,38 @@ func (config LocationConfig) Values() (url.Values, error) {
return v, nil
}
// Method returns Telegram API method name for sending Location
func (config LocationConfig) Method() string {
// method returns Telegram API method name for sending Location.
func (config LocationConfig) method() string {
return "sendLocation"
}
// ChatActionConfig contains information about a SendChatAction request.
type ChatActionConfig struct {
BaseChat
Action string
Action string // required
}
// Values returns url.Values representation of ChatActionConfig
func (config ChatActionConfig) Values() (url.Values, error) {
v, _ := config.BaseChat.Values()
// values returns a url.Values representation of ChatActionConfig.
func (config ChatActionConfig) values() (url.Values, error) {
v, _ := config.BaseChat.values()
v.Add("action", config.Action)
return v, nil
}
// Method returns Telegram API method name for sending ChatAction
func (config ChatActionConfig) Method() string {
// method returns Telegram API method name for sending ChatAction.
func (config ChatActionConfig) method() string {
return "sendChatAction"
}
// UserProfilePhotosConfig contains information about a GetUserProfilePhotos request.
// UserProfilePhotosConfig contains information about a
// GetUserProfilePhotos request.
type UserProfilePhotosConfig struct {
UserID int
Offset int
Limit int
}
// FileConfig has information about a file hosted on Telegram
// FileConfig has information about a file hosted on Telegram.
type FileConfig struct {
FileID string
}
@ -492,14 +497,16 @@ type WebhookConfig struct {
Certificate interface{}
}
// FileBytes contains information about a set of bytes to upload as a File.
// FileBytes contains information about a set of bytes to upload
// as a File.
type FileBytes struct {
Name string
Bytes []byte
}
// FileReader contains information about a reader to upload as a File.
// If Size is -1, it will read the entire Reader into memory to calculate a Size.
// If Size is -1, it will read the entire Reader into memory to
// calculate a Size.
type FileReader struct {
Name string
Reader io.Reader

View File

@ -5,12 +5,14 @@ import (
)
// NewMessage creates a new Message.
// Perhaps set a ChatAction of ChatTyping while processing.
//
// chatID is where to send it, text is the message text.
func NewMessage(chatID int, text string) MessageConfig {
return MessageConfig{
BaseChat: BaseChat{ChatID: chatID, ReplyToMessageID: 0},
BaseChat: BaseChat{
ChatID: chatID,
ReplyToMessageID: 0,
},
Text: text,
DisableWebPagePreview: false,
}
@ -29,137 +31,192 @@ func NewForward(chatID int, fromChatID int, messageID int) ForwardConfig {
}
// NewPhotoUpload creates a new photo uploader.
// This requires a file on the local filesystem to upload to Telegram.
// Perhaps set a ChatAction of ChatUploadPhoto while processing.
//
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewPhotoUpload(chatID int, file interface{}) PhotoConfig {
return PhotoConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewPhotoShare shares an existing photo.
// You may use this to reshare an existing photo without reuploading it.
//
// chatID is where to send it, fileID is the ID of the file already uploaded.
// chatID is where to send it, fileID is the ID of the file
// already uploaded.
func NewPhotoShare(chatID int, fileID string) PhotoConfig {
return PhotoConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewAudioUpload creates a new audio uploader.
// This requires a file on the local filesystem to upload to Telegram.
// Perhaps set a ChatAction of ChatRecordAudio or ChatUploadAudio while processing.
//
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewAudioUpload(chatID int, file interface{}) AudioConfig {
return AudioConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewAudioShare shares an existing audio file.
// You may use this to reshare an existing audio file without reuploading it.
// You may use this to reshare an existing audio file without
// reuploading it.
//
// chatID is where to send it, fileID is the ID of the audio already uploaded.
// chatID is where to send it, fileID is the ID of the audio
// already uploaded.
func NewAudioShare(chatID int, fileID string) AudioConfig {
return AudioConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewDocumentUpload creates a new document uploader.
// This requires a file on the local filesystem to upload to Telegram.
// Perhaps set a ChatAction of ChatUploadDocument while processing.
//
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewDocumentUpload(chatID int, file interface{}) DocumentConfig {
return DocumentConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewDocumentShare shares an existing document.
// You may use this to reshare an existing document without reuploading it.
// You may use this to reshare an existing document without
// reuploading it.
//
// chatID is where to send it, fileID is the ID of the document already uploaded.
// chatID is where to send it, fileID is the ID of the document
// already uploaded.
func NewDocumentShare(chatID int, fileID string) DocumentConfig {
return DocumentConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewStickerUpload creates a new sticker uploader.
// This requires a file on the local filesystem to upload to Telegram.
//
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewStickerUpload(chatID int, file interface{}) StickerConfig {
return StickerConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewStickerShare shares an existing sticker.
// You may use this to reshare an existing sticker without reuploading it.
// You may use this to reshare an existing sticker without
// reuploading it.
//
// chatID is where to send it, fileID is the ID of the sticker already uploaded.
// chatID is where to send it, fileID is the ID of the sticker
// already uploaded.
func NewStickerShare(chatID int, fileID string) StickerConfig {
return StickerConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewVideoUpload creates a new video uploader.
// This requires a file on the local filesystem to upload to Telegram.
// Perhaps set a ChatAction of ChatRecordVideo or ChatUploadVideo while processing.
//
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewVideoUpload(chatID int, file interface{}) VideoConfig {
return VideoConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewVideoShare shares an existing video.
// You may use this to reshare an existing video without reuploading it.
//
// chatID is where to send it, fileID is the ID of the video already uploaded.
// chatID is where to send it, fileID is the ID of the video
// already uploaded.
func NewVideoShare(chatID int, fileID string) VideoConfig {
return VideoConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewVoiceUpload creates a new voice uploader.
// This requires a file on the local filesystem to upload to Telegram.
// Perhaps set a ChatAction of ChatRecordVideo or ChatUploadVideo while processing.
//
// chatID is where to send it, file is a string path to the file, or FileReader or FileBytes.
// chatID is where to send it, file is a string path to the file,
// FileReader, or FileBytes.
func NewVoiceUpload(chatID int, file interface{}) VoiceConfig {
return VoiceConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, File: file, UseExisting: false},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
File: file,
UseExisting: false,
},
}
}
// NewVoiceShare shares an existing voice.
// You may use this to reshare an existing voice without reuploading it.
//
// chatID is where to send it, fileID is the ID of the video already uploaded.
// chatID is where to send it, fileID is the ID of the video
// already uploaded.
func NewVoiceShare(chatID int, fileID string) VoiceConfig {
return VoiceConfig{
BaseFile: BaseFile{BaseChat: BaseChat{ChatID: chatID}, FileID: fileID, UseExisting: true},
BaseFile: BaseFile{
BaseChat: BaseChat{ChatID: chatID},
FileID: fileID,
UseExisting: true,
},
}
}
// NewLocation shares your location.
// Perhaps set a ChatAction of ChatFindLocation while processing.
//
// chatID is where to send it, latitude and longitude are coordinates.
func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig {
return LocationConfig{
BaseChat: BaseChat{ChatID: chatID, ReplyToMessageID: 0, ReplyMarkup: nil},
BaseChat: BaseChat{
ChatID: chatID,
ReplyToMessageID: 0,
ReplyMarkup: nil,
},
Latitude: latitude,
Longitude: longitude,
}
@ -168,7 +225,7 @@ func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig
// NewChatAction sets a chat action.
// Actions last for 5 seconds, or until your next action.
//
// chatID is where to send it, action should be set via CHAT constants.
// chatID is where to send it, action should be set via Chat constants.
func NewChatAction(chatID int, action string) ChatActionConfig {
return ChatActionConfig{
BaseChat: BaseChat{ChatID: chatID},
@ -213,7 +270,7 @@ func NewWebhook(link string) WebhookConfig {
// NewWebhookWithCert creates a new webhook with a certificate.
//
// link is the url you wish to get webhooks,
// file contains a string to a file, or a FileReader or FileBytes.
// file contains a string to a file, FileReader, or FileBytes.
func NewWebhookWithCert(link string, file interface{}) WebhookConfig {
u, _ := url.Parse(link)

199
types.go
View File

@ -3,11 +3,13 @@ package tgbotapi
import (
"encoding/json"
"fmt"
"log"
"strings"
"time"
)
// APIResponse is a response from the Telegram API with the result stored raw.
// APIResponse is a response from the Telegram API with the result
// stored raw.
type APIResponse struct {
Ok bool `json:"ok"`
Result json.RawMessage `json:"result"`
@ -22,17 +24,18 @@ type Update struct {
InlineQuery InlineQuery `json:"inline_query"`
}
// User is a user, contained in Message and returned by GetSelf.
// User is a user on Telegram.
type User struct {
ID int `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
UserName string `json:"username"`
LastName string `json:"last_name"` // optional
UserName string `json:"username"` // optional
}
// String displays a simple text version of a user.
// It is normally a user's username,
// but falls back to a first/last name as available.
//
// It is normally a user's username, but falls back to a first/last
// name as available.
func (u *User) String() string {
if u.UserName != "" {
return u.UserName
@ -46,71 +49,72 @@ func (u *User) String() string {
return name
}
// GroupChat is a group chat, and not currently in use.
// GroupChat is a group chat.
type GroupChat struct {
ID int `json:"id"`
Title string `json:"title"`
}
// Chat is returned in Message, it contains information about the Chat a message was sent in.
// Chat contains information about the place a message was sent.
type Chat struct {
ID int `json:"id"`
Type string `json:"type"`
Title string `json:"title"`
UserName string `json:"username"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Title string `json:"title"` // optional
UserName string `json:"username"` // optional
FirstName string `json:"first_name"` // optional
LastName string `json:"last_name"` // optional
}
// IsPrivate returns true if the Chat is a private conversation
// IsPrivate returns if the Chat is a private conversation.
func (c *Chat) IsPrivate() bool {
return c.Type == "private"
}
// IsGroup returns true if the Chat is a group conversation
// IsGroup returns if the Chat is a group.
func (c *Chat) IsGroup() bool {
return c.Type == "group"
}
// IsSuperGroup returns true if the Chat is a supergroup conversation
// IsSuperGroup returns if the Chat is a supergroup.
func (c *Chat) IsSuperGroup() bool {
return c.Type == "supergroup"
}
// IsChannel returns true if the Chat is a channel
// IsChannel returns if the Chat is a channel.
func (c *Chat) IsChannel() bool {
return c.Type == "channel"
}
// Message is returned by almost every request, and contains data about almost anything.
// Message is returned by almost every request, and contains data about
// almost anything.
type Message struct {
MessageID int `json:"message_id"`
From User `json:"from"`
From User `json:"from"` // optional
Date int `json:"date"`
Chat Chat `json:"chat"`
ForwardFrom User `json:"forward_from"`
ForwardDate int `json:"forward_date"`
ReplyToMessage *Message `json:"reply_to_message"`
Text string `json:"text"`
Audio Audio `json:"audio"`
Document Document `json:"document"`
Photo []PhotoSize `json:"photo"`
Sticker Sticker `json:"sticker"`
Video Video `json:"video"`
Voice Voice `json:"voice"`
Caption string `json:"caption"`
Contact Contact `json:"contact"`
Location Location `json:"location"`
NewChatParticipant User `json:"new_chat_participant"`
LeftChatParticipant User `json:"left_chat_participant"`
NewChatTitle string `json:"new_chat_title"`
NewChatPhoto []PhotoSize `json:"new_chat_photo"`
DeleteChatPhoto bool `json:"delete_chat_photo"`
GroupChatCreated bool `json:"group_chat_created"`
SuperGroupChatCreated bool `json:"supergroup_chat_created"`
ChannelChatCreated bool `json:"channel_chat_created"`
MigrateToChatID int `json:"migrate_to_chat_id"`
MigrateFromChatID int `json:"migrate_from_chat_id"`
ForwardFrom User `json:"forward_from"` // optional
ForwardDate int `json:"forward_date"` // optional
ReplyToMessage *Message `json:"reply_to_message"` // optional
Text string `json:"text"` // optional
Audio Audio `json:"audio"` // optional
Document Document `json:"document"` // optional
Photo []PhotoSize `json:"photo"` // optional
Sticker Sticker `json:"sticker"` // optional
Video Video `json:"video"` // optional
Voice Voice `json:"voice"` // optional
Caption string `json:"caption"` // optional
Contact Contact `json:"contact"` // optional
Location Location `json:"location"` // optional
NewChatParticipant User `json:"new_chat_participant"` // optional
LeftChatParticipant User `json:"left_chat_participant"` // optional
NewChatTitle string `json:"new_chat_title"` // optional
NewChatPhoto []PhotoSize `json:"new_chat_photo"` // optional
DeleteChatPhoto bool `json:"delete_chat_photo"` // optional
GroupChatCreated bool `json:"group_chat_created"` // optional
SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional
ChannelChatCreated bool `json:"channel_chat_created"` // optional
MigrateToChatID int `json:"migrate_to_chat_id"` // optional
MigrateFromChatID int `json:"migrate_from_chat_id"` // optional
}
// Time converts the message timestamp into a Time.
@ -119,101 +123,111 @@ func (m *Message) Time() time.Time {
}
// IsGroup returns if the message was sent to a group.
//
// Deprecated in favor of Chat.IsGroup.
func (m *Message) IsGroup() bool {
log.Println("Message.IsGroup is deprecated.")
log.Println("Please use Chat.IsGroup instead.")
return m.Chat.IsGroup()
}
// IsCommand returns true if message starts from /
// IsCommand returns true if message starts with '/'.
func (m *Message) IsCommand() bool {
return m.Text != "" && m.Text[0] == '/'
}
// Command if message is command returns first word from message(entire command)
// otherwise returns empty string
// Command checks if the message was a command and if it was, returns the
// command. If the Message was not a command, it returns an empty string.
func (m *Message) Command() string {
if m.IsCommand() {
if !m.IsCommand() {
return ""
}
return strings.SplitN(m.Text, " ", 2)[0]
}
return ""
}
// CommandArguments if message is command, returns all text after command, excluding the command itself
// otherwise returns empty string
// CommandArguments checks if the message was a command and if it was,
// returns all text after the command name. If the Message was not a
// command, it returns an empty string.
func (m *Message) CommandArguments() string {
if m.IsCommand() {
split := strings.SplitN(m.Text, " ", 2)
if len(split) == 2 {
return strings.SplitN(m.Text, " ", 2)[1]
}
}
if !m.IsCommand() {
return ""
}
split := strings.SplitN(m.Text, " ", 2)
if len(split) != 2 {
return ""
}
return strings.SplitN(m.Text, " ", 2)[1]
}
// PhotoSize contains information about photos, including ID and Width and Height.
// PhotoSize contains information about photos.
type PhotoSize struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
FileSize int `json:"file_size"`
FileSize int `json:"file_size"` // optional
}
// Audio contains information about audio,
// including ID, Duration, Performer and Title.
// Audio contains information about audio.
type Audio struct {
FileID string `json:"file_id"`
Duration int `json:"duration"`
Performer string `json:"performer"`
Title string `json:"title"`
MimeType string `json:"mime_type"`
FileSize int `json:"file_size"`
Performer string `json:"performer"` // optional
Title string `json:"title"` // optional
MimeType string `json:"mime_type"` // optional
FileSize int `json:"file_size"` // optional
}
// Document contains information about a document, including ID and a Thumbnail.
// Document contains information about a document.
type Document struct {
FileID string `json:"file_id"`
Thumbnail PhotoSize `json:"thumb"`
FileName string `json:"file_name"`
MimeType string `json:"mime_type"`
FileSize int `json:"file_size"`
Thumbnail PhotoSize `json:"thumb"` // optional
FileName string `json:"file_name"` // optional
MimeType string `json:"mime_type"` // optional
FileSize int `json:"file_size"` // optional
}
// Sticker contains information about a sticker, including ID and Thumbnail.
// Sticker contains information about a sticker.
type Sticker struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
Thumbnail PhotoSize `json:"thumb"`
FileSize int `json:"file_size"`
Thumbnail PhotoSize `json:"thumb"` // optional
FileSize int `json:"file_size"` // optional
}
// Video contains information about a video, including ID and duration and Thumbnail.
// Video contains information about a video.
type Video struct {
FileID string `json:"file_id"`
Width int `json:"width"`
Height int `json:"height"`
Duration int `json:"duration"`
Thumbnail PhotoSize `json:"thumb"`
MimeType string `json:"mime_type"`
FileSize int `json:"file_size"`
Thumbnail PhotoSize `json:"thumb"` // optional
MimeType string `json:"mime_type"` // optional
FileSize int `json:"file_size"` // optional
}
// Voice contains information about a voice, including ID and duration.
// Voice contains information about a voice.
type Voice struct {
FileID string `json:"file_id"`
Duration int `json:"duration"`
MimeType string `json:"mime_type"`
FileSize int `json:"file_size"`
MimeType string `json:"mime_type"` // optional
FileSize int `json:"file_size"` // optional
}
// Contact contains information about a contact, such as PhoneNumber and UserId.
// Contact contains information about a contact.
//
// Note that LastName and UserID may be empty.
type Contact struct {
PhoneNumber string `json:"phone_number"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
UserID int `json:"user_id"`
LastName string `json:"last_name"` // optional
UserID int `json:"user_id"` // optional
}
// Location contains information about a place, such as Longitude and Latitude.
// Location contains information about a place.
type Location struct {
Longitude float32 `json:"longitude"`
Latitude float32 `json:"latitude"`
@ -225,11 +239,11 @@ type UserProfilePhotos struct {
Photos []PhotoSize `json:"photos"`
}
// File contains information about a file to download from Telegram
// File contains information about a file to download from Telegram.
type File struct {
FileID string `json:"file_id"`
FileSize int `json:"file_size"`
FilePath string `json:"file_path"`
FileSize int `json:"file_size"` // optional
FilePath string `json:"file_path"` // optional
}
// Link returns a full path to the download URL for a File.
@ -242,24 +256,25 @@ func (f *File) Link(token string) string {
// ReplyKeyboardMarkup allows the Bot to set a custom keyboard.
type ReplyKeyboardMarkup struct {
Keyboard [][]string `json:"keyboard"`
ResizeKeyboard bool `json:"resize_keyboard"`
OneTimeKeyboard bool `json:"one_time_keyboard"`
Selective bool `json:"selective"`
ResizeKeyboard bool `json:"resize_keyboard"` // optional
OneTimeKeyboard bool `json:"one_time_keyboard"` // optional
Selective bool `json:"selective"` // optional
}
// ReplyKeyboardHide allows the Bot to hide a custom keyboard.
type ReplyKeyboardHide struct {
HideKeyboard bool `json:"hide_keyboard"`
Selective bool `json:"selective"`
Selective bool `json:"selective"` // optional
}
// ForceReply allows the Bot to have users directly reply to it without additional interaction.
// ForceReply allows the Bot to have users directly reply to it without
// additional interaction.
type ForceReply struct {
ForceReply bool `json:"force_reply"`
Selective bool `json:"selective"`
Selective bool `json:"selective"` // optional
}
// InlineQuery is a Query from Telegram for an inline request
// InlineQuery is a Query from Telegram for an inline request.
type InlineQuery struct {
ID string `json:"id"`
From User `json:"user"`