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.
@@ -4,17 +4,22 @@ [![GoDoc](https://godoc.org/github.com/Syfaro/telegram-bot-api?status.svg)](http://godoc.org/github.com/Syfaro/telegram-bot-api)
[![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. - -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. +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. -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 @@ }
} ``` -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 @@ }
} ``` -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.
@@ -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 @@ "strings"
"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 @@ Client *http.Client `json:"-"`
} // 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 @@ return bot, nil
} // 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 @@
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 @@
// 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 @@ default:
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 @@ }
// 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 @@ }
// 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 @@
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 @@ return bot.sendChattable(c)
} } +// 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 @@ log.Printf("%s resp: %+v\n", context, message)
} } +// 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 @@
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 @@
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 @@ }
// 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 @@
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 @@ // GetUpdates fetches updates.
// 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 @@
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 @@ return apiResp, nil
} // 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 @@ return updatesChan, 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{}
@@ -3,15 +3,17 @@
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 @@
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 @@ MimeType string
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 @@
params["reply_markup"] = string(data) } - if len(file.MimeType) > 0 { + if file.MimeType != "" { params["mime_type"] = file.MimeType }@@ -131,20 +133,22 @@
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 @@ ParseMode string
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 @@
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 @@ BaseFile
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 @@
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 @@ Performer string
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 @@
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 @@
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 @@ Duration int
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 @@
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 @@ BaseFile
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 @@
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 @@
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 @@
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 @@ URL *url.URL
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
@@ -5,13 +5,15 @@ "net/url"
) // 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}, - Text: text, + BaseChat: BaseChat{ + ChatID: chatID, + ReplyToMessageID: 0, + }, + Text: text, DisableWebPagePreview: false, } }@@ -29,137 +31,192 @@ }
} // 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 @@
// 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 @@
// 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)
@@ -3,11 +3,13 @@
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 @@ Message Message `json:"message"`
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 @@
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 @@ return time.Unix(int64(m.Date), 0)
} // 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() { - return strings.SplitN(m.Text, " ", 2)[0] + if !m.IsCommand() { + return "" } - return "" + + return strings.SplitN(m.Text, " ", 2)[0] } -// 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 "" + + 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 @@ TotalCount int `json:"total_count"`
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 @@
// 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"`