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) [![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. 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. The scope of this project is just to provide a wrapper around the API
without any additional features. There are other projects for creating
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`. something with plugins and command handlers without having to design
all that yourself.
## Example ## 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 ```go
package main 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 ```go
package main 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 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 package tgbotapi
import ( import (
@ -17,7 +18,7 @@ import (
"time" "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 { type BotAPI struct {
Token string `json:"token"` Token string `json:"token"`
Debug bool `json:"debug"` Debug bool `json:"debug"`
@ -26,13 +27,16 @@ type BotAPI struct {
} }
// NewBotAPI creates a new BotAPI instance. // 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) { func NewBotAPI(token string) (*BotAPI, error) {
return NewBotAPIWithClient(token, &http.Client{}) return NewBotAPIWithClient(token, &http.Client{})
} }
// NewBotAPIWithClient creates a new BotAPI instance passing an http.Client. // NewBotAPIWithClient creates a new BotAPI instance
// Requires a token, provided by @BotFather on Telegram // 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) { func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
bot := &BotAPI{ bot := &BotAPI{
Token: token, 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. // 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) { 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 { if err != nil {
return APIResponse{}, err return APIResponse{}, err
} }
@ -81,6 +86,7 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse,
return apiResp, nil 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) { func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) {
resp, err := bot.MakeRequest(endpoint, params) resp, err := bot.MakeRequest(endpoint, params)
if err != nil { 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. // UploadFile makes a request to the API with a file.
// //
// Requires the parameter to hold the file not be in the params. // 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) { func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
ms := multipartstreamer.New() ms := multipartstreamer.New()
ms.WriteFields(params) 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") 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 { if err != nil {
return APIResponse{}, err return APIResponse{}, err
} }
@ -173,7 +185,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna
// GetFileDirectURL returns direct URL to file // GetFileDirectURL returns direct URL to file
// //
// Requires fileID // It requires the FileID.
func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) { func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
file, err := bot.GetFile(FileConfig{fileID}) file, err := bot.GetFile(FileConfig{fileID})
@ -186,7 +198,9 @@ func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
// GetMe fetches the currently authenticated bot. // 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) { func (bot *BotAPI) GetMe() (User, error) {
resp, err := bot.MakeRequest("getMe", nil) resp, err := bot.MakeRequest("getMe", nil)
if err != nil { if err != nil {
@ -201,16 +215,16 @@ func (bot *BotAPI) GetMe() (User, error) {
return user, nil 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 { func (bot *BotAPI) IsMessageToMe(message Message) bool {
return strings.Contains(message.Text, "@"+bot.Self.UserName) 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) { func (bot *BotAPI) Send(c Chattable) (Message, error) {
switch c.(type) { switch c.(type) {
case Fileable: 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{}) { func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
if bot.Debug { if bot.Debug {
log.Printf("%s req : %+v\n", context, v) 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) { func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
v, err := config.Values() v, err := config.values()
if err != nil { if err != nil {
return Message{}, err return Message{}, err
@ -242,15 +260,16 @@ func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error)
return message, nil return message, nil
} }
// uploadAndSend will send a Message with a new file to Telegram.
func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) { func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
params, err := config.Params() params, err := config.params()
if err != nil { if err != nil {
return Message{}, err 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 { if err != nil {
return Message{}, err return Message{}, err
} }
@ -258,28 +277,29 @@ func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error
var message Message var message Message
json.Unmarshal(resp.Result, &message) json.Unmarshal(resp.Result, &message)
if bot.Debug { bot.debugLog(method, nil, message)
log.Printf("%s resp: %+v\n", method, message)
}
return message, nil 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) { func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
if config.UseExistingFile() { if config.useExistingFile() {
return bot.sendExisting(config.Method(), config) 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) { func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
v, err := config.Values() v, err := config.values()
if err != nil { if err != nil {
return Message{}, err return Message{}, err
} }
message, err := bot.makeMessageRequest(config.Method(), v) message, err := bot.makeMessageRequest(config.method(), v)
if err != nil { if err != nil {
return Message{}, err return Message{}, err
@ -290,7 +310,7 @@ func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
// GetUserProfilePhotos gets a user's profile photos. // GetUserProfilePhotos gets a user's profile photos.
// //
// Requires UserID. // It requires UserID.
// Offset and Limit are optional. // Offset and Limit are optional.
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) { func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
v := url.Values{} v := url.Values{}
@ -315,7 +335,7 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro
return profilePhotos, nil 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. // Requires FileID.
func (bot *BotAPI) GetFile(config FileConfig) (File, error) { 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! // If a WebHook is set, this will not return any data!
// //
// Offset, Limit, and Timeout are optional. // Offset, Limit, and Timeout are optional.
// To not get old items, set Offset to one higher than the previous item. // To avoid stale items, set Offset to one higher than the previous item.
// Set Timeout to a large number to reduce requests and get responses instantly. // 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) { func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
v := url.Values{} v := url.Values{}
if config.Offset > 0 { if config.Offset > 0 {
@ -361,24 +382,22 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
var updates []Update var updates []Update
json.Unmarshal(resp.Result, &updates) json.Unmarshal(resp.Result, &updates)
if bot.Debug { bot.debugLog("getUpdates", v, updates)
log.Printf("getUpdates: %+v\n", updates)
}
return updates, nil return updates, nil
} }
// RemoveWebhook removes webhook // RemoveWebhook unsets the webhook.
//
// There are no parameters for this method.
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) { func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
return bot.MakeRequest("setWebhook", url.Values{}) return bot.MakeRequest("setWebhook", url.Values{})
} }
// SetWebhook sets a webhook. // SetWebhook sets a webhook.
//
// If this is set, GetUpdates will not get any data! // 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) { func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
if config.Certificate == nil { if config.Certificate == nil {
v := url.Values{} v := url.Values{}
@ -406,8 +425,6 @@ func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
} }
// GetUpdatesChan starts and returns a channel for getting updates. // GetUpdatesChan starts and returns a channel for getting updates.
//
// Requires UpdateConfig
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (<-chan Update, error) { func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (<-chan Update, error) {
updatesChan := make(chan Update, 100) 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. // 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) { func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) {
v := url.Values{} v := url.Values{}

View File

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

View File

@ -5,12 +5,14 @@ import (
) )
// NewMessage creates a new Message. // NewMessage creates a new Message.
// Perhaps set a ChatAction of ChatTyping while processing.
// //
// chatID is where to send it, text is the message text. // chatID is where to send it, text is the message text.
func NewMessage(chatID int, text string) MessageConfig { func NewMessage(chatID int, text string) MessageConfig {
return MessageConfig{ return MessageConfig{
BaseChat: BaseChat{ChatID: chatID, ReplyToMessageID: 0}, BaseChat: BaseChat{
ChatID: chatID,
ReplyToMessageID: 0,
},
Text: text, Text: text,
DisableWebPagePreview: false, DisableWebPagePreview: false,
} }
@ -29,137 +31,192 @@ func NewForward(chatID int, fromChatID int, messageID int) ForwardConfig {
} }
// NewPhotoUpload creates a new photo uploader. // 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 { func NewPhotoUpload(chatID int, file interface{}) PhotoConfig {
return 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. // NewPhotoShare shares an existing photo.
// You may use this to reshare an existing photo without reuploading it. // 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 { func NewPhotoShare(chatID int, fileID string) PhotoConfig {
return 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. // 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 { func NewAudioUpload(chatID int, file interface{}) AudioConfig {
return 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. // 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 { func NewAudioShare(chatID int, fileID string) AudioConfig {
return 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. // 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 { func NewDocumentUpload(chatID int, file interface{}) DocumentConfig {
return 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. // 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 { func NewDocumentShare(chatID int, fileID string) DocumentConfig {
return 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. // 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 { func NewStickerUpload(chatID int, file interface{}) StickerConfig {
return 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. // 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 { func NewStickerShare(chatID int, fileID string) StickerConfig {
return 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. // 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 { func NewVideoUpload(chatID int, file interface{}) VideoConfig {
return 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. // NewVideoShare shares an existing video.
// You may use this to reshare an existing video without reuploading it. // 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 { func NewVideoShare(chatID int, fileID string) VideoConfig {
return 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. // 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 { func NewVoiceUpload(chatID int, file interface{}) VoiceConfig {
return 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. // NewVoiceShare shares an existing voice.
// You may use this to reshare an existing voice without reuploading it. // 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 { func NewVoiceShare(chatID int, fileID string) VoiceConfig {
return 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. // NewLocation shares your location.
// Perhaps set a ChatAction of ChatFindLocation while processing.
// //
// chatID is where to send it, latitude and longitude are coordinates. // chatID is where to send it, latitude and longitude are coordinates.
func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig { func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig {
return LocationConfig{ return LocationConfig{
BaseChat: BaseChat{ChatID: chatID, ReplyToMessageID: 0, ReplyMarkup: nil}, BaseChat: BaseChat{
ChatID: chatID,
ReplyToMessageID: 0,
ReplyMarkup: nil,
},
Latitude: latitude, Latitude: latitude,
Longitude: longitude, Longitude: longitude,
} }
@ -168,7 +225,7 @@ func NewLocation(chatID int, latitude float64, longitude float64) LocationConfig
// NewChatAction sets a chat action. // NewChatAction sets a chat action.
// Actions last for 5 seconds, or until your next 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 { func NewChatAction(chatID int, action string) ChatActionConfig {
return ChatActionConfig{ return ChatActionConfig{
BaseChat: BaseChat{ChatID: chatID}, BaseChat: BaseChat{ChatID: chatID},
@ -213,7 +270,7 @@ func NewWebhook(link string) WebhookConfig {
// NewWebhookWithCert creates a new webhook with a certificate. // NewWebhookWithCert creates a new webhook with a certificate.
// //
// link is the url you wish to get webhooks, // 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 { func NewWebhookWithCert(link string, file interface{}) WebhookConfig {
u, _ := url.Parse(link) u, _ := url.Parse(link)

195
types.go
View File

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