summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go')
-rw-r--r--vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go726
1 files changed, 726 insertions, 0 deletions
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go
new file mode 100644
index 00000000..c58afec4
--- /dev/null
+++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/bot.go
@@ -0,0 +1,726 @@
+// Package tgbotapi has functions and types used for interacting with
+// the Telegram Bot API.
+package tgbotapi
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// HTTPClient is the type needed for the bot to perform HTTP requests.
+type HTTPClient interface {
+ Do(req *http.Request) (*http.Response, error)
+}
+
+// BotAPI allows you to interact with the Telegram Bot API.
+type BotAPI struct {
+ Token string `json:"token"`
+ Debug bool `json:"debug"`
+ Buffer int `json:"buffer"`
+
+ Self User `json:"-"`
+ Client HTTPClient `json:"-"`
+ shutdownChannel chan interface{}
+
+ apiEndpoint string
+}
+
+// NewBotAPI creates a new BotAPI instance.
+//
+// It requires a token, provided by @BotFather on Telegram.
+func NewBotAPI(token string) (*BotAPI, error) {
+ return NewBotAPIWithClient(token, APIEndpoint, &http.Client{})
+}
+
+// NewBotAPIWithAPIEndpoint creates a new BotAPI instance
+// and allows you to pass API endpoint.
+//
+// It requires a token, provided by @BotFather on Telegram and API endpoint.
+func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) {
+ return NewBotAPIWithClient(token, apiEndpoint, &http.Client{})
+}
+
+// NewBotAPIWithClient creates a new BotAPI instance
+// and allows you to pass a http.Client.
+//
+// It requires a token, provided by @BotFather on Telegram and API endpoint.
+func NewBotAPIWithClient(token, apiEndpoint string, client HTTPClient) (*BotAPI, error) {
+ bot := &BotAPI{
+ Token: token,
+ Client: client,
+ Buffer: 100,
+ shutdownChannel: make(chan interface{}),
+
+ apiEndpoint: apiEndpoint,
+ }
+
+ self, err := bot.GetMe()
+ if err != nil {
+ return nil, err
+ }
+
+ bot.Self = self
+
+ return bot, nil
+}
+
+// SetAPIEndpoint changes the Telegram Bot API endpoint used by the instance.
+func (bot *BotAPI) SetAPIEndpoint(apiEndpoint string) {
+ bot.apiEndpoint = apiEndpoint
+}
+
+func buildParams(in Params) url.Values {
+ if in == nil {
+ return url.Values{}
+ }
+
+ out := url.Values{}
+
+ for key, value := range in {
+ out.Set(key, value)
+ }
+
+ return out
+}
+
+// MakeRequest makes a request to a specific endpoint with our token.
+func (bot *BotAPI) MakeRequest(endpoint string, params Params) (*APIResponse, error) {
+ if bot.Debug {
+ log.Printf("Endpoint: %s, params: %v\n", endpoint, params)
+ }
+
+ method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
+
+ values := buildParams(params)
+
+ req, err := http.NewRequest("POST", method, strings.NewReader(values.Encode()))
+ if err != nil {
+ return &APIResponse{}, err
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ resp, err := bot.Client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var apiResp APIResponse
+ bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
+ if err != nil {
+ return &apiResp, err
+ }
+
+ if bot.Debug {
+ log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes))
+ }
+
+ if !apiResp.Ok {
+ var parameters ResponseParameters
+
+ if apiResp.Parameters != nil {
+ parameters = *apiResp.Parameters
+ }
+
+ return &apiResp, &Error{
+ Code: apiResp.ErrorCode,
+ Message: apiResp.Description,
+ ResponseParameters: parameters,
+ }
+ }
+
+ return &apiResp, nil
+}
+
+// decodeAPIResponse decode response and return slice of bytes if debug enabled.
+// If debug disabled, just decode http.Response.Body stream to APIResponse struct
+// for efficient memory usage
+func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) ([]byte, error) {
+ if !bot.Debug {
+ dec := json.NewDecoder(responseBody)
+ err := dec.Decode(resp)
+ return nil, err
+ }
+
+ // if debug, read response body
+ data, err := ioutil.ReadAll(responseBody)
+ if err != nil {
+ return nil, err
+ }
+
+ err = json.Unmarshal(data, resp)
+ if err != nil {
+ return nil, err
+ }
+
+ return data, nil
+}
+
+// UploadFiles makes a request to the API with files.
+func (bot *BotAPI) UploadFiles(endpoint string, params Params, files []RequestFile) (*APIResponse, error) {
+ r, w := io.Pipe()
+ m := multipart.NewWriter(w)
+
+ // This code modified from the very helpful @HirbodBehnam
+ // https://github.com/go-telegram-bot-api/telegram-bot-api/issues/354#issuecomment-663856473
+ go func() {
+ defer w.Close()
+ defer m.Close()
+
+ for field, value := range params {
+ if err := m.WriteField(field, value); err != nil {
+ w.CloseWithError(err)
+ return
+ }
+ }
+
+ for _, file := range files {
+ if file.Data.NeedsUpload() {
+ name, reader, err := file.Data.UploadData()
+ if err != nil {
+ w.CloseWithError(err)
+ return
+ }
+
+ part, err := m.CreateFormFile(file.Name, name)
+ if err != nil {
+ w.CloseWithError(err)
+ return
+ }
+
+ if _, err := io.Copy(part, reader); err != nil {
+ w.CloseWithError(err)
+ return
+ }
+
+ if closer, ok := reader.(io.ReadCloser); ok {
+ if err = closer.Close(); err != nil {
+ w.CloseWithError(err)
+ return
+ }
+ }
+ } else {
+ value := file.Data.SendData()
+
+ if err := m.WriteField(file.Name, value); err != nil {
+ w.CloseWithError(err)
+ return
+ }
+ }
+ }
+ }()
+
+ if bot.Debug {
+ log.Printf("Endpoint: %s, params: %v, with %d files\n", endpoint, params, len(files))
+ }
+
+ method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
+
+ req, err := http.NewRequest("POST", method, r)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Content-Type", m.FormDataContentType())
+
+ resp, err := bot.Client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ var apiResp APIResponse
+ bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
+ if err != nil {
+ return &apiResp, err
+ }
+
+ if bot.Debug {
+ log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes))
+ }
+
+ if !apiResp.Ok {
+ var parameters ResponseParameters
+
+ if apiResp.Parameters != nil {
+ parameters = *apiResp.Parameters
+ }
+
+ return &apiResp, &Error{
+ Message: apiResp.Description,
+ ResponseParameters: parameters,
+ }
+ }
+
+ return &apiResp, nil
+}
+
+// GetFileDirectURL returns direct URL to file
+//
+// It requires the FileID.
+func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
+ file, err := bot.GetFile(FileConfig{fileID})
+
+ if err != nil {
+ return "", err
+ }
+
+ return file.Link(bot.Token), nil
+}
+
+// GetMe fetches the currently authenticated bot.
+//
+// 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 {
+ return User{}, err
+ }
+
+ var user User
+ err = json.Unmarshal(resp.Result, &user)
+
+ return user, err
+}
+
+// IsMessageToMe returns true if message directed to this bot.
+//
+// It requires the Message.
+func (bot *BotAPI) IsMessageToMe(message Message) bool {
+ return strings.Contains(message.Text, "@"+bot.Self.UserName)
+}
+
+func hasFilesNeedingUpload(files []RequestFile) bool {
+ for _, file := range files {
+ if file.Data.NeedsUpload() {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Request sends a Chattable to Telegram, and returns the APIResponse.
+func (bot *BotAPI) Request(c Chattable) (*APIResponse, error) {
+ params, err := c.params()
+ if err != nil {
+ return nil, err
+ }
+
+ if t, ok := c.(Fileable); ok {
+ files := t.files()
+
+ // If we have files that need to be uploaded, we should delegate the
+ // request to UploadFile.
+ if hasFilesNeedingUpload(files) {
+ return bot.UploadFiles(t.method(), params, files)
+ }
+
+ // However, if there are no files to be uploaded, there's likely things
+ // that need to be turned into params instead.
+ for _, file := range files {
+ params[file.Name] = file.Data.SendData()
+ }
+ }
+
+ return bot.MakeRequest(c.method(), params)
+}
+
+// Send will send a Chattable item to Telegram and provides the
+// returned Message.
+func (bot *BotAPI) Send(c Chattable) (Message, error) {
+ resp, err := bot.Request(c)
+ if err != nil {
+ return Message{}, err
+ }
+
+ var message Message
+ err = json.Unmarshal(resp.Result, &message)
+
+ return message, err
+}
+
+// SendMediaGroup sends a media group and returns the resulting messages.
+func (bot *BotAPI) SendMediaGroup(config MediaGroupConfig) ([]Message, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return nil, err
+ }
+
+ var messages []Message
+ err = json.Unmarshal(resp.Result, &messages)
+
+ return messages, err
+}
+
+// GetUserProfilePhotos gets a user's profile photos.
+//
+// It requires UserID.
+// Offset and Limit are optional.
+func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return UserProfilePhotos{}, err
+ }
+
+ var profilePhotos UserProfilePhotos
+ err = json.Unmarshal(resp.Result, &profilePhotos)
+
+ return profilePhotos, err
+}
+
+// GetFile returns a File which can download a file from Telegram.
+//
+// Requires FileID.
+func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return File{}, err
+ }
+
+ var file File
+ err = json.Unmarshal(resp.Result, &file)
+
+ return file, err
+}
+
+// GetUpdates fetches updates.
+// If a WebHook is set, this will not return any data!
+//
+// Offset, Limit, Timeout, and AllowedUpdates are optional.
+// 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) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return []Update{}, err
+ }
+
+ var updates []Update
+ err = json.Unmarshal(resp.Result, &updates)
+
+ return updates, err
+}
+
+// GetWebhookInfo allows you to fetch information about a webhook and if
+// one currently is set, along with pending update count and error messages.
+func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
+ resp, err := bot.MakeRequest("getWebhookInfo", nil)
+ if err != nil {
+ return WebhookInfo{}, err
+ }
+
+ var info WebhookInfo
+ err = json.Unmarshal(resp.Result, &info)
+
+ return info, err
+}
+
+// GetUpdatesChan starts and returns a channel for getting updates.
+func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel {
+ ch := make(chan Update, bot.Buffer)
+
+ go func() {
+ for {
+ select {
+ case <-bot.shutdownChannel:
+ close(ch)
+ return
+ default:
+ }
+
+ updates, err := bot.GetUpdates(config)
+ if err != nil {
+ log.Println(err)
+ log.Println("Failed to get updates, retrying in 3 seconds...")
+ time.Sleep(time.Second * 3)
+
+ continue
+ }
+
+ for _, update := range updates {
+ if update.UpdateID >= config.Offset {
+ config.Offset = update.UpdateID + 1
+ ch <- update
+ }
+ }
+ }
+ }()
+
+ return ch
+}
+
+// StopReceivingUpdates stops the go routine which receives updates
+func (bot *BotAPI) StopReceivingUpdates() {
+ if bot.Debug {
+ log.Println("Stopping the update receiver routine...")
+ }
+ close(bot.shutdownChannel)
+}
+
+// ListenForWebhook registers a http handler for a webhook.
+func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
+ ch := make(chan Update, bot.Buffer)
+
+ http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
+ update, err := bot.HandleUpdate(r)
+ if err != nil {
+ errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
+ w.WriteHeader(http.StatusBadRequest)
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write(errMsg)
+ return
+ }
+
+ ch <- *update
+ })
+
+ return ch
+}
+
+// ListenForWebhookRespReqFormat registers a http handler for a single incoming webhook.
+func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.Request) UpdatesChannel {
+ ch := make(chan Update, bot.Buffer)
+
+ func(w http.ResponseWriter, r *http.Request) {
+ update, err := bot.HandleUpdate(r)
+ if err != nil {
+ errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
+ w.WriteHeader(http.StatusBadRequest)
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write(errMsg)
+ return
+ }
+
+ ch <- *update
+ close(ch)
+ }(w, r)
+
+ return ch
+}
+
+// HandleUpdate parses and returns update received via webhook
+func (bot *BotAPI) HandleUpdate(r *http.Request) (*Update, error) {
+ if r.Method != http.MethodPost {
+ err := errors.New("wrong HTTP method required POST")
+ return nil, err
+ }
+
+ var update Update
+ err := json.NewDecoder(r.Body).Decode(&update)
+ if err != nil {
+ return nil, err
+ }
+
+ return &update, nil
+}
+
+// WriteToHTTPResponse writes the request to the HTTP ResponseWriter.
+//
+// It doesn't support uploading files.
+//
+// See https://core.telegram.org/bots/api#making-requests-when-getting-updates
+// for details.
+func WriteToHTTPResponse(w http.ResponseWriter, c Chattable) error {
+ params, err := c.params()
+ if err != nil {
+ return err
+ }
+
+ if t, ok := c.(Fileable); ok {
+ if hasFilesNeedingUpload(t.files()) {
+ return errors.New("unable to use http response to upload files")
+ }
+ }
+
+ values := buildParams(params)
+ values.Set("method", c.method())
+
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ _, err = w.Write([]byte(values.Encode()))
+ return err
+}
+
+// GetChat gets information about a chat.
+func (bot *BotAPI) GetChat(config ChatInfoConfig) (Chat, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return Chat{}, err
+ }
+
+ var chat Chat
+ err = json.Unmarshal(resp.Result, &chat)
+
+ return chat, err
+}
+
+// GetChatAdministrators gets a list of administrators in the chat.
+//
+// If none have been appointed, only the creator will be returned.
+// Bots are not shown, even if they are an administrator.
+func (bot *BotAPI) GetChatAdministrators(config ChatAdministratorsConfig) ([]ChatMember, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return []ChatMember{}, err
+ }
+
+ var members []ChatMember
+ err = json.Unmarshal(resp.Result, &members)
+
+ return members, err
+}
+
+// GetChatMembersCount gets the number of users in a chat.
+func (bot *BotAPI) GetChatMembersCount(config ChatMemberCountConfig) (int, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return -1, err
+ }
+
+ var count int
+ err = json.Unmarshal(resp.Result, &count)
+
+ return count, err
+}
+
+// GetChatMember gets a specific chat member.
+func (bot *BotAPI) GetChatMember(config GetChatMemberConfig) (ChatMember, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return ChatMember{}, err
+ }
+
+ var member ChatMember
+ err = json.Unmarshal(resp.Result, &member)
+
+ return member, err
+}
+
+// GetGameHighScores allows you to get the high scores for a game.
+func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return []GameHighScore{}, err
+ }
+
+ var highScores []GameHighScore
+ err = json.Unmarshal(resp.Result, &highScores)
+
+ return highScores, err
+}
+
+// GetInviteLink get InviteLink for a chat
+func (bot *BotAPI) GetInviteLink(config ChatInviteLinkConfig) (string, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return "", err
+ }
+
+ var inviteLink string
+ err = json.Unmarshal(resp.Result, &inviteLink)
+
+ return inviteLink, err
+}
+
+// GetStickerSet returns a StickerSet.
+func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return StickerSet{}, err
+ }
+
+ var stickers StickerSet
+ err = json.Unmarshal(resp.Result, &stickers)
+
+ return stickers, err
+}
+
+// StopPoll stops a poll and returns the result.
+func (bot *BotAPI) StopPoll(config StopPollConfig) (Poll, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return Poll{}, err
+ }
+
+ var poll Poll
+ err = json.Unmarshal(resp.Result, &poll)
+
+ return poll, err
+}
+
+// GetMyCommands gets the currently registered commands.
+func (bot *BotAPI) GetMyCommands() ([]BotCommand, error) {
+ return bot.GetMyCommandsWithConfig(GetMyCommandsConfig{})
+}
+
+// GetMyCommandsWithConfig gets the currently registered commands with a config.
+func (bot *BotAPI) GetMyCommandsWithConfig(config GetMyCommandsConfig) ([]BotCommand, error) {
+ resp, err := bot.Request(config)
+ if err != nil {
+ return nil, err
+ }
+
+ var commands []BotCommand
+ err = json.Unmarshal(resp.Result, &commands)
+
+ return commands, err
+}
+
+// CopyMessage copy messages of any kind. The method is analogous to the method
+// forwardMessage, but the copied message doesn't have a link to the original
+// message. Returns the MessageID of the sent message on success.
+func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) {
+ params, err := config.params()
+ if err != nil {
+ return MessageID{}, err
+ }
+
+ resp, err := bot.MakeRequest(config.method(), params)
+ if err != nil {
+ return MessageID{}, err
+ }
+
+ var messageID MessageID
+ err = json.Unmarshal(resp.Result, &messageID)
+
+ return messageID, err
+}
+
+// EscapeText takes an input text and escape Telegram markup symbols.
+// In this way we can send a text without being afraid of having to escape the characters manually.
+// Note that you don't have to include the formatting style in the input text, or it will be escaped too.
+// If there is an error, an empty string will be returned.
+//
+// parseMode is the text formatting mode (ModeMarkdown, ModeMarkdownV2 or ModeHTML)
+// text is the input string that will be escaped
+func EscapeText(parseMode string, text string) string {
+ var replacer *strings.Replacer
+
+ if parseMode == ModeHTML {
+ replacer = strings.NewReplacer("<", "&lt;", ">", "&gt;", "&", "&amp;")
+ } else if parseMode == ModeMarkdown {
+ replacer = strings.NewReplacer("_", "\\_", "*", "\\*", "`", "\\`", "[", "\\[")
+ } else if parseMode == ModeMarkdownV2 {
+ replacer = strings.NewReplacer(
+ "_", "\\_", "*", "\\*", "[", "\\[", "]", "\\]", "(",
+ "\\(", ")", "\\)", "~", "\\~", "`", "\\`", ">", "\\>",
+ "#", "\\#", "+", "\\+", "-", "\\-", "=", "\\=", "|",
+ "\\|", "{", "\\{", "}", "\\}", ".", "\\.", "!", "\\!",
+ )
+ } else {
+ return ""
+ }
+
+ return replacer.Replace(text)
+}