diff options
Diffstat (limited to 'vendor/github.com/go-telegram-bot-api')
15 files changed, 6881 insertions, 3545 deletions
diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/.travis.yml b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/.travis.yml deleted file mode 100644 index 51865c84..00000000 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go - -go: - - '1.10' - - '1.11' - - '1.12' - - '1.13' - - tip diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go deleted file mode 100644 index e6c2cc83..00000000 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/bot.go +++ /dev/null @@ -1,1014 +0,0 @@ -// Package tgbotapi has functions and types used for interacting with -// the Telegram Bot API. -package tgbotapi - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "os" - "strconv" - "strings" - "time" - - "github.com/technoweenie/multipartstreamer" -) - -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 -} - -func (b *BotAPI) SetAPIEndpoint(apiEndpoint string) { - b.apiEndpoint = apiEndpoint -} - -// MakeRequest makes a request to a specific endpoint with our token. -func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) { - method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint) - - req, err := http.NewRequest("POST", method, strings.NewReader(params.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 APIResponse{}, 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("%s resp: %s", endpoint, bytes) - } - - if !apiResp.Ok { - 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, err error) { - if !bot.Debug { - dec := json.NewDecoder(responseBody) - err = dec.Decode(resp) - return - } - - // if debug, read reponse body - data, err := ioutil.ReadAll(responseBody) - if err != nil { - return - } - - err = json.Unmarshal(data, resp) - if err != nil { - return - } - - return data, 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 { - return Message{}, err - } - - var message Message - json.Unmarshal(resp.Result, &message) - - bot.debugLog(endpoint, params, message) - - return message, nil -} - -// 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, -// a FileReader struct, or a url.URL. -// -// 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() - - switch f := file.(type) { - case string: - ms.WriteFields(params) - - fileHandle, err := os.Open(f) - if err != nil { - return APIResponse{}, err - } - defer fileHandle.Close() - - fi, err := os.Stat(f) - if err != nil { - return APIResponse{}, err - } - - ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle) - case FileBytes: - ms.WriteFields(params) - - buf := bytes.NewBuffer(f.Bytes) - ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf) - case FileReader: - ms.WriteFields(params) - - if f.Size != -1 { - ms.WriteReader(fieldname, f.Name, f.Size, f.Reader) - - break - } - - data, err := ioutil.ReadAll(f.Reader) - if err != nil { - return APIResponse{}, err - } - - buf := bytes.NewBuffer(data) - - ms.WriteReader(fieldname, f.Name, int64(len(data)), buf) - case url.URL: - params[fieldname] = f.String() - - ms.WriteFields(params) - default: - return APIResponse{}, errors.New(ErrBadFileType) - } - - method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint) - - req, err := http.NewRequest("POST", method, nil) - if err != nil { - return APIResponse{}, err - } - - ms.SetupRequest(req) - - res, err := bot.Client.Do(req) - if err != nil { - return APIResponse{}, err - } - defer res.Body.Close() - - bytes, err := ioutil.ReadAll(res.Body) - if err != nil { - return APIResponse{}, err - } - - if bot.Debug { - log.Println(string(bytes)) - } - - var apiResp APIResponse - - err = json.Unmarshal(bytes, &apiResp) - if err != nil { - return APIResponse{}, err - } - - if !apiResp.Ok { - return APIResponse{}, errors.New(apiResp.Description) - } - - 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 - json.Unmarshal(resp.Result, &user) - - bot.debugLog("getMe", nil, user) - - return user, nil -} - -// 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) -} - -// Send will send a Chattable item to Telegram. -// -// It requires the Chattable to send. -func (bot *BotAPI) Send(c Chattable) (Message, error) { - switch c.(type) { - case Fileable: - return bot.sendFile(c.(Fileable)) - default: - 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) - 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() - - if err != nil { - return Message{}, err - } - - message, err := bot.makeMessageRequest(method, v) - if err != nil { - return Message{}, err - } - - 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() - if err != nil { - return Message{}, err - } - - file := config.getFile() - - resp, err := bot.UploadFile(method, params, config.name(), file) - if err != nil { - return Message{}, err - } - - var message Message - json.Unmarshal(resp.Result, &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) - } - - return bot.uploadAndSend(config.method(), config) -} - -// sendChattable sends a Chattable. -func (bot *BotAPI) sendChattable(config Chattable) (Message, error) { - v, err := config.values() - if err != nil { - return Message{}, err - } - - message, err := bot.makeMessageRequest(config.method(), v) - - if err != nil { - return Message{}, err - } - - return message, nil -} - -// GetUserProfilePhotos gets a user's profile photos. -// -// It requires UserID. -// Offset and Limit are optional. -func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) { - v := url.Values{} - v.Add("user_id", strconv.Itoa(config.UserID)) - if config.Offset != 0 { - v.Add("offset", strconv.Itoa(config.Offset)) - } - if config.Limit != 0 { - v.Add("limit", strconv.Itoa(config.Limit)) - } - - resp, err := bot.MakeRequest("getUserProfilePhotos", v) - if err != nil { - return UserProfilePhotos{}, err - } - - var profilePhotos UserProfilePhotos - json.Unmarshal(resp.Result, &profilePhotos) - - bot.debugLog("GetUserProfilePhoto", v, profilePhotos) - - return profilePhotos, nil -} - -// GetFile returns a File which can download a file from Telegram. -// -// Requires FileID. -func (bot *BotAPI) GetFile(config FileConfig) (File, error) { - v := url.Values{} - v.Add("file_id", config.FileID) - - resp, err := bot.MakeRequest("getFile", v) - if err != nil { - return File{}, err - } - - var file File - json.Unmarshal(resp.Result, &file) - - bot.debugLog("GetFile", v, file) - - return file, nil -} - -// GetUpdates fetches updates. -// If a WebHook is set, this will not return any data! -// -// Offset, Limit, and Timeout 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) { - v := url.Values{} - if config.Offset != 0 { - v.Add("offset", strconv.Itoa(config.Offset)) - } - if config.Limit > 0 { - v.Add("limit", strconv.Itoa(config.Limit)) - } - if config.Timeout > 0 { - v.Add("timeout", strconv.Itoa(config.Timeout)) - } - - resp, err := bot.MakeRequest("getUpdates", v) - if err != nil { - return []Update{}, err - } - - var updates []Update - json.Unmarshal(resp.Result, &updates) - - bot.debugLog("getUpdates", v, updates) - - return updates, nil -} - -// RemoveWebhook unsets the webhook. -func (bot *BotAPI) RemoveWebhook() (APIResponse, error) { - return bot.MakeRequest("deleteWebhook", url.Values{}) -} - -// SetWebhook sets a webhook. -// -// If this is set, GetUpdates will not get any data! -// -// If you do not have a legitimate 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{} - v.Add("url", config.URL.String()) - if config.MaxConnections != 0 { - v.Add("max_connections", strconv.Itoa(config.MaxConnections)) - } - - return bot.MakeRequest("setWebhook", v) - } - - params := make(map[string]string) - params["url"] = config.URL.String() - if config.MaxConnections != 0 { - params["max_connections"] = strconv.Itoa(config.MaxConnections) - } - - resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate) - if err != nil { - return APIResponse{}, err - } - - return resp, nil -} - -// 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", url.Values{}) - 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, error) { - 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, nil -} - -// 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) { - bytes, _ := ioutil.ReadAll(r.Body) - r.Body.Close() - - var update Update - json.Unmarshal(bytes, &update) - - ch <- update - }) - - return ch -} - -// 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{} - - v.Add("inline_query_id", config.InlineQueryID) - v.Add("cache_time", strconv.Itoa(config.CacheTime)) - v.Add("is_personal", strconv.FormatBool(config.IsPersonal)) - v.Add("next_offset", config.NextOffset) - data, err := json.Marshal(config.Results) - if err != nil { - return APIResponse{}, err - } - v.Add("results", string(data)) - v.Add("switch_pm_text", config.SwitchPMText) - v.Add("switch_pm_parameter", config.SwitchPMParameter) - - bot.debugLog("answerInlineQuery", v, nil) - - return bot.MakeRequest("answerInlineQuery", v) -} - -// AnswerCallbackQuery sends a response to an inline query callback. -func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, error) { - v := url.Values{} - - v.Add("callback_query_id", config.CallbackQueryID) - if config.Text != "" { - v.Add("text", config.Text) - } - v.Add("show_alert", strconv.FormatBool(config.ShowAlert)) - if config.URL != "" { - v.Add("url", config.URL) - } - v.Add("cache_time", strconv.Itoa(config.CacheTime)) - - bot.debugLog("answerCallbackQuery", v, nil) - - return bot.MakeRequest("answerCallbackQuery", v) -} - -// KickChatMember kicks a user from a chat. Note that this only will work -// in supergroups, and requires the bot to be an admin. Also note they -// will be unable to rejoin until they are unbanned. -func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - v.Add("user_id", strconv.Itoa(config.UserID)) - - if config.UntilDate != 0 { - v.Add("until_date", strconv.FormatInt(config.UntilDate, 10)) - } - - bot.debugLog("kickChatMember", v, nil) - - return bot.MakeRequest("kickChatMember", v) -} - -// LeaveChat makes the bot leave the chat. -func (bot *BotAPI) LeaveChat(config ChatConfig) (APIResponse, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - - bot.debugLog("leaveChat", v, nil) - - return bot.MakeRequest("leaveChat", v) -} - -// GetChat gets information about a chat. -func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - - resp, err := bot.MakeRequest("getChat", v) - if err != nil { - return Chat{}, err - } - - var chat Chat - err = json.Unmarshal(resp.Result, &chat) - - bot.debugLog("getChat", v, 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 ChatConfig) ([]ChatMember, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - - resp, err := bot.MakeRequest("getChatAdministrators", v) - if err != nil { - return []ChatMember{}, err - } - - var members []ChatMember - err = json.Unmarshal(resp.Result, &members) - - bot.debugLog("getChatAdministrators", v, members) - - return members, err -} - -// GetChatMembersCount gets the number of users in a chat. -func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - - resp, err := bot.MakeRequest("getChatMembersCount", v) - if err != nil { - return -1, err - } - - var count int - err = json.Unmarshal(resp.Result, &count) - - bot.debugLog("getChatMembersCount", v, count) - - return count, err -} - -// GetChatMember gets a specific chat member. -func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - v.Add("user_id", strconv.Itoa(config.UserID)) - - resp, err := bot.MakeRequest("getChatMember", v) - if err != nil { - return ChatMember{}, err - } - - var member ChatMember - err = json.Unmarshal(resp.Result, &member) - - bot.debugLog("getChatMember", v, member) - - return member, err -} - -// UnbanChatMember unbans a user from a chat. Note that this only will work -// in supergroups and channels, and requires the bot to be an admin. -func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) { - v := url.Values{} - - if config.SuperGroupUsername != "" { - v.Add("chat_id", config.SuperGroupUsername) - } else if config.ChannelUsername != "" { - v.Add("chat_id", config.ChannelUsername) - } else { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } - v.Add("user_id", strconv.Itoa(config.UserID)) - - bot.debugLog("unbanChatMember", v, nil) - - return bot.MakeRequest("unbanChatMember", v) -} - -// RestrictChatMember to restrict a user in a supergroup. The bot must be an -//administrator in the supergroup for this to work and must have the -//appropriate admin rights. Pass True for all boolean parameters to lift -//restrictions from a user. Returns True on success. -func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) { - v := url.Values{} - - if config.SuperGroupUsername != "" { - v.Add("chat_id", config.SuperGroupUsername) - } else if config.ChannelUsername != "" { - v.Add("chat_id", config.ChannelUsername) - } else { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } - v.Add("user_id", strconv.Itoa(config.UserID)) - - if config.CanSendMessages != nil { - v.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages)) - } - if config.CanSendMediaMessages != nil { - v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages)) - } - if config.CanSendOtherMessages != nil { - v.Add("can_send_other_messages", strconv.FormatBool(*config.CanSendOtherMessages)) - } - if config.CanAddWebPagePreviews != nil { - v.Add("can_add_web_page_previews", strconv.FormatBool(*config.CanAddWebPagePreviews)) - } - if config.UntilDate != 0 { - v.Add("until_date", strconv.FormatInt(config.UntilDate, 10)) - } - - bot.debugLog("restrictChatMember", v, nil) - - return bot.MakeRequest("restrictChatMember", v) -} - -// PromoteChatMember add admin rights to user -func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) { - v := url.Values{} - - if config.SuperGroupUsername != "" { - v.Add("chat_id", config.SuperGroupUsername) - } else if config.ChannelUsername != "" { - v.Add("chat_id", config.ChannelUsername) - } else { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } - v.Add("user_id", strconv.Itoa(config.UserID)) - - if config.CanChangeInfo != nil { - v.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo)) - } - if config.CanPostMessages != nil { - v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages)) - } - if config.CanEditMessages != nil { - v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages)) - } - if config.CanDeleteMessages != nil { - v.Add("can_delete_messages", strconv.FormatBool(*config.CanDeleteMessages)) - } - if config.CanInviteUsers != nil { - v.Add("can_invite_users", strconv.FormatBool(*config.CanInviteUsers)) - } - if config.CanRestrictMembers != nil { - v.Add("can_restrict_members", strconv.FormatBool(*config.CanRestrictMembers)) - } - if config.CanPinMessages != nil { - v.Add("can_pin_messages", strconv.FormatBool(*config.CanPinMessages)) - } - if config.CanPromoteMembers != nil { - v.Add("can_promote_members", strconv.FormatBool(*config.CanPromoteMembers)) - } - - bot.debugLog("promoteChatMember", v, nil) - - return bot.MakeRequest("promoteChatMember", v) -} - -// GetGameHighScores allows you to get the high scores for a game. -func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) { - v, _ := config.values() - - resp, err := bot.MakeRequest(config.method(), v) - if err != nil { - return []GameHighScore{}, err - } - - var highScores []GameHighScore - err = json.Unmarshal(resp.Result, &highScores) - - return highScores, err -} - -// AnswerShippingQuery allows you to reply to Update with shipping_query parameter. -func (bot *BotAPI) AnswerShippingQuery(config ShippingConfig) (APIResponse, error) { - v := url.Values{} - - v.Add("shipping_query_id", config.ShippingQueryID) - v.Add("ok", strconv.FormatBool(config.OK)) - if config.OK == true { - data, err := json.Marshal(config.ShippingOptions) - if err != nil { - return APIResponse{}, err - } - v.Add("shipping_options", string(data)) - } else { - v.Add("error_message", config.ErrorMessage) - } - - bot.debugLog("answerShippingQuery", v, nil) - - return bot.MakeRequest("answerShippingQuery", v) -} - -// AnswerPreCheckoutQuery allows you to reply to Update with pre_checkout_query. -func (bot *BotAPI) AnswerPreCheckoutQuery(config PreCheckoutConfig) (APIResponse, error) { - v := url.Values{} - - v.Add("pre_checkout_query_id", config.PreCheckoutQueryID) - v.Add("ok", strconv.FormatBool(config.OK)) - if config.OK != true { - v.Add("error", config.ErrorMessage) - } - - bot.debugLog("answerPreCheckoutQuery", v, nil) - - return bot.MakeRequest("answerPreCheckoutQuery", v) -} - -// DeleteMessage deletes a message in a chat -func (bot *BotAPI) DeleteMessage(config DeleteMessageConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - bot.debugLog(config.method(), v, nil) - - return bot.MakeRequest(config.method(), v) -} - -// GetInviteLink get InviteLink for a chat -func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } - - resp, err := bot.MakeRequest("exportChatInviteLink", v) - if err != nil { - return "", err - } - - var inviteLink string - err = json.Unmarshal(resp.Result, &inviteLink) - - return inviteLink, err -} - -// PinChatMessage pin message in supergroup -func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - bot.debugLog(config.method(), v, nil) - - return bot.MakeRequest(config.method(), v) -} - -// UnpinChatMessage unpin message in supergroup -func (bot *BotAPI) UnpinChatMessage(config UnpinChatMessageConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - bot.debugLog(config.method(), v, nil) - - return bot.MakeRequest(config.method(), v) -} - -// SetChatTitle change title of chat. -func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - bot.debugLog(config.method(), v, nil) - - return bot.MakeRequest(config.method(), v) -} - -// SetChatDescription change description of chat. -func (bot *BotAPI) SetChatDescription(config SetChatDescriptionConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - bot.debugLog(config.method(), v, nil) - - return bot.MakeRequest(config.method(), v) -} - -// SetChatPhoto change photo of chat. -func (bot *BotAPI) SetChatPhoto(config SetChatPhotoConfig) (APIResponse, error) { - params, err := config.params() - if err != nil { - return APIResponse{}, err - } - - file := config.getFile() - - return bot.UploadFile(config.method(), params, config.name(), file) -} - -// DeleteChatPhoto delete photo of chat. -func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - bot.debugLog(config.method(), v, nil) - - return bot.MakeRequest(config.method(), v) -} - -// GetStickerSet get a sticker set. -func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { - v, err := config.values() - if err != nil { - return StickerSet{}, err - } - bot.debugLog(config.method(), v, nil) - res, err := bot.MakeRequest(config.method(), v) - if err != nil { - return StickerSet{}, err - } - stickerSet := StickerSet{} - err = json.Unmarshal(res.Result, &stickerSet) - if err != nil { - return StickerSet{}, err - } - return stickerSet, nil -} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go deleted file mode 100644 index a7052ddc..00000000 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/configs.go +++ /dev/null @@ -1,1279 +0,0 @@ -package tgbotapi - -import ( - "encoding/json" - "io" - "net/url" - "strconv" -) - -// Telegram constants -const ( - // 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 = "https://api.telegram.org/file/bot%s/%s" -) - -// Constant values for ChatActions -const ( - ChatTyping = "typing" - ChatUploadPhoto = "upload_photo" - ChatRecordVideo = "record_video" - ChatUploadVideo = "upload_video" - ChatRecordAudio = "record_audio" - ChatUploadAudio = "upload_audio" - ChatUploadDocument = "upload_document" - ChatFindLocation = "find_location" -) - -// API errors -const ( - // ErrAPIForbidden happens when a token is bad - ErrAPIForbidden = "forbidden" -) - -// Constant values for ParseMode in MessageConfig -const ( - ModeMarkdown = "Markdown" - ModeHTML = "HTML" -) - -// Library errors -const ( - // ErrBadFileType happens when you pass an unknown type - ErrBadFileType = "bad file type" - ErrBadURL = "bad or empty url" -) - -// Chattable is any config type that can be sent. -type Chattable interface { - values() (url.Values, error) - method() string -} - -// 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 -} - -// BaseChat is base type for all chat config types. -type BaseChat struct { - ChatID int64 // required - ChannelUsername string - ReplyToMessageID int - ReplyMarkup interface{} - DisableNotification bool -} - -// 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) - } else { - v.Add("chat_id", strconv.FormatInt(chat.ChatID, 10)) - } - - if chat.ReplyToMessageID != 0 { - v.Add("reply_to_message_id", strconv.Itoa(chat.ReplyToMessageID)) - } - - if chat.ReplyMarkup != nil { - data, err := json.Marshal(chat.ReplyMarkup) - if err != nil { - return v, err - } - - v.Add("reply_markup", string(data)) - } - - v.Add("disable_notification", strconv.FormatBool(chat.DisableNotification)) - - return v, nil -} - -// BaseFile is a base type for all file config types. -type BaseFile struct { - BaseChat - File interface{} - FileID string - UseExisting bool - MimeType string - FileSize int -} - -// 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 != "" { - params["chat_id"] = file.ChannelUsername - } else { - params["chat_id"] = strconv.FormatInt(file.ChatID, 10) - } - - if file.ReplyToMessageID != 0 { - params["reply_to_message_id"] = strconv.Itoa(file.ReplyToMessageID) - } - - if file.ReplyMarkup != nil { - data, err := json.Marshal(file.ReplyMarkup) - if err != nil { - return params, err - } - - params["reply_markup"] = string(data) - } - - if file.MimeType != "" { - params["mime_type"] = file.MimeType - } - - if file.FileSize > 0 { - params["file_size"] = strconv.Itoa(file.FileSize) - } - - params["disable_notification"] = strconv.FormatBool(file.DisableNotification) - - return params, nil -} - -// getFile returns the file. -func (file BaseFile) getFile() interface{} { - return file.File -} - -// useExistingFile returns if the BaseFile has already been uploaded. -func (file BaseFile) useExistingFile() bool { - return file.UseExisting -} - -// BaseEdit is base type of all chat edits. -type BaseEdit struct { - ChatID int64 - ChannelUsername string - MessageID int - InlineMessageID string - ReplyMarkup *InlineKeyboardMarkup -} - -func (edit BaseEdit) values() (url.Values, error) { - v := url.Values{} - - if edit.InlineMessageID == "" { - if edit.ChannelUsername != "" { - v.Add("chat_id", edit.ChannelUsername) - } else { - v.Add("chat_id", strconv.FormatInt(edit.ChatID, 10)) - } - v.Add("message_id", strconv.Itoa(edit.MessageID)) - } else { - v.Add("inline_message_id", edit.InlineMessageID) - } - - if edit.ReplyMarkup != nil { - data, err := json.Marshal(edit.ReplyMarkup) - if err != nil { - return v, err - } - v.Add("reply_markup", string(data)) - } - - return v, nil -} - -// MessageConfig contains information about a SendMessage request. -type MessageConfig struct { - BaseChat - Text string - ParseMode string - DisableWebPagePreview bool -} - -// values returns a url.Values representation of MessageConfig. -func (config MessageConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - v.Add("text", config.Text) - v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview)) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - - return v, nil -} - -// 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 int64 // required - FromChannelUsername string - MessageID int // required -} - -// values returns a url.Values representation of ForwardConfig. -func (config ForwardConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - v.Add("from_chat_id", strconv.FormatInt(config.FromChatID, 10)) - 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 { - return "forwardMessage" -} - -// PhotoConfig contains information about a SendPhoto request. -type PhotoConfig struct { - BaseFile - Caption string - ParseMode string -} - -// 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 - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } - - return params, nil -} - -// Values returns a url.Values representation of PhotoConfig. -func (config PhotoConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } - - return v, nil -} - -// 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" -} - -// AudioConfig contains information about a SendAudio request. -type AudioConfig struct { - BaseFile - Caption string - ParseMode string - Duration int - Performer string - Title string -} - -// values returns a url.Values representation of AudioConfig. -func (config AudioConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Duration != 0 { - v.Add("duration", strconv.Itoa(config.Duration)) - } - - if config.Performer != "" { - v.Add("performer", config.Performer) - } - if config.Title != "" { - v.Add("title", config.Title) - } - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } - - return v, nil -} - -// 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) - } - - if config.Performer != "" { - params["performer"] = config.Performer - } - if config.Title != "" { - params["title"] = config.Title - } - if config.Caption != "" { - params["caption"] = config.Caption - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } - - return params, nil -} - -// 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" -} - -// DocumentConfig contains information about a SendDocument request. -type DocumentConfig struct { - BaseFile - Caption string - ParseMode string -} - -// values returns a url.Values representation of DocumentConfig. -func (config DocumentConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } - - return v, nil -} - -// params returns a map[string]string representation of DocumentConfig. -func (config DocumentConfig) params() (map[string]string, error) { - params, _ := config.BaseFile.params() - - if config.Caption != "" { - params["caption"] = config.Caption - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } - - return params, nil -} - -// 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 { - return "sendDocument" -} - -// StickerConfig contains information about a SendSticker request. -type StickerConfig struct { - BaseFile -} - -// values returns a url.Values representation of StickerConfig. -func (config StickerConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - - return v, nil -} - -// 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 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 { - return "sendSticker" -} - -// VideoConfig contains information about a SendVideo request. -type VideoConfig struct { - BaseFile - Duration int - Caption string - ParseMode string -} - -// values returns a url.Values representation of VideoConfig. -func (config VideoConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Duration != 0 { - v.Add("duration", strconv.Itoa(config.Duration)) - } - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } - - return v, nil -} - -// params returns a map[string]string representation of VideoConfig. -func (config VideoConfig) params() (map[string]string, error) { - params, _ := config.BaseFile.params() - - if config.Caption != "" { - params["caption"] = config.Caption - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } - - return params, nil -} - -// 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 { - return "sendVideo" -} - -// AnimationConfig contains information about a SendAnimation request. -type AnimationConfig struct { - BaseFile - Duration int - Caption string - ParseMode string -} - -// values returns a url.Values representation of AnimationConfig. -func (config AnimationConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Duration != 0 { - v.Add("duration", strconv.Itoa(config.Duration)) - } - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } - - return v, nil -} - -// params returns a map[string]string representation of AnimationConfig. -func (config AnimationConfig) params() (map[string]string, error) { - params, _ := config.BaseFile.params() - - if config.Caption != "" { - params["caption"] = config.Caption - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } - - return params, nil -} - -// name returns the field name for the Animation. -func (config AnimationConfig) name() string { - return "animation" -} - -// method returns Telegram API method name for sending Animation. -func (config AnimationConfig) method() string { - return "sendAnimation" -} - -// VideoNoteConfig contains information about a SendVideoNote request. -type VideoNoteConfig struct { - BaseFile - Duration int - Length int -} - -// values returns a url.Values representation of VideoNoteConfig. -func (config VideoNoteConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Duration != 0 { - v.Add("duration", strconv.Itoa(config.Duration)) - } - - // Telegram API seems to have a bug, if no length is provided or it is 0, it will send an error response - if config.Length != 0 { - v.Add("length", strconv.Itoa(config.Length)) - } - - return v, nil -} - -// params returns a map[string]string representation of VideoNoteConfig. -func (config VideoNoteConfig) params() (map[string]string, error) { - params, _ := config.BaseFile.params() - - if config.Length != 0 { - params["length"] = strconv.Itoa(config.Length) - } - if config.Duration != 0 { - params["duration"] = strconv.Itoa(config.Duration) - } - - return params, nil -} - -// name returns the field name for the VideoNote. -func (config VideoNoteConfig) name() string { - return "video_note" -} - -// method returns Telegram API method name for sending VideoNote. -func (config VideoNoteConfig) method() string { - return "sendVideoNote" -} - -// VoiceConfig contains information about a SendVoice request. -type VoiceConfig struct { - BaseFile - Caption string - ParseMode string - Duration int -} - -// values returns a url.Values representation of VoiceConfig. -func (config VoiceConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add(config.name(), config.FileID) - if config.Duration != 0 { - v.Add("duration", strconv.Itoa(config.Duration)) - } - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } - - return v, nil -} - -// 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) - } - if config.Caption != "" { - params["caption"] = config.Caption - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } - - return params, nil -} - -// 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 { - return "sendVoice" -} - -// MediaGroupConfig contains information about a sendMediaGroup request. -type MediaGroupConfig struct { - BaseChat - InputMedia []interface{} -} - -func (config MediaGroupConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - data, err := json.Marshal(config.InputMedia) - if err != nil { - return v, err - } - - v.Add("media", string(data)) - - return v, nil -} - -func (config MediaGroupConfig) method() string { - return "sendMediaGroup" -} - -// LocationConfig contains information about a SendLocation request. -type LocationConfig struct { - BaseChat - Latitude float64 // required - Longitude float64 // required -} - -// values returns a url.Values representation of LocationConfig. -func (config LocationConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64)) - v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64)) - - return v, nil -} - -// method returns Telegram API method name for sending Location. -func (config LocationConfig) method() string { - return "sendLocation" -} - -// VenueConfig contains information about a SendVenue request. -type VenueConfig struct { - BaseChat - Latitude float64 // required - Longitude float64 // required - Title string // required - Address string // required - FoursquareID string -} - -func (config VenueConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64)) - v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64)) - v.Add("title", config.Title) - v.Add("address", config.Address) - if config.FoursquareID != "" { - v.Add("foursquare_id", config.FoursquareID) - } - - return v, nil -} - -func (config VenueConfig) method() string { - return "sendVenue" -} - -// ContactConfig allows you to send a contact. -type ContactConfig struct { - BaseChat - PhoneNumber string - FirstName string - LastName string -} - -func (config ContactConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add("phone_number", config.PhoneNumber) - v.Add("first_name", config.FirstName) - v.Add("last_name", config.LastName) - - return v, nil -} - -func (config ContactConfig) method() string { - return "sendContact" -} - -// GameConfig allows you to send a game. -type GameConfig struct { - BaseChat - GameShortName string -} - -func (config GameConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - - v.Add("game_short_name", config.GameShortName) - - return v, nil -} - -func (config GameConfig) method() string { - return "sendGame" -} - -// SetGameScoreConfig allows you to update the game score in a chat. -type SetGameScoreConfig struct { - UserID int - Score int - Force bool - DisableEditMessage bool - ChatID int64 - ChannelUsername string - MessageID int - InlineMessageID string -} - -func (config SetGameScoreConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("user_id", strconv.Itoa(config.UserID)) - v.Add("score", strconv.Itoa(config.Score)) - if config.InlineMessageID == "" { - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } - v.Add("message_id", strconv.Itoa(config.MessageID)) - } else { - v.Add("inline_message_id", config.InlineMessageID) - } - v.Add("disable_edit_message", strconv.FormatBool(config.DisableEditMessage)) - - return v, nil -} - -func (config SetGameScoreConfig) method() string { - return "setGameScore" -} - -// GetGameHighScoresConfig allows you to fetch the high scores for a game. -type GetGameHighScoresConfig struct { - UserID int - ChatID int - ChannelUsername string - MessageID int - InlineMessageID string -} - -func (config GetGameHighScoresConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("user_id", strconv.Itoa(config.UserID)) - if config.InlineMessageID == "" { - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.Itoa(config.ChatID)) - } else { - v.Add("chat_id", config.ChannelUsername) - } - v.Add("message_id", strconv.Itoa(config.MessageID)) - } else { - v.Add("inline_message_id", config.InlineMessageID) - } - - return v, nil -} - -func (config GetGameHighScoresConfig) method() string { - return "getGameHighScores" -} - -// ChatActionConfig contains information about a SendChatAction request. -type ChatActionConfig struct { - BaseChat - Action string // required -} - -// values returns a url.Values representation of ChatActionConfig. -func (config ChatActionConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - v.Add("action", config.Action) - return v, nil -} - -// method returns Telegram API method name for sending ChatAction. -func (config ChatActionConfig) method() string { - return "sendChatAction" -} - -// EditMessageTextConfig allows you to modify the text in a message. -type EditMessageTextConfig struct { - BaseEdit - Text string - ParseMode string - DisableWebPagePreview bool -} - -func (config EditMessageTextConfig) values() (url.Values, error) { - v, err := config.BaseEdit.values() - if err != nil { - return v, err - } - - v.Add("text", config.Text) - v.Add("parse_mode", config.ParseMode) - v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview)) - - return v, nil -} - -func (config EditMessageTextConfig) method() string { - return "editMessageText" -} - -// EditMessageCaptionConfig allows you to modify the caption of a message. -type EditMessageCaptionConfig struct { - BaseEdit - Caption string - ParseMode string -} - -func (config EditMessageCaptionConfig) values() (url.Values, error) { - v, _ := config.BaseEdit.values() - - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - - return v, nil -} - -func (config EditMessageCaptionConfig) method() string { - return "editMessageCaption" -} - -// EditMessageReplyMarkupConfig allows you to modify the reply markup -// of a message. -type EditMessageReplyMarkupConfig struct { - BaseEdit -} - -func (config EditMessageReplyMarkupConfig) values() (url.Values, error) { - return config.BaseEdit.values() -} - -func (config EditMessageReplyMarkupConfig) method() string { - return "editMessageReplyMarkup" -} - -// 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. -type FileConfig struct { - FileID string -} - -// UpdateConfig contains information about a GetUpdates request. -type UpdateConfig struct { - Offset int - Limit int - Timeout int -} - -// WebhookConfig contains information about a SetWebhook request. -type WebhookConfig struct { - URL *url.URL - Certificate interface{} - MaxConnections int -} - -// 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. -type FileReader struct { - Name string - Reader io.Reader - Size int64 -} - -// InlineConfig contains information on making an InlineQuery response. -type InlineConfig struct { - InlineQueryID string `json:"inline_query_id"` - Results []interface{} `json:"results"` - CacheTime int `json:"cache_time"` - IsPersonal bool `json:"is_personal"` - NextOffset string `json:"next_offset"` - SwitchPMText string `json:"switch_pm_text"` - SwitchPMParameter string `json:"switch_pm_parameter"` -} - -// CallbackConfig contains information on making a CallbackQuery response. -type CallbackConfig struct { - CallbackQueryID string `json:"callback_query_id"` - Text string `json:"text"` - ShowAlert bool `json:"show_alert"` - URL string `json:"url"` - CacheTime int `json:"cache_time"` -} - -// ChatMemberConfig contains information about a user in a chat for use -// with administrative functions such as kicking or unbanning a user. -type ChatMemberConfig struct { - ChatID int64 - SuperGroupUsername string - ChannelUsername string - UserID int -} - -// KickChatMemberConfig contains extra fields to kick user -type KickChatMemberConfig struct { - ChatMemberConfig - UntilDate int64 -} - -// RestrictChatMemberConfig contains fields to restrict members of chat -type RestrictChatMemberConfig struct { - ChatMemberConfig - UntilDate int64 - CanSendMessages *bool - CanSendMediaMessages *bool - CanSendOtherMessages *bool - CanAddWebPagePreviews *bool -} - -// PromoteChatMemberConfig contains fields to promote members of chat -type PromoteChatMemberConfig struct { - ChatMemberConfig - CanChangeInfo *bool - CanPostMessages *bool - CanEditMessages *bool - CanDeleteMessages *bool - CanInviteUsers *bool - CanRestrictMembers *bool - CanPinMessages *bool - CanPromoteMembers *bool -} - -// ChatConfig contains information about getting information on a chat. -type ChatConfig struct { - ChatID int64 - SuperGroupUsername string -} - -// ChatConfigWithUser contains information about getting information on -// a specific user within a chat. -type ChatConfigWithUser struct { - ChatID int64 - SuperGroupUsername string - UserID int -} - -// InvoiceConfig contains information for sendInvoice request. -type InvoiceConfig struct { - BaseChat - Title string // required - Description string // required - Payload string // required - ProviderToken string // required - StartParameter string // required - Currency string // required - Prices *[]LabeledPrice // required - PhotoURL string - PhotoSize int - PhotoWidth int - PhotoHeight int - NeedName bool - NeedPhoneNumber bool - NeedEmail bool - NeedShippingAddress bool - IsFlexible bool -} - -func (config InvoiceConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } - v.Add("title", config.Title) - v.Add("description", config.Description) - v.Add("payload", config.Payload) - v.Add("provider_token", config.ProviderToken) - v.Add("start_parameter", config.StartParameter) - v.Add("currency", config.Currency) - data, err := json.Marshal(config.Prices) - if err != nil { - return v, err - } - v.Add("prices", string(data)) - if config.PhotoURL != "" { - v.Add("photo_url", config.PhotoURL) - } - if config.PhotoSize != 0 { - v.Add("photo_size", strconv.Itoa(config.PhotoSize)) - } - if config.PhotoWidth != 0 { - v.Add("photo_width", strconv.Itoa(config.PhotoWidth)) - } - if config.PhotoHeight != 0 { - v.Add("photo_height", strconv.Itoa(config.PhotoHeight)) - } - if config.NeedName != false { - v.Add("need_name", strconv.FormatBool(config.NeedName)) - } - if config.NeedPhoneNumber != false { - v.Add("need_phone_number", strconv.FormatBool(config.NeedPhoneNumber)) - } - if config.NeedEmail != false { - v.Add("need_email", strconv.FormatBool(config.NeedEmail)) - } - if config.NeedShippingAddress != false { - v.Add("need_shipping_address", strconv.FormatBool(config.NeedShippingAddress)) - } - if config.IsFlexible != false { - v.Add("is_flexible", strconv.FormatBool(config.IsFlexible)) - } - - return v, nil -} - -func (config InvoiceConfig) method() string { - return "sendInvoice" -} - -// ShippingConfig contains information for answerShippingQuery request. -type ShippingConfig struct { - ShippingQueryID string // required - OK bool // required - ShippingOptions *[]ShippingOption - ErrorMessage string -} - -// PreCheckoutConfig conatins information for answerPreCheckoutQuery request. -type PreCheckoutConfig struct { - PreCheckoutQueryID string // required - OK bool // required - ErrorMessage string -} - -// DeleteMessageConfig contains information of a message in a chat to delete. -type DeleteMessageConfig struct { - ChatID int64 - MessageID int -} - -func (config DeleteMessageConfig) method() string { - return "deleteMessage" -} - -func (config DeleteMessageConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - v.Add("message_id", strconv.Itoa(config.MessageID)) - - return v, nil -} - -// PinChatMessageConfig contains information of a message in a chat to pin. -type PinChatMessageConfig struct { - ChatID int64 - MessageID int - DisableNotification bool -} - -func (config PinChatMessageConfig) method() string { - return "pinChatMessage" -} - -func (config PinChatMessageConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - v.Add("message_id", strconv.Itoa(config.MessageID)) - v.Add("disable_notification", strconv.FormatBool(config.DisableNotification)) - - return v, nil -} - -// UnpinChatMessageConfig contains information of chat to unpin. -type UnpinChatMessageConfig struct { - ChatID int64 -} - -func (config UnpinChatMessageConfig) method() string { - return "unpinChatMessage" -} - -func (config UnpinChatMessageConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - - return v, nil -} - -// SetChatTitleConfig contains information for change chat title. -type SetChatTitleConfig struct { - ChatID int64 - Title string -} - -func (config SetChatTitleConfig) method() string { - return "setChatTitle" -} - -func (config SetChatTitleConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - v.Add("title", config.Title) - - return v, nil -} - -// SetChatDescriptionConfig contains information for change chat description. -type SetChatDescriptionConfig struct { - ChatID int64 - Description string -} - -func (config SetChatDescriptionConfig) method() string { - return "setChatDescription" -} - -func (config SetChatDescriptionConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - v.Add("description", config.Description) - - return v, nil -} - -// SetChatPhotoConfig contains information for change chat photo -type SetChatPhotoConfig struct { - BaseFile -} - -// name returns the field name for the Photo. -func (config SetChatPhotoConfig) name() string { - return "photo" -} - -// method returns Telegram API method name for sending Photo. -func (config SetChatPhotoConfig) method() string { - return "setChatPhoto" -} - -// DeleteChatPhotoConfig contains information for delete chat photo. -type DeleteChatPhotoConfig struct { - ChatID int64 -} - -func (config DeleteChatPhotoConfig) method() string { - return "deleteChatPhoto" -} - -func (config DeleteChatPhotoConfig) values() (url.Values, error) { - v := url.Values{} - - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - - return v, nil -} - -// GetStickerSetConfig contains information for get sticker set. -type GetStickerSetConfig struct { - Name string -} - -func (config GetStickerSetConfig) method() string { - return "getStickerSet" -} - -func (config GetStickerSetConfig) values() (url.Values, error) { - v := url.Values{} - v.Add("name", config.Name) - return v, nil -} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go deleted file mode 100644 index 6e295cab..00000000 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/types.go +++ /dev/null @@ -1,990 +0,0 @@ -package tgbotapi - -import ( - "encoding/json" - "errors" - "fmt" - "net/url" - "strings" - "time" -) - -// 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"` - ErrorCode int `json:"error_code"` - Description string `json:"description"` - Parameters *ResponseParameters `json:"parameters"` -} - -// ResponseParameters are various errors that can be returned in APIResponse. -type ResponseParameters struct { - MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional - RetryAfter int `json:"retry_after"` // optional -} - -// Update is an update response, from GetUpdates. -type Update struct { - UpdateID int `json:"update_id"` - Message *Message `json:"message"` - EditedMessage *Message `json:"edited_message"` - ChannelPost *Message `json:"channel_post"` - EditedChannelPost *Message `json:"edited_channel_post"` - InlineQuery *InlineQuery `json:"inline_query"` - ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result"` - CallbackQuery *CallbackQuery `json:"callback_query"` - ShippingQuery *ShippingQuery `json:"shipping_query"` - PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"` -} - -// UpdatesChannel is the channel for getting updates. -type UpdatesChannel <-chan Update - -// Clear discards all unprocessed incoming updates. -func (ch UpdatesChannel) Clear() { - for len(ch) != 0 { - <-ch - } -} - -// User is a user on Telegram. -type User struct { - ID int `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` // optional - UserName string `json:"username"` // optional - LanguageCode string `json:"language_code"` // optional - IsBot bool `json:"is_bot"` // 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. -func (u *User) String() string { - if u == nil { - return "" - } - if u.UserName != "" { - return u.UserName - } - - name := u.FirstName - if u.LastName != "" { - name += " " + u.LastName - } - - return name -} - -// GroupChat is a group chat. -type GroupChat struct { - ID int `json:"id"` - Title string `json:"title"` -} - -// ChatPhoto represents a chat photo. -type ChatPhoto struct { - SmallFileID string `json:"small_file_id"` - BigFileID string `json:"big_file_id"` -} - -// Chat contains information about the place a message was sent. -type Chat struct { - ID int64 `json:"id"` - Type string `json:"type"` - Title string `json:"title"` // optional - UserName string `json:"username"` // optional - FirstName string `json:"first_name"` // optional - LastName string `json:"last_name"` // optional - AllMembersAreAdmins bool `json:"all_members_are_administrators"` // optional - Photo *ChatPhoto `json:"photo"` - Description string `json:"description,omitempty"` // optional - InviteLink string `json:"invite_link,omitempty"` // optional - PinnedMessage *Message `json:"pinned_message"` // optional -} - -// IsPrivate returns if the Chat is a private conversation. -func (c Chat) IsPrivate() bool { - return c.Type == "private" -} - -// IsGroup returns if the Chat is a group. -func (c Chat) IsGroup() bool { - return c.Type == "group" -} - -// IsSuperGroup returns if the Chat is a supergroup. -func (c Chat) IsSuperGroup() bool { - return c.Type == "supergroup" -} - -// IsChannel returns if the Chat is a channel. -func (c Chat) IsChannel() bool { - return c.Type == "channel" -} - -// ChatConfig returns a ChatConfig struct for chat related methods. -func (c Chat) ChatConfig() ChatConfig { - return ChatConfig{ChatID: c.ID} -} - -// 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"` // optional - Date int `json:"date"` - Chat *Chat `json:"chat"` - ForwardFrom *User `json:"forward_from"` // optional - ForwardFromChat *Chat `json:"forward_from_chat"` // optional - ForwardFromMessageID int `json:"forward_from_message_id"` // optional - ForwardDate int `json:"forward_date"` // optional - ReplyToMessage *Message `json:"reply_to_message"` // optional - EditDate int `json:"edit_date"` // optional - Text string `json:"text"` // optional - Entities *[]MessageEntity `json:"entities"` // optional - CaptionEntities *[]MessageEntity `json:"caption_entities"` // optional - Audio *Audio `json:"audio"` // optional - Document *Document `json:"document"` // optional - Animation *ChatAnimation `json:"animation"` // optional - Game *Game `json:"game"` // optional - Photo *[]PhotoSize `json:"photo"` // optional - Sticker *Sticker `json:"sticker"` // optional - Video *Video `json:"video"` // optional - VideoNote *VideoNote `json:"video_note"` // optional - Voice *Voice `json:"voice"` // optional - Caption string `json:"caption"` // optional - Contact *Contact `json:"contact"` // optional - Location *Location `json:"location"` // optional - Venue *Venue `json:"venue"` // optional - NewChatMembers *[]User `json:"new_chat_members"` // optional - LeftChatMember *User `json:"left_chat_member"` // 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 int64 `json:"migrate_to_chat_id"` // optional - MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional - PinnedMessage *Message `json:"pinned_message"` // optional - Invoice *Invoice `json:"invoice"` // optional - SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional - PassportData *PassportData `json:"passport_data,omitempty"` // optional -} - -// Time converts the message timestamp into a Time. -func (m *Message) Time() time.Time { - return time.Unix(int64(m.Date), 0) -} - -// IsCommand returns true if message starts with a "bot_command" entity. -func (m *Message) IsCommand() bool { - if m.Entities == nil || len(*m.Entities) == 0 { - return false - } - - entity := (*m.Entities)[0] - return entity.Offset == 0 && entity.IsCommand() -} - -// 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. -// -// If the command contains the at name syntax, it is removed. Use -// CommandWithAt() if you do not want that. -func (m *Message) Command() string { - command := m.CommandWithAt() - - if i := strings.Index(command, "@"); i != -1 { - command = command[:i] - } - - return command -} - -// CommandWithAt 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. -// -// If the command contains the at name syntax, it is not removed. Use Command() -// if you want that. -func (m *Message) CommandWithAt() string { - if !m.IsCommand() { - return "" - } - - // IsCommand() checks that the message begins with a bot_command entity - entity := (*m.Entities)[0] - return m.Text[1:entity.Length] -} - -// 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. -// -// Note: The first character after the command name is omitted: -// - "/foo bar baz" yields "bar baz", not " bar baz" -// - "/foo-bar baz" yields "bar baz", too -// Even though the latter is not a command conforming to the spec, the API -// marks "/foo" as command entity. -func (m *Message) CommandArguments() string { - if !m.IsCommand() { - return "" - } - - // IsCommand() checks that the message begins with a bot_command entity - entity := (*m.Entities)[0] - if len(m.Text) == entity.Length { - return "" // The command makes up the whole message - } - - return m.Text[entity.Length+1:] -} - -// MessageEntity contains information about data in a Message. -type MessageEntity struct { - Type string `json:"type"` - Offset int `json:"offset"` - Length int `json:"length"` - URL string `json:"url"` // optional - User *User `json:"user"` // optional -} - -// ParseURL attempts to parse a URL contained within a MessageEntity. -func (e MessageEntity) ParseURL() (*url.URL, error) { - if e.URL == "" { - return nil, errors.New(ErrBadURL) - } - - return url.Parse(e.URL) -} - -// IsMention returns true if the type of the message entity is "mention" (@username). -func (e MessageEntity) IsMention() bool { - return e.Type == "mention" -} - -// IsHashtag returns true if the type of the message entity is "hashtag". -func (e MessageEntity) IsHashtag() bool { - return e.Type == "hashtag" -} - -// IsCommand returns true if the type of the message entity is "bot_command". -func (e MessageEntity) IsCommand() bool { - return e.Type == "bot_command" -} - -// IsUrl returns true if the type of the message entity is "url". -func (e MessageEntity) IsUrl() bool { - return e.Type == "url" -} - -// IsEmail returns true if the type of the message entity is "email". -func (e MessageEntity) IsEmail() bool { - return e.Type == "email" -} - -// IsBold returns true if the type of the message entity is "bold" (bold text). -func (e MessageEntity) IsBold() bool { - return e.Type == "bold" -} - -// IsItalic returns true if the type of the message entity is "italic" (italic text). -func (e MessageEntity) IsItalic() bool { - return e.Type == "italic" -} - -// IsCode returns true if the type of the message entity is "code" (monowidth string). -func (e MessageEntity) IsCode() bool { - return e.Type == "code" -} - -// IsPre returns true if the type of the message entity is "pre" (monowidth block). -func (e MessageEntity) IsPre() bool { - return e.Type == "pre" -} - -// IsTextLink returns true if the type of the message entity is "text_link" (clickable text URL). -func (e MessageEntity) IsTextLink() bool { - return e.Type == "text_link" -} - -// 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"` // optional -} - -// Audio contains information about audio. -type Audio struct { - FileID string `json:"file_id"` - Duration int `json:"duration"` - 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. -type Document struct { - FileID string `json:"file_id"` - 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. -type Sticker struct { - FileUniqueID string `json:"file_unique_id"` - FileID string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - Thumbnail *PhotoSize `json:"thumb"` // optional - Emoji string `json:"emoji"` // optional - FileSize int `json:"file_size"` // optional - SetName string `json:"set_name"` // optional - IsAnimated bool `json:"is_animated"` // optional -} - -type StickerSet struct { - Name string `json:"name"` - Title string `json:"title"` - IsAnimated bool `json:"is_animated"` - ContainsMasks bool `json:"contains_masks"` - Stickers []Sticker `json:"stickers"` -} - -// ChatAnimation contains information about an animation. -type ChatAnimation struct { - FileID string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - Duration int `json:"duration"` - Thumbnail *PhotoSize `json:"thumb"` // optional - FileName string `json:"file_name"` // optional - MimeType string `json:"mime_type"` // optional - FileSize int `json:"file_size"` // optional -} - -// 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"` // optional - MimeType string `json:"mime_type"` // optional - FileSize int `json:"file_size"` // optional -} - -// VideoNote contains information about a video. -type VideoNote struct { - FileID string `json:"file_id"` - Length int `json:"length"` - Duration int `json:"duration"` - Thumbnail *PhotoSize `json:"thumb"` // optional - FileSize int `json:"file_size"` // optional -} - -// Voice contains information about a voice. -type Voice struct { - FileID string `json:"file_id"` - Duration int `json:"duration"` - MimeType string `json:"mime_type"` // optional - FileSize int `json:"file_size"` // optional -} - -// 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"` // optional - UserID int `json:"user_id"` // optional -} - -// Location contains information about a place. -type Location struct { - Longitude float64 `json:"longitude"` - Latitude float64 `json:"latitude"` -} - -// Venue contains information about a venue, including its Location. -type Venue struct { - Location Location `json:"location"` - Title string `json:"title"` - Address string `json:"address"` - FoursquareID string `json:"foursquare_id"` // optional -} - -// UserProfilePhotos contains a set of user profile photos. -type UserProfilePhotos struct { - TotalCount int `json:"total_count"` - Photos [][]PhotoSize `json:"photos"` -} - -// File contains information about a file to download from Telegram. -type File struct { - FileID string `json:"file_id"` - FileSize int `json:"file_size"` // optional - FilePath string `json:"file_path"` // optional -} - -// Link returns a full path to the download URL for a File. -// -// It requires the Bot Token to create the link. -func (f *File) Link(token string) string { - return fmt.Sprintf(FileEndpoint, token, f.FilePath) -} - -// ReplyKeyboardMarkup allows the Bot to set a custom keyboard. -type ReplyKeyboardMarkup struct { - Keyboard [][]KeyboardButton `json:"keyboard"` - ResizeKeyboard bool `json:"resize_keyboard"` // optional - OneTimeKeyboard bool `json:"one_time_keyboard"` // optional - Selective bool `json:"selective"` // optional -} - -// KeyboardButton is a button within a custom keyboard. -type KeyboardButton struct { - Text string `json:"text"` - RequestContact bool `json:"request_contact"` - RequestLocation bool `json:"request_location"` -} - -// ReplyKeyboardHide allows the Bot to hide a custom keyboard. -type ReplyKeyboardHide struct { - HideKeyboard bool `json:"hide_keyboard"` - Selective bool `json:"selective"` // optional -} - -// ReplyKeyboardRemove allows the Bot to hide a custom keyboard. -type ReplyKeyboardRemove struct { - RemoveKeyboard bool `json:"remove_keyboard"` - Selective bool `json:"selective"` -} - -// InlineKeyboardMarkup is a custom keyboard presented for an inline bot. -type InlineKeyboardMarkup struct { - InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"` -} - -// InlineKeyboardButton is a button within a custom keyboard for -// inline query responses. -// -// Note that some values are references as even an empty string -// will change behavior. -// -// CallbackGame, if set, MUST be first button in first row. -type InlineKeyboardButton struct { - Text string `json:"text"` - URL *string `json:"url,omitempty"` // optional - CallbackData *string `json:"callback_data,omitempty"` // optional - SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` // optional - SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` // optional - CallbackGame *CallbackGame `json:"callback_game,omitempty"` // optional - Pay bool `json:"pay,omitempty"` // optional -} - -// CallbackQuery is data sent when a keyboard button with callback data -// is clicked. -type CallbackQuery struct { - ID string `json:"id"` - From *User `json:"from"` - Message *Message `json:"message"` // optional - InlineMessageID string `json:"inline_message_id"` // optional - ChatInstance string `json:"chat_instance"` - Data string `json:"data"` // optional - GameShortName string `json:"game_short_name"` // optional -} - -// 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"` // optional -} - -// ChatMember is information about a member in a chat. -type ChatMember struct { - User *User `json:"user"` - Status string `json:"status"` - UntilDate int64 `json:"until_date,omitempty"` // optional - CanBeEdited bool `json:"can_be_edited,omitempty"` // optional - CanChangeInfo bool `json:"can_change_info,omitempty"` // optional - CanPostMessages bool `json:"can_post_messages,omitempty"` // optional - CanEditMessages bool `json:"can_edit_messages,omitempty"` // optional - CanDeleteMessages bool `json:"can_delete_messages,omitempty"` // optional - CanInviteUsers bool `json:"can_invite_users,omitempty"` // optional - CanRestrictMembers bool `json:"can_restrict_members,omitempty"` // optional - CanPinMessages bool `json:"can_pin_messages,omitempty"` // optional - CanPromoteMembers bool `json:"can_promote_members,omitempty"` // optional - CanSendMessages bool `json:"can_send_messages,omitempty"` // optional - CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` // optional - CanSendOtherMessages bool `json:"can_send_other_messages,omitempty"` // optional - CanAddWebPagePreviews bool `json:"can_add_web_page_previews,omitempty"` // optional -} - -// IsCreator returns if the ChatMember was the creator of the chat. -func (chat ChatMember) IsCreator() bool { return chat.Status == "creator" } - -// IsAdministrator returns if the ChatMember is a chat administrator. -func (chat ChatMember) IsAdministrator() bool { return chat.Status == "administrator" } - -// IsMember returns if the ChatMember is a current member of the chat. -func (chat ChatMember) IsMember() bool { return chat.Status == "member" } - -// HasLeft returns if the ChatMember left the chat. -func (chat ChatMember) HasLeft() bool { return chat.Status == "left" } - -// WasKicked returns if the ChatMember was kicked from the chat. -func (chat ChatMember) WasKicked() bool { return chat.Status == "kicked" } - -// Game is a game within Telegram. -type Game struct { - Title string `json:"title"` - Description string `json:"description"` - Photo []PhotoSize `json:"photo"` - Text string `json:"text"` - TextEntities []MessageEntity `json:"text_entities"` - Animation Animation `json:"animation"` -} - -// Animation is a GIF animation demonstrating the game. -type Animation struct { - FileID string `json:"file_id"` - Thumb PhotoSize `json:"thumb"` - FileName string `json:"file_name"` - MimeType string `json:"mime_type"` - FileSize int `json:"file_size"` -} - -// GameHighScore is a user's score and position on the leaderboard. -type GameHighScore struct { - Position int `json:"position"` - User User `json:"user"` - Score int `json:"score"` -} - -// CallbackGame is for starting a game in an inline keyboard button. -type CallbackGame struct{} - -// WebhookInfo is information about a currently set webhook. -type WebhookInfo struct { - URL string `json:"url"` - HasCustomCertificate bool `json:"has_custom_certificate"` - PendingUpdateCount int `json:"pending_update_count"` - LastErrorDate int `json:"last_error_date"` // optional - LastErrorMessage string `json:"last_error_message"` // optional -} - -// IsSet returns true if a webhook is currently set. -func (info WebhookInfo) IsSet() bool { - return info.URL != "" -} - -// InputMediaPhoto contains a photo for displaying as part of a media group. -type InputMediaPhoto struct { - Type string `json:"type"` - Media string `json:"media"` - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` -} - -// InputMediaVideo contains a video for displaying as part of a media group. -type InputMediaVideo struct { - Type string `json:"type"` - Media string `json:"media"` - // thumb intentionally missing as it is not currently compatible - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - Width int `json:"width"` - Height int `json:"height"` - Duration int `json:"duration"` - SupportsStreaming bool `json:"supports_streaming"` -} - -// InlineQuery is a Query from Telegram for an inline request. -type InlineQuery struct { - ID string `json:"id"` - From *User `json:"from"` - Location *Location `json:"location"` // optional - Query string `json:"query"` - Offset string `json:"offset"` -} - -// InlineQueryResultArticle is an inline query response article. -type InlineQueryResultArticle struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - Title string `json:"title"` // required - InputMessageContent interface{} `json:"input_message_content,omitempty"` // required - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - URL string `json:"url"` - HideURL bool `json:"hide_url"` - Description string `json:"description"` - ThumbURL string `json:"thumb_url"` - ThumbWidth int `json:"thumb_width"` - ThumbHeight int `json:"thumb_height"` -} - -// InlineQueryResultPhoto is an inline query response photo. -type InlineQueryResultPhoto struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - URL string `json:"photo_url"` // required - MimeType string `json:"mime_type"` - Width int `json:"photo_width"` - Height int `json:"photo_height"` - ThumbURL string `json:"thumb_url"` - Title string `json:"title"` - Description string `json:"description"` - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultCachedPhoto is an inline query response with cached photo. -type InlineQueryResultCachedPhoto struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - PhotoID string `json:"photo_file_id"` // required - Title string `json:"title"` - Description string `json:"description"` - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultGIF is an inline query response GIF. -type InlineQueryResultGIF struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - URL string `json:"gif_url"` // required - ThumbURL string `json:"thumb_url"` // required - Width int `json:"gif_width,omitempty"` - Height int `json:"gif_height,omitempty"` - Duration int `json:"gif_duration,omitempty"` - Title string `json:"title,omitempty"` - Caption string `json:"caption,omitempty"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultCachedGIF is an inline query response with cached gif. -type InlineQueryResultCachedGIF struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - GifID string `json:"gif_file_id"` // required - Title string `json:"title"` - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF. -type InlineQueryResultMPEG4GIF struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - URL string `json:"mpeg4_url"` // required - Width int `json:"mpeg4_width"` - Height int `json:"mpeg4_height"` - Duration int `json:"mpeg4_duration"` - ThumbURL string `json:"thumb_url"` - Title string `json:"title"` - Caption string `json:"caption"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultCachedMpeg4Gif is an inline query response with cached -// H.264/MPEG-4 AVC video without sound gif. -type InlineQueryResultCachedMpeg4Gif struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - MGifID string `json:"mpeg4_file_id"` // required - Title string `json:"title"` - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultVideo is an inline query response video. -type InlineQueryResultVideo struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - URL string `json:"video_url"` // required - MimeType string `json:"mime_type"` // required - ThumbURL string `json:"thumb_url"` - Title string `json:"title"` - Caption string `json:"caption"` - Width int `json:"video_width"` - Height int `json:"video_height"` - Duration int `json:"video_duration"` - Description string `json:"description"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultCachedVideo is an inline query response with cached video. -type InlineQueryResultCachedVideo struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - VideoID string `json:"video_file_id"` // required - Title string `json:"title"` // required - Description string `json:"description"` - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultAudio is an inline query response audio. -type InlineQueryResultAudio struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - URL string `json:"audio_url"` // required - Title string `json:"title"` // required - Caption string `json:"caption"` - Performer string `json:"performer"` - Duration int `json:"audio_duration"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultCachedAudio is an inline query response with cached audio. -type InlineQueryResultCachedAudio struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - AudioID string `json:"audio_file_id"` // required - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultVoice is an inline query response voice. -type InlineQueryResultVoice struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - URL string `json:"voice_url"` // required - Title string `json:"title"` // required - Caption string `json:"caption"` - Duration int `json:"voice_duration"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultCachedVoice is an inline query response with cached voice. -type InlineQueryResultCachedVoice struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - VoiceID string `json:"voice_file_id"` // required - Title string `json:"title"` // required - Caption string `json:"caption"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultDocument is an inline query response document. -type InlineQueryResultDocument struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - Title string `json:"title"` // required - Caption string `json:"caption"` - URL string `json:"document_url"` // required - MimeType string `json:"mime_type"` // required - Description string `json:"description"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` - ThumbURL string `json:"thumb_url"` - ThumbWidth int `json:"thumb_width"` - ThumbHeight int `json:"thumb_height"` -} - -// InlineQueryResultCachedDocument is an inline query response with cached document. -type InlineQueryResultCachedDocument struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - DocumentID string `json:"document_file_id"` // required - Title string `json:"title"` // required - Caption string `json:"caption"` - Description string `json:"description"` - ParseMode string `json:"parse_mode"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` -} - -// InlineQueryResultLocation is an inline query response location. -type InlineQueryResultLocation struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - Latitude float64 `json:"latitude"` // required - Longitude float64 `json:"longitude"` // required - Title string `json:"title"` // required - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` - ThumbURL string `json:"thumb_url"` - ThumbWidth int `json:"thumb_width"` - ThumbHeight int `json:"thumb_height"` -} - -// InlineQueryResultVenue is an inline query response venue. -type InlineQueryResultVenue struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - Latitude float64 `json:"latitude"` // required - Longitude float64 `json:"longitude"` // required - Title string `json:"title"` // required - Address string `json:"address"` // required - FoursquareID string `json:"foursquare_id"` - FoursquareType string `json:"foursquare_type"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` - InputMessageContent interface{} `json:"input_message_content,omitempty"` - ThumbURL string `json:"thumb_url"` - ThumbWidth int `json:"thumb_width"` - ThumbHeight int `json:"thumb_height"` -} - -// InlineQueryResultGame is an inline query response game. -type InlineQueryResultGame struct { - Type string `json:"type"` - ID string `json:"id"` - GameShortName string `json:"game_short_name"` - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` -} - -// ChosenInlineResult is an inline query result chosen by a User -type ChosenInlineResult struct { - ResultID string `json:"result_id"` - From *User `json:"from"` - Location *Location `json:"location"` - InlineMessageID string `json:"inline_message_id"` - Query string `json:"query"` -} - -// InputTextMessageContent contains text for displaying -// as an inline query result. -type InputTextMessageContent struct { - Text string `json:"message_text"` - ParseMode string `json:"parse_mode"` - DisableWebPagePreview bool `json:"disable_web_page_preview"` -} - -// InputLocationMessageContent contains a location for displaying -// as an inline query result. -type InputLocationMessageContent struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` -} - -// InputVenueMessageContent contains a venue for displaying -// as an inline query result. -type InputVenueMessageContent struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - Title string `json:"title"` - Address string `json:"address"` - FoursquareID string `json:"foursquare_id"` -} - -// InputContactMessageContent contains a contact for displaying -// as an inline query result. -type InputContactMessageContent struct { - PhoneNumber string `json:"phone_number"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` -} - -// Invoice contains basic information about an invoice. -type Invoice struct { - Title string `json:"title"` - Description string `json:"description"` - StartParameter string `json:"start_parameter"` - Currency string `json:"currency"` - TotalAmount int `json:"total_amount"` -} - -// LabeledPrice represents a portion of the price for goods or services. -type LabeledPrice struct { - Label string `json:"label"` - Amount int `json:"amount"` -} - -// ShippingAddress represents a shipping address. -type ShippingAddress struct { - CountryCode string `json:"country_code"` - State string `json:"state"` - City string `json:"city"` - StreetLine1 string `json:"street_line1"` - StreetLine2 string `json:"street_line2"` - PostCode string `json:"post_code"` -} - -// OrderInfo represents information about an order. -type OrderInfo struct { - Name string `json:"name,omitempty"` - PhoneNumber string `json:"phone_number,omitempty"` - Email string `json:"email,omitempty"` - ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"` -} - -// ShippingOption represents one shipping option. -type ShippingOption struct { - ID string `json:"id"` - Title string `json:"title"` - Prices *[]LabeledPrice `json:"prices"` -} - -// SuccessfulPayment contains basic information about a successful payment. -type SuccessfulPayment struct { - Currency string `json:"currency"` - TotalAmount int `json:"total_amount"` - InvoicePayload string `json:"invoice_payload"` - ShippingOptionID string `json:"shipping_option_id,omitempty"` - OrderInfo *OrderInfo `json:"order_info,omitempty"` - TelegramPaymentChargeID string `json:"telegram_payment_charge_id"` - ProviderPaymentChargeID string `json:"provider_payment_charge_id"` -} - -// ShippingQuery contains information about an incoming shipping query. -type ShippingQuery struct { - ID string `json:"id"` - From *User `json:"from"` - InvoicePayload string `json:"invoice_payload"` - ShippingAddress *ShippingAddress `json:"shipping_address"` -} - -// PreCheckoutQuery contains information about an incoming pre-checkout query. -type PreCheckoutQuery struct { - ID string `json:"id"` - From *User `json:"from"` - Currency string `json:"currency"` - TotalAmount int `json:"total_amount"` - InvoicePayload string `json:"invoice_payload"` - ShippingOptionID string `json:"shipping_option_id,omitempty"` - OrderInfo *OrderInfo `json:"order_info,omitempty"` -} - -// Error is an error containing extra information returned by the Telegram API. -type Error struct { - Code int - Message string - ResponseParameters -} - -func (e Error) Error() string { - return e.Message -} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/.gitignore b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/.gitignore index fb5a5e83..eb7a23b2 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/.gitignore +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/.gitignore @@ -1,3 +1,4 @@ .idea/ coverage.out tmp/ +book/ diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/LICENSE.txt b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/LICENSE.txt index b1fef93b..b1fef93b 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/LICENSE.txt +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/LICENSE.txt diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/README.md b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/README.md index 43b33ed8..45d3e1db 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/README.md +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/README.md @@ -1,12 +1,14 @@ # Golang bindings for the Telegram Bot API -[![GoDoc](https://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api?status.svg)](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api) -[![Travis](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api.svg)](https://travis-ci.org/go-telegram-bot-api/telegram-bot-api) +[![Go Reference](https://pkg.go.dev/badge/github.com/go-telegram-bot-api/telegram-bot-api/v5.svg)](https://pkg.go.dev/github.com/go-telegram-bot-api/telegram-bot-api/v5) +[![Test](https://github.com/go-telegram-bot-api/telegram-bot-api/actions/workflows/test.yml/badge.svg)](https://github.com/go-telegram-bot-api/telegram-bot-api/actions/workflows/test.yml) -All methods are fairly self explanatory, and reading the [godoc](http://godoc.org/github.com/go-telegram-bot-api/telegram-bot-api) page should +All methods are fairly self-explanatory, and reading the [godoc](https://pkg.go.dev/github.com/go-telegram-bot-api/telegram-bot-api/v5) page should explain everything. If something isn't clear, open an issue or submit a pull request. +There are more tutorials and high-level information on the website, [go-telegram-bot-api.dev](https://go-telegram-bot-api.dev). + 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 @@ -18,7 +20,7 @@ you want to ask questions or discuss development. ## Example First, ensure the library is installed and up to date by running -`go get -u github.com/go-telegram-bot-api/telegram-bot-api`. +`go get -u github.com/go-telegram-bot-api/telegram-bot-api/v5`. This is a very simple bot that just displays any gotten updates, then replies it to that chat. @@ -29,7 +31,7 @@ package main import ( "log" - "github.com/go-telegram-bot-api/telegram-bot-api" + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) func main() { @@ -45,28 +47,21 @@ func main() { u := tgbotapi.NewUpdate(0) u.Timeout = 60 - updates, err := bot.GetUpdatesChan(u) + updates := bot.GetUpdatesChan(u) for update := range updates { - if update.Message == nil { // ignore any non-Message Updates - continue - } - - log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) + if update.Message != nil { // If we got a message + log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) - msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text) - msg.ReplyToMessageID = update.Message.MessageID + msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text) + msg.ReplyToMessageID = update.Message.MessageID - bot.Send(msg) + bot.Send(msg) + } } } ``` -There are more examples on the [wiki](https://github.com/go-telegram-bot-api/telegram-bot-api/wiki) -with detailed information on how to do many different kinds of things. -It's a great place to get started on using keyboards, commands, or other -kinds of reply markup. - If you need to use webhooks (if you wish to run on Google App Engine), you may use a slightly different method. @@ -77,7 +72,7 @@ import ( "log" "net/http" - "github.com/go-telegram-bot-api/telegram-bot-api" + "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) func main() { @@ -90,17 +85,22 @@ func main() { log.Printf("Authorized on account %s", bot.Self.UserName) - _, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) + wh, _ := tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem") + + _, err = bot.SetWebhook(wh) if err != nil { log.Fatal(err) } + info, err := bot.GetWebhookInfo() if err != nil { log.Fatal(err) } + if info.LastErrorDate != 0 { log.Printf("Telegram callback failed: %s", info.LastErrorMessage) } + updates := bot.ListenForWebhook("/" + bot.Token) go http.ListenAndServeTLS("0.0.0.0:8443", "cert.pem", "key.pem", nil) @@ -110,7 +110,7 @@ func main() { } ``` -If you need, you may generate a self signed certficate, as this requires +If you need, you may generate a self-signed certificate, 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. diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/book.toml b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/book.toml new file mode 100644 index 00000000..841d5ba6 --- /dev/null +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/book.toml @@ -0,0 +1,9 @@ +[book] +authors = ["Syfaro"] +language = "en" +multilingual = false +src = "docs" +title = "Go Telegram Bot API" + +[output.html] +git-repository-url = "https://github.com/go-telegram-bot-api/telegram-bot-api" 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("<", "<", ">", ">", "&", "&") + } else if parseMode == ModeMarkdown { + replacer = strings.NewReplacer("_", "\\_", "*", "\\*", "`", "\\`", "[", "\\[") + } else if parseMode == ModeMarkdownV2 { + replacer = strings.NewReplacer( + "_", "\\_", "*", "\\*", "[", "\\[", "]", "\\]", "(", + "\\(", ")", "\\)", "~", "\\~", "`", "\\`", ">", "\\>", + "#", "\\#", "+", "\\+", "-", "\\-", "=", "\\=", "|", + "\\|", "{", "\\{", "}", "\\}", ".", "\\.", "!", "\\!", + ) + } else { + return "" + } + + return replacer.Replace(text) +} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/configs.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/configs.go new file mode 100644 index 00000000..5e43af49 --- /dev/null +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/configs.go @@ -0,0 +1,2468 @@ +package tgbotapi + +import ( + "bytes" + "fmt" + "io" + "net/url" + "os" + "strconv" +) + +// Telegram constants +const ( + // 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 = "https://api.telegram.org/file/bot%s/%s" +) + +// Constant values for ChatActions +const ( + ChatTyping = "typing" + ChatUploadPhoto = "upload_photo" + ChatRecordVideo = "record_video" + ChatUploadVideo = "upload_video" + ChatRecordVoice = "record_voice" + ChatUploadVoice = "upload_voice" + ChatUploadDocument = "upload_document" + ChatChooseSticker = "choose_sticker" + ChatFindLocation = "find_location" + ChatRecordVideoNote = "record_video_note" + ChatUploadVideoNote = "upload_video_note" +) + +// API errors +const ( + // ErrAPIForbidden happens when a token is bad + ErrAPIForbidden = "forbidden" +) + +// Constant values for ParseMode in MessageConfig +const ( + ModeMarkdown = "Markdown" + ModeMarkdownV2 = "MarkdownV2" + ModeHTML = "HTML" +) + +// Constant values for update types +const ( + // UpdateTypeMessage is new incoming message of any kind — text, photo, sticker, etc. + UpdateTypeMessage = "message" + + // UpdateTypeEditedMessage is new version of a message that is known to the bot and was edited + UpdateTypeEditedMessage = "edited_message" + + // UpdateTypeChannelPost is new incoming channel post of any kind — text, photo, sticker, etc. + UpdateTypeChannelPost = "channel_post" + + // UpdateTypeEditedChannelPost is new version of a channel post that is known to the bot and was edited + UpdateTypeEditedChannelPost = "edited_channel_post" + + // UpdateTypeInlineQuery is new incoming inline query + UpdateTypeInlineQuery = "inline_query" + + // UpdateTypeChosenInlineResult i the result of an inline query that was chosen by a user and sent to their + // chat partner. Please see the documentation on the feedback collecting for + // details on how to enable these updates for your bot. + UpdateTypeChosenInlineResult = "chosen_inline_result" + + // UpdateTypeCallbackQuery is new incoming callback query + UpdateTypeCallbackQuery = "callback_query" + + // UpdateTypeShippingQuery is new incoming shipping query. Only for invoices with flexible price + UpdateTypeShippingQuery = "shipping_query" + + // UpdateTypePreCheckoutQuery is new incoming pre-checkout query. Contains full information about checkout + UpdateTypePreCheckoutQuery = "pre_checkout_query" + + // UpdateTypePoll is new poll state. Bots receive only updates about stopped polls and polls + // which are sent by the bot + UpdateTypePoll = "poll" + + // UpdateTypePollAnswer is when user changed their answer in a non-anonymous poll. Bots receive new votes + // only in polls that were sent by the bot itself. + UpdateTypePollAnswer = "poll_answer" + + // UpdateTypeMyChatMember is when the bot's chat member status was updated in a chat. For private chats, this + // update is received only when the bot is blocked or unblocked by the user. + UpdateTypeMyChatMember = "my_chat_member" + + // UpdateTypeChatMember is when the bot must be an administrator in the chat and must explicitly specify + // this update in the list of allowed_updates to receive these updates. + UpdateTypeChatMember = "chat_member" +) + +// Library errors +const ( + ErrBadURL = "bad or empty url" +) + +// Chattable is any config type that can be sent. +type Chattable interface { + params() (Params, error) + method() string +} + +// Fileable is any config type that can be sent that includes a file. +type Fileable interface { + Chattable + files() []RequestFile +} + +// RequestFile represents a file associated with a field name. +type RequestFile struct { + // The file field name. + Name string + // The file data to include. + Data RequestFileData +} + +// RequestFileData represents the data to be used for a file. +type RequestFileData interface { + // NeedsUpload shows if the file needs to be uploaded. + NeedsUpload() bool + + // UploadData gets the file name and an `io.Reader` for the file to be uploaded. This + // must only be called when the file needs to be uploaded. + UploadData() (string, io.Reader, error) + // SendData gets the file data to send when a file does not need to be uploaded. This + // must only be called when the file does not need to be uploaded. + SendData() string +} + +// FileBytes contains information about a set of bytes to upload +// as a File. +type FileBytes struct { + Name string + Bytes []byte +} + +func (fb FileBytes) NeedsUpload() bool { + return true +} + +func (fb FileBytes) UploadData() (string, io.Reader, error) { + return fb.Name, bytes.NewReader(fb.Bytes), nil +} + +func (fb FileBytes) SendData() string { + panic("FileBytes must be uploaded") +} + +// FileReader contains information about a reader to upload as a File. +type FileReader struct { + Name string + Reader io.Reader +} + +func (fr FileReader) NeedsUpload() bool { + return true +} + +func (fr FileReader) UploadData() (string, io.Reader, error) { + return fr.Name, fr.Reader, nil +} + +func (fr FileReader) SendData() string { + panic("FileReader must be uploaded") +} + +// FilePath is a path to a local file. +type FilePath string + +func (fp FilePath) NeedsUpload() bool { + return true +} + +func (fp FilePath) UploadData() (string, io.Reader, error) { + fileHandle, err := os.Open(string(fp)) + if err != nil { + return "", nil, err + } + + name := fileHandle.Name() + return name, fileHandle, err +} + +func (fp FilePath) SendData() string { + panic("FilePath must be uploaded") +} + +// FileURL is a URL to use as a file for a request. +type FileURL string + +func (fu FileURL) NeedsUpload() bool { + return false +} + +func (fu FileURL) UploadData() (string, io.Reader, error) { + panic("FileURL cannot be uploaded") +} + +func (fu FileURL) SendData() string { + return string(fu) +} + +// FileID is an ID of a file already uploaded to Telegram. +type FileID string + +func (fi FileID) NeedsUpload() bool { + return false +} + +func (fi FileID) UploadData() (string, io.Reader, error) { + panic("FileID cannot be uploaded") +} + +func (fi FileID) SendData() string { + return string(fi) +} + +// fileAttach is an internal file type used for processed media groups. +type fileAttach string + +func (fa fileAttach) NeedsUpload() bool { + return false +} + +func (fa fileAttach) UploadData() (string, io.Reader, error) { + panic("fileAttach cannot be uploaded") +} + +func (fa fileAttach) SendData() string { + return string(fa) +} + +// LogOutConfig is a request to log out of the cloud Bot API server. +// +// Note that you may not log back in for at least 10 minutes. +type LogOutConfig struct{} + +func (LogOutConfig) method() string { + return "logOut" +} + +func (LogOutConfig) params() (Params, error) { + return nil, nil +} + +// CloseConfig is a request to close the bot instance on a local server. +// +// Note that you may not close an instance for the first 10 minutes after the +// bot has started. +type CloseConfig struct{} + +func (CloseConfig) method() string { + return "close" +} + +func (CloseConfig) params() (Params, error) { + return nil, nil +} + +// BaseChat is base type for all chat config types. +type BaseChat struct { + ChatID int64 // required + ChannelUsername string + ReplyToMessageID int + ReplyMarkup interface{} + DisableNotification bool + AllowSendingWithoutReply bool +} + +func (chat *BaseChat) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) + params.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) + params.AddBool("disable_notification", chat.DisableNotification) + params.AddBool("allow_sending_without_reply", chat.AllowSendingWithoutReply) + + err := params.AddInterface("reply_markup", chat.ReplyMarkup) + + return params, err +} + +// BaseFile is a base type for all file config types. +type BaseFile struct { + BaseChat + File RequestFileData +} + +func (file BaseFile) params() (Params, error) { + return file.BaseChat.params() +} + +// BaseEdit is base type of all chat edits. +type BaseEdit struct { + ChatID int64 + ChannelUsername string + MessageID int + InlineMessageID string + ReplyMarkup *InlineKeyboardMarkup +} + +func (edit BaseEdit) params() (Params, error) { + params := make(Params) + + if edit.InlineMessageID != "" { + params["inline_message_id"] = edit.InlineMessageID + } else { + params.AddFirstValid("chat_id", edit.ChatID, edit.ChannelUsername) + params.AddNonZero("message_id", edit.MessageID) + } + + err := params.AddInterface("reply_markup", edit.ReplyMarkup) + + return params, err +} + +// MessageConfig contains information about a SendMessage request. +type MessageConfig struct { + BaseChat + Text string + ParseMode string + Entities []MessageEntity + DisableWebPagePreview bool +} + +func (config MessageConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonEmpty("text", config.Text) + params.AddBool("disable_web_page_preview", config.DisableWebPagePreview) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("entities", config.Entities) + + return params, err +} + +func (config MessageConfig) method() string { + return "sendMessage" +} + +// ForwardConfig contains information about a ForwardMessage request. +type ForwardConfig struct { + BaseChat + FromChatID int64 // required + FromChannelUsername string + MessageID int // required +} + +func (config ForwardConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonZero64("from_chat_id", config.FromChatID) + params.AddNonZero("message_id", config.MessageID) + + return params, nil +} + +func (config ForwardConfig) method() string { + return "forwardMessage" +} + +// CopyMessageConfig contains information about a copyMessage request. +type CopyMessageConfig struct { + BaseChat + FromChatID int64 + FromChannelUsername string + MessageID int + Caption string + ParseMode string + CaptionEntities []MessageEntity +} + +func (config CopyMessageConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddFirstValid("from_chat_id", config.FromChatID, config.FromChannelUsername) + params.AddNonZero("message_id", config.MessageID) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config CopyMessageConfig) method() string { + return "copyMessage" +} + +// PhotoConfig contains information about a SendPhoto request. +type PhotoConfig struct { + BaseFile + Thumb RequestFileData + Caption string + ParseMode string + CaptionEntities []MessageEntity +} + +func (config PhotoConfig) params() (Params, error) { + params, err := config.BaseFile.params() + if err != nil { + return params, err + } + + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config PhotoConfig) method() string { + return "sendPhoto" +} + +func (config PhotoConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "photo", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// AudioConfig contains information about a SendAudio request. +type AudioConfig struct { + BaseFile + Thumb RequestFileData + Caption string + ParseMode string + CaptionEntities []MessageEntity + Duration int + Performer string + Title string +} + +func (config AudioConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("performer", config.Performer) + params.AddNonEmpty("title", config.Title) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config AudioConfig) method() string { + return "sendAudio" +} + +func (config AudioConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "audio", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// DocumentConfig contains information about a SendDocument request. +type DocumentConfig struct { + BaseFile + Thumb RequestFileData + Caption string + ParseMode string + CaptionEntities []MessageEntity + DisableContentTypeDetection bool +} + +func (config DocumentConfig) params() (Params, error) { + params, err := config.BaseFile.params() + + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + params.AddBool("disable_content_type_detection", config.DisableContentTypeDetection) + + return params, err +} + +func (config DocumentConfig) method() string { + return "sendDocument" +} + +func (config DocumentConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "document", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// StickerConfig contains information about a SendSticker request. +type StickerConfig struct { + BaseFile +} + +func (config StickerConfig) params() (Params, error) { + return config.BaseChat.params() +} + +func (config StickerConfig) method() string { + return "sendSticker" +} + +func (config StickerConfig) files() []RequestFile { + return []RequestFile{{ + Name: "sticker", + Data: config.File, + }} +} + +// VideoConfig contains information about a SendVideo request. +type VideoConfig struct { + BaseFile + Thumb RequestFileData + Duration int + Caption string + ParseMode string + CaptionEntities []MessageEntity + SupportsStreaming bool +} + +func (config VideoConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + params.AddBool("supports_streaming", config.SupportsStreaming) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config VideoConfig) method() string { + return "sendVideo" +} + +func (config VideoConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "video", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// AnimationConfig contains information about a SendAnimation request. +type AnimationConfig struct { + BaseFile + Duration int + Thumb RequestFileData + Caption string + ParseMode string + CaptionEntities []MessageEntity +} + +func (config AnimationConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config AnimationConfig) method() string { + return "sendAnimation" +} + +func (config AnimationConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "animation", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// VideoNoteConfig contains information about a SendVideoNote request. +type VideoNoteConfig struct { + BaseFile + Thumb RequestFileData + Duration int + Length int +} + +func (config VideoNoteConfig) params() (Params, error) { + params, err := config.BaseChat.params() + + params.AddNonZero("duration", config.Duration) + params.AddNonZero("length", config.Length) + + return params, err +} + +func (config VideoNoteConfig) method() string { + return "sendVideoNote" +} + +func (config VideoNoteConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "video_note", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// VoiceConfig contains information about a SendVoice request. +type VoiceConfig struct { + BaseFile + Thumb RequestFileData + Caption string + ParseMode string + CaptionEntities []MessageEntity + Duration int +} + +func (config VoiceConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config VoiceConfig) method() string { + return "sendVoice" +} + +func (config VoiceConfig) files() []RequestFile { + files := []RequestFile{{ + Name: "voice", + Data: config.File, + }} + + if config.Thumb != nil { + files = append(files, RequestFile{ + Name: "thumb", + Data: config.Thumb, + }) + } + + return files +} + +// LocationConfig contains information about a SendLocation request. +type LocationConfig struct { + BaseChat + Latitude float64 // required + Longitude float64 // required + HorizontalAccuracy float64 // optional + LivePeriod int // optional + Heading int // optional + ProximityAlertRadius int // optional +} + +func (config LocationConfig) params() (Params, error) { + params, err := config.BaseChat.params() + + params.AddNonZeroFloat("latitude", config.Latitude) + params.AddNonZeroFloat("longitude", config.Longitude) + params.AddNonZeroFloat("horizontal_accuracy", config.HorizontalAccuracy) + params.AddNonZero("live_period", config.LivePeriod) + params.AddNonZero("heading", config.Heading) + params.AddNonZero("proximity_alert_radius", config.ProximityAlertRadius) + + return params, err +} + +func (config LocationConfig) method() string { + return "sendLocation" +} + +// EditMessageLiveLocationConfig allows you to update a live location. +type EditMessageLiveLocationConfig struct { + BaseEdit + Latitude float64 // required + Longitude float64 // required + HorizontalAccuracy float64 // optional + Heading int // optional + ProximityAlertRadius int // optional +} + +func (config EditMessageLiveLocationConfig) params() (Params, error) { + params, err := config.BaseEdit.params() + + params.AddNonZeroFloat("latitude", config.Latitude) + params.AddNonZeroFloat("longitude", config.Longitude) + params.AddNonZeroFloat("horizontal_accuracy", config.HorizontalAccuracy) + params.AddNonZero("heading", config.Heading) + params.AddNonZero("proximity_alert_radius", config.ProximityAlertRadius) + + return params, err +} + +func (config EditMessageLiveLocationConfig) method() string { + return "editMessageLiveLocation" +} + +// StopMessageLiveLocationConfig stops updating a live location. +type StopMessageLiveLocationConfig struct { + BaseEdit +} + +func (config StopMessageLiveLocationConfig) params() (Params, error) { + return config.BaseEdit.params() +} + +func (config StopMessageLiveLocationConfig) method() string { + return "stopMessageLiveLocation" +} + +// VenueConfig contains information about a SendVenue request. +type VenueConfig struct { + BaseChat + Latitude float64 // required + Longitude float64 // required + Title string // required + Address string // required + FoursquareID string + FoursquareType string + GooglePlaceID string + GooglePlaceType string +} + +func (config VenueConfig) params() (Params, error) { + params, err := config.BaseChat.params() + + params.AddNonZeroFloat("latitude", config.Latitude) + params.AddNonZeroFloat("longitude", config.Longitude) + params["title"] = config.Title + params["address"] = config.Address + params.AddNonEmpty("foursquare_id", config.FoursquareID) + params.AddNonEmpty("foursquare_type", config.FoursquareType) + params.AddNonEmpty("google_place_id", config.GooglePlaceID) + params.AddNonEmpty("google_place_type", config.GooglePlaceType) + + return params, err +} + +func (config VenueConfig) method() string { + return "sendVenue" +} + +// ContactConfig allows you to send a contact. +type ContactConfig struct { + BaseChat + PhoneNumber string + FirstName string + LastName string + VCard string +} + +func (config ContactConfig) params() (Params, error) { + params, err := config.BaseChat.params() + + params["phone_number"] = config.PhoneNumber + params["first_name"] = config.FirstName + + params.AddNonEmpty("last_name", config.LastName) + params.AddNonEmpty("vcard", config.VCard) + + return params, err +} + +func (config ContactConfig) method() string { + return "sendContact" +} + +// SendPollConfig allows you to send a poll. +type SendPollConfig struct { + BaseChat + Question string + Options []string + IsAnonymous bool + Type string + AllowsMultipleAnswers bool + CorrectOptionID int64 + Explanation string + ExplanationParseMode string + ExplanationEntities []MessageEntity + OpenPeriod int + CloseDate int + IsClosed bool +} + +func (config SendPollConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params["question"] = config.Question + if err = params.AddInterface("options", config.Options); err != nil { + return params, err + } + params["is_anonymous"] = strconv.FormatBool(config.IsAnonymous) + params.AddNonEmpty("type", config.Type) + params["allows_multiple_answers"] = strconv.FormatBool(config.AllowsMultipleAnswers) + params["correct_option_id"] = strconv.FormatInt(config.CorrectOptionID, 10) + params.AddBool("is_closed", config.IsClosed) + params.AddNonEmpty("explanation", config.Explanation) + params.AddNonEmpty("explanation_parse_mode", config.ExplanationParseMode) + params.AddNonZero("open_period", config.OpenPeriod) + params.AddNonZero("close_date", config.CloseDate) + err = params.AddInterface("explanation_entities", config.ExplanationEntities) + + return params, err +} + +func (SendPollConfig) method() string { + return "sendPoll" +} + +// GameConfig allows you to send a game. +type GameConfig struct { + BaseChat + GameShortName string +} + +func (config GameConfig) params() (Params, error) { + params, err := config.BaseChat.params() + + params["game_short_name"] = config.GameShortName + + return params, err +} + +func (config GameConfig) method() string { + return "sendGame" +} + +// SetGameScoreConfig allows you to update the game score in a chat. +type SetGameScoreConfig struct { + UserID int64 + Score int + Force bool + DisableEditMessage bool + ChatID int64 + ChannelUsername string + MessageID int + InlineMessageID string +} + +func (config SetGameScoreConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserID) + params.AddNonZero("scrore", config.Score) + params.AddBool("disable_edit_message", config.DisableEditMessage) + + if config.InlineMessageID != "" { + params["inline_message_id"] = config.InlineMessageID + } else { + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + } + + return params, nil +} + +func (config SetGameScoreConfig) method() string { + return "setGameScore" +} + +// GetGameHighScoresConfig allows you to fetch the high scores for a game. +type GetGameHighScoresConfig struct { + UserID int64 + ChatID int64 + ChannelUsername string + MessageID int + InlineMessageID string +} + +func (config GetGameHighScoresConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserID) + + if config.InlineMessageID != "" { + params["inline_message_id"] = config.InlineMessageID + } else { + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + } + + return params, nil +} + +func (config GetGameHighScoresConfig) method() string { + return "getGameHighScores" +} + +// ChatActionConfig contains information about a SendChatAction request. +type ChatActionConfig struct { + BaseChat + Action string // required +} + +func (config ChatActionConfig) params() (Params, error) { + params, err := config.BaseChat.params() + + params["action"] = config.Action + + return params, err +} + +func (config ChatActionConfig) method() string { + return "sendChatAction" +} + +// EditMessageTextConfig allows you to modify the text in a message. +type EditMessageTextConfig struct { + BaseEdit + Text string + ParseMode string + Entities []MessageEntity + DisableWebPagePreview bool +} + +func (config EditMessageTextConfig) params() (Params, error) { + params, err := config.BaseEdit.params() + if err != nil { + return params, err + } + + params["text"] = config.Text + params.AddNonEmpty("parse_mode", config.ParseMode) + params.AddBool("disable_web_page_preview", config.DisableWebPagePreview) + err = params.AddInterface("entities", config.Entities) + + return params, err +} + +func (config EditMessageTextConfig) method() string { + return "editMessageText" +} + +// EditMessageCaptionConfig allows you to modify the caption of a message. +type EditMessageCaptionConfig struct { + BaseEdit + Caption string + ParseMode string + CaptionEntities []MessageEntity +} + +func (config EditMessageCaptionConfig) params() (Params, error) { + params, err := config.BaseEdit.params() + if err != nil { + return params, err + } + + params["caption"] = config.Caption + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + +func (config EditMessageCaptionConfig) method() string { + return "editMessageCaption" +} + +// EditMessageMediaConfig allows you to make an editMessageMedia request. +type EditMessageMediaConfig struct { + BaseEdit + + Media interface{} +} + +func (EditMessageMediaConfig) method() string { + return "editMessageMedia" +} + +func (config EditMessageMediaConfig) params() (Params, error) { + params, err := config.BaseEdit.params() + if err != nil { + return params, err + } + + err = params.AddInterface("media", prepareInputMediaParam(config.Media, 0)) + + return params, err +} + +func (config EditMessageMediaConfig) files() []RequestFile { + return prepareInputMediaFile(config.Media, 0) +} + +// EditMessageReplyMarkupConfig allows you to modify the reply markup +// of a message. +type EditMessageReplyMarkupConfig struct { + BaseEdit +} + +func (config EditMessageReplyMarkupConfig) params() (Params, error) { + return config.BaseEdit.params() +} + +func (config EditMessageReplyMarkupConfig) method() string { + return "editMessageReplyMarkup" +} + +// StopPollConfig allows you to stop a poll sent by the bot. +type StopPollConfig struct { + BaseEdit +} + +func (config StopPollConfig) params() (Params, error) { + return config.BaseEdit.params() +} + +func (StopPollConfig) method() string { + return "stopPoll" +} + +// UserProfilePhotosConfig contains information about a +// GetUserProfilePhotos request. +type UserProfilePhotosConfig struct { + UserID int64 + Offset int + Limit int +} + +func (UserProfilePhotosConfig) method() string { + return "getUserProfilePhotos" +} + +func (config UserProfilePhotosConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserID) + params.AddNonZero("offset", config.Offset) + params.AddNonZero("limit", config.Limit) + + return params, nil +} + +// FileConfig has information about a file hosted on Telegram. +type FileConfig struct { + FileID string +} + +func (FileConfig) method() string { + return "getFile" +} + +func (config FileConfig) params() (Params, error) { + params := make(Params) + + params["file_id"] = config.FileID + + return params, nil +} + +// UpdateConfig contains information about a GetUpdates request. +type UpdateConfig struct { + Offset int + Limit int + Timeout int + AllowedUpdates []string +} + +func (UpdateConfig) method() string { + return "getUpdates" +} + +func (config UpdateConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero("offset", config.Offset) + params.AddNonZero("limit", config.Limit) + params.AddNonZero("timeout", config.Timeout) + params.AddInterface("allowed_updates", config.AllowedUpdates) + + return params, nil +} + +// WebhookConfig contains information about a SetWebhook request. +type WebhookConfig struct { + URL *url.URL + Certificate RequestFileData + IPAddress string + MaxConnections int + AllowedUpdates []string + DropPendingUpdates bool +} + +func (config WebhookConfig) method() string { + return "setWebhook" +} + +func (config WebhookConfig) params() (Params, error) { + params := make(Params) + + if config.URL != nil { + params["url"] = config.URL.String() + } + + params.AddNonEmpty("ip_address", config.IPAddress) + params.AddNonZero("max_connections", config.MaxConnections) + err := params.AddInterface("allowed_updates", config.AllowedUpdates) + params.AddBool("drop_pending_updates", config.DropPendingUpdates) + + return params, err +} + +func (config WebhookConfig) files() []RequestFile { + if config.Certificate != nil { + return []RequestFile{{ + Name: "certificate", + Data: config.Certificate, + }} + } + + return nil +} + +// DeleteWebhookConfig is a helper to delete a webhook. +type DeleteWebhookConfig struct { + DropPendingUpdates bool +} + +func (config DeleteWebhookConfig) method() string { + return "deleteWebhook" +} + +func (config DeleteWebhookConfig) params() (Params, error) { + params := make(Params) + + params.AddBool("drop_pending_updates", config.DropPendingUpdates) + + return params, nil +} + +// InlineConfig contains information on making an InlineQuery response. +type InlineConfig struct { + InlineQueryID string `json:"inline_query_id"` + Results []interface{} `json:"results"` + CacheTime int `json:"cache_time"` + IsPersonal bool `json:"is_personal"` + NextOffset string `json:"next_offset"` + SwitchPMText string `json:"switch_pm_text"` + SwitchPMParameter string `json:"switch_pm_parameter"` +} + +func (config InlineConfig) method() string { + return "answerInlineQuery" +} + +func (config InlineConfig) params() (Params, error) { + params := make(Params) + + params["inline_query_id"] = config.InlineQueryID + params.AddNonZero("cache_time", config.CacheTime) + params.AddBool("is_personal", config.IsPersonal) + params.AddNonEmpty("next_offset", config.NextOffset) + params.AddNonEmpty("switch_pm_text", config.SwitchPMText) + params.AddNonEmpty("switch_pm_parameter", config.SwitchPMParameter) + err := params.AddInterface("results", config.Results) + + return params, err +} + +// CallbackConfig contains information on making a CallbackQuery response. +type CallbackConfig struct { + CallbackQueryID string `json:"callback_query_id"` + Text string `json:"text"` + ShowAlert bool `json:"show_alert"` + URL string `json:"url"` + CacheTime int `json:"cache_time"` +} + +func (config CallbackConfig) method() string { + return "answerCallbackQuery" +} + +func (config CallbackConfig) params() (Params, error) { + params := make(Params) + + params["callback_query_id"] = config.CallbackQueryID + params.AddNonEmpty("text", config.Text) + params.AddBool("show_alert", config.ShowAlert) + params.AddNonEmpty("url", config.URL) + params.AddNonZero("cache_time", config.CacheTime) + + return params, nil +} + +// ChatMemberConfig contains information about a user in a chat for use +// with administrative functions such as kicking or unbanning a user. +type ChatMemberConfig struct { + ChatID int64 + SuperGroupUsername string + ChannelUsername string + UserID int64 +} + +// UnbanChatMemberConfig allows you to unban a user. +type UnbanChatMemberConfig struct { + ChatMemberConfig + OnlyIfBanned bool +} + +func (config UnbanChatMemberConfig) method() string { + return "unbanChatMember" +} + +func (config UnbanChatMemberConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero64("user_id", config.UserID) + params.AddBool("only_if_banned", config.OnlyIfBanned) + + return params, nil +} + +// BanChatMemberConfig contains extra fields to kick user. +type BanChatMemberConfig struct { + ChatMemberConfig + UntilDate int64 + RevokeMessages bool +} + +func (config BanChatMemberConfig) method() string { + return "banChatMember" +} + +func (config BanChatMemberConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero64("user_id", config.UserID) + params.AddNonZero64("until_date", config.UntilDate) + params.AddBool("revoke_messages", config.RevokeMessages) + + return params, nil +} + +// KickChatMemberConfig contains extra fields to ban user. +// +// This was renamed to BanChatMember in later versions of the Telegram Bot API. +type KickChatMemberConfig = BanChatMemberConfig + +// RestrictChatMemberConfig contains fields to restrict members of chat +type RestrictChatMemberConfig struct { + ChatMemberConfig + UntilDate int64 + Permissions *ChatPermissions +} + +func (config RestrictChatMemberConfig) method() string { + return "restrictChatMember" +} + +func (config RestrictChatMemberConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero64("user_id", config.UserID) + + err := params.AddInterface("permissions", config.Permissions) + params.AddNonZero64("until_date", config.UntilDate) + + return params, err +} + +// PromoteChatMemberConfig contains fields to promote members of chat +type PromoteChatMemberConfig struct { + ChatMemberConfig + IsAnonymous bool + CanManageChat bool + CanChangeInfo bool + CanPostMessages bool + CanEditMessages bool + CanDeleteMessages bool + CanManageVoiceChats bool + CanInviteUsers bool + CanRestrictMembers bool + CanPinMessages bool + CanPromoteMembers bool +} + +func (config PromoteChatMemberConfig) method() string { + return "promoteChatMember" +} + +func (config PromoteChatMemberConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero64("user_id", config.UserID) + + params.AddBool("is_anonymous", config.IsAnonymous) + params.AddBool("can_manage_chat", config.CanManageChat) + params.AddBool("can_change_info", config.CanChangeInfo) + params.AddBool("can_post_messages", config.CanPostMessages) + params.AddBool("can_edit_messages", config.CanEditMessages) + params.AddBool("can_delete_messages", config.CanDeleteMessages) + params.AddBool("can_manage_voice_chats", config.CanManageVoiceChats) + params.AddBool("can_invite_users", config.CanInviteUsers) + params.AddBool("can_restrict_members", config.CanRestrictMembers) + params.AddBool("can_pin_messages", config.CanPinMessages) + params.AddBool("can_promote_members", config.CanPromoteMembers) + + return params, nil +} + +// SetChatAdministratorCustomTitle sets the title of an administrative user +// promoted by the bot for a chat. +type SetChatAdministratorCustomTitle struct { + ChatMemberConfig + CustomTitle string +} + +func (SetChatAdministratorCustomTitle) method() string { + return "setChatAdministratorCustomTitle" +} + +func (config SetChatAdministratorCustomTitle) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero64("user_id", config.UserID) + params.AddNonEmpty("custom_title", config.CustomTitle) + + return params, nil +} + +// BanChatSenderChatConfig bans a channel chat in a supergroup or a channel. The +// owner of the chat will not be able to send messages and join live streams on +// behalf of the chat, unless it is unbanned first. The bot must be an +// administrator in the supergroup or channel for this to work and must have the +// appropriate administrator rights. +type BanChatSenderChatConfig struct { + ChatID int64 + ChannelUsername string + SenderChatID int64 + UntilDate int +} + +func (config BanChatSenderChatConfig) method() string { + return "banChatSenderChat" +} + +func (config BanChatSenderChatConfig) params() (Params, error) { + params := make(Params) + + _ = params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero64("sender_chat_id", config.SenderChatID) + params.AddNonZero("until_date", config.UntilDate) + + return params, nil +} + +// UnbanChatSenderChatConfig unbans a previously banned channel chat in a +// supergroup or channel. The bot must be an administrator for this to work and +// must have the appropriate administrator rights. +type UnbanChatSenderChatConfig struct { + ChatID int64 + ChannelUsername string + SenderChatID int64 +} + +func (config UnbanChatSenderChatConfig) method() string { + return "unbanChatSenderChat" +} + +func (config UnbanChatSenderChatConfig) params() (Params, error) { + params := make(Params) + + _ = params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero64("sender_chat_id", config.SenderChatID) + + return params, nil +} + +// ChatConfig contains information about getting information on a chat. +type ChatConfig struct { + ChatID int64 + SuperGroupUsername string +} + +func (config ChatConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + + return params, nil +} + +// ChatInfoConfig contains information about getting chat information. +type ChatInfoConfig struct { + ChatConfig +} + +func (ChatInfoConfig) method() string { + return "getChat" +} + +// ChatMemberCountConfig contains information about getting the number of users in a chat. +type ChatMemberCountConfig struct { + ChatConfig +} + +func (ChatMemberCountConfig) method() string { + return "getChatMembersCount" +} + +// ChatAdministratorsConfig contains information about getting chat administrators. +type ChatAdministratorsConfig struct { + ChatConfig +} + +func (ChatAdministratorsConfig) method() string { + return "getChatAdministrators" +} + +// SetChatPermissionsConfig allows you to set default permissions for the +// members in a group. The bot must be an administrator and have rights to +// restrict members. +type SetChatPermissionsConfig struct { + ChatConfig + Permissions *ChatPermissions +} + +func (SetChatPermissionsConfig) method() string { + return "setChatPermissions" +} + +func (config SetChatPermissionsConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + err := params.AddInterface("permissions", config.Permissions) + + return params, err +} + +// ChatInviteLinkConfig contains information about getting a chat link. +// +// Note that generating a new link will revoke any previous links. +type ChatInviteLinkConfig struct { + ChatConfig +} + +func (ChatInviteLinkConfig) method() string { + return "exportChatInviteLink" +} + +func (config ChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + + return params, nil +} + +// CreateChatInviteLinkConfig allows you to create an additional invite link for +// a chat. The bot must be an administrator in the chat for this to work and +// must have the appropriate admin rights. The link can be revoked using the +// RevokeChatInviteLinkConfig. +type CreateChatInviteLinkConfig struct { + ChatConfig + Name string + ExpireDate int + MemberLimit int + CreatesJoinRequest bool +} + +func (CreateChatInviteLinkConfig) method() string { + return "createChatInviteLink" +} + +func (config CreateChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddNonEmpty("name", config.Name) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("expire_date", config.ExpireDate) + params.AddNonZero("member_limit", config.MemberLimit) + params.AddBool("creates_join_request", config.CreatesJoinRequest) + + return params, nil +} + +// EditChatInviteLinkConfig allows you to edit a non-primary invite link created +// by the bot. The bot must be an administrator in the chat for this to work and +// must have the appropriate admin rights. +type EditChatInviteLinkConfig struct { + ChatConfig + InviteLink string + Name string + ExpireDate int + MemberLimit int + CreatesJoinRequest bool +} + +func (EditChatInviteLinkConfig) method() string { + return "editChatInviteLink" +} + +func (config EditChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonEmpty("name", config.Name) + params["invite_link"] = config.InviteLink + params.AddNonZero("expire_date", config.ExpireDate) + params.AddNonZero("member_limit", config.MemberLimit) + params.AddBool("creates_join_request", config.CreatesJoinRequest) + + return params, nil +} + +// RevokeChatInviteLinkConfig allows you to revoke an invite link created by the +// bot. If the primary link is revoked, a new link is automatically generated. +// The bot must be an administrator in the chat for this to work and must have +// the appropriate admin rights. +type RevokeChatInviteLinkConfig struct { + ChatConfig + InviteLink string +} + +func (RevokeChatInviteLinkConfig) method() string { + return "revokeChatInviteLink" +} + +func (config RevokeChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params["invite_link"] = config.InviteLink + + return params, nil +} + +// ApproveChatJoinRequestConfig allows you to approve a chat join request. +type ApproveChatJoinRequestConfig struct { + ChatConfig + UserID int64 +} + +func (ApproveChatJoinRequestConfig) method() string { + return "approveChatJoinRequest" +} + +func (config ApproveChatJoinRequestConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("user_id", int(config.UserID)) + + return params, nil +} + +// DeclineChatJoinRequest allows you to decline a chat join request. +type DeclineChatJoinRequest struct { + ChatConfig + UserID int64 +} + +func (DeclineChatJoinRequest) method() string { + return "declineChatJoinRequest" +} + +func (config DeclineChatJoinRequest) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("user_id", int(config.UserID)) + + return params, nil +} + +// LeaveChatConfig allows you to leave a chat. +type LeaveChatConfig struct { + ChatID int64 + ChannelUsername string +} + +func (config LeaveChatConfig) method() string { + return "leaveChat" +} + +func (config LeaveChatConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + + return params, nil +} + +// ChatConfigWithUser contains information about a chat and a user. +type ChatConfigWithUser struct { + ChatID int64 + SuperGroupUsername string + UserID int64 +} + +func (config ChatConfigWithUser) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero64("user_id", config.UserID) + + return params, nil +} + +// GetChatMemberConfig is information about getting a specific member in a chat. +type GetChatMemberConfig struct { + ChatConfigWithUser +} + +func (GetChatMemberConfig) method() string { + return "getChatMember" +} + +// InvoiceConfig contains information for sendInvoice request. +type InvoiceConfig struct { + BaseChat + Title string // required + Description string // required + Payload string // required + ProviderToken string // required + Currency string // required + Prices []LabeledPrice // required + MaxTipAmount int + SuggestedTipAmounts []int + StartParameter string + ProviderData string + PhotoURL string + PhotoSize int + PhotoWidth int + PhotoHeight int + NeedName bool + NeedPhoneNumber bool + NeedEmail bool + NeedShippingAddress bool + SendPhoneNumberToProvider bool + SendEmailToProvider bool + IsFlexible bool +} + +func (config InvoiceConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params["title"] = config.Title + params["description"] = config.Description + params["payload"] = config.Payload + params["provider_token"] = config.ProviderToken + params["currency"] = config.Currency + if err = params.AddInterface("prices", config.Prices); err != nil { + return params, err + } + + params.AddNonZero("max_tip_amount", config.MaxTipAmount) + err = params.AddInterface("suggested_tip_amounts", config.SuggestedTipAmounts) + params.AddNonEmpty("start_parameter", config.StartParameter) + params.AddNonEmpty("provider_data", config.ProviderData) + params.AddNonEmpty("photo_url", config.PhotoURL) + params.AddNonZero("photo_size", config.PhotoSize) + params.AddNonZero("photo_width", config.PhotoWidth) + params.AddNonZero("photo_height", config.PhotoHeight) + params.AddBool("need_name", config.NeedName) + params.AddBool("need_phone_number", config.NeedPhoneNumber) + params.AddBool("need_email", config.NeedEmail) + params.AddBool("need_shipping_address", config.NeedShippingAddress) + params.AddBool("is_flexible", config.IsFlexible) + params.AddBool("send_phone_number_to_provider", config.SendPhoneNumberToProvider) + params.AddBool("send_email_to_provider", config.SendEmailToProvider) + + return params, err +} + +func (config InvoiceConfig) method() string { + return "sendInvoice" +} + +// ShippingConfig contains information for answerShippingQuery request. +type ShippingConfig struct { + ShippingQueryID string // required + OK bool // required + ShippingOptions []ShippingOption + ErrorMessage string +} + +func (config ShippingConfig) method() string { + return "answerShippingQuery" +} + +func (config ShippingConfig) params() (Params, error) { + params := make(Params) + + params["shipping_query_id"] = config.ShippingQueryID + params.AddBool("ok", config.OK) + err := params.AddInterface("shipping_options", config.ShippingOptions) + params.AddNonEmpty("error_message", config.ErrorMessage) + + return params, err +} + +// PreCheckoutConfig conatins information for answerPreCheckoutQuery request. +type PreCheckoutConfig struct { + PreCheckoutQueryID string // required + OK bool // required + ErrorMessage string +} + +func (config PreCheckoutConfig) method() string { + return "answerPreCheckoutQuery" +} + +func (config PreCheckoutConfig) params() (Params, error) { + params := make(Params) + + params["pre_checkout_query_id"] = config.PreCheckoutQueryID + params.AddBool("ok", config.OK) + params.AddNonEmpty("error_message", config.ErrorMessage) + + return params, nil +} + +// DeleteMessageConfig contains information of a message in a chat to delete. +type DeleteMessageConfig struct { + ChannelUsername string + ChatID int64 + MessageID int +} + +func (config DeleteMessageConfig) method() string { + return "deleteMessage" +} + +func (config DeleteMessageConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + + return params, nil +} + +// PinChatMessageConfig contains information of a message in a chat to pin. +type PinChatMessageConfig struct { + ChatID int64 + ChannelUsername string + MessageID int + DisableNotification bool +} + +func (config PinChatMessageConfig) method() string { + return "pinChatMessage" +} + +func (config PinChatMessageConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + params.AddBool("disable_notification", config.DisableNotification) + + return params, nil +} + +// UnpinChatMessageConfig contains information of a chat message to unpin. +// +// If MessageID is not specified, it will unpin the most recent pin. +type UnpinChatMessageConfig struct { + ChatID int64 + ChannelUsername string + MessageID int +} + +func (config UnpinChatMessageConfig) method() string { + return "unpinChatMessage" +} + +func (config UnpinChatMessageConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + + return params, nil +} + +// UnpinAllChatMessagesConfig contains information of all messages to unpin in +// a chat. +type UnpinAllChatMessagesConfig struct { + ChatID int64 + ChannelUsername string +} + +func (config UnpinAllChatMessagesConfig) method() string { + return "unpinAllChatMessages" +} + +func (config UnpinAllChatMessagesConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + + return params, nil +} + +// SetChatPhotoConfig allows you to set a group, supergroup, or channel's photo. +type SetChatPhotoConfig struct { + BaseFile +} + +func (config SetChatPhotoConfig) method() string { + return "setChatPhoto" +} + +func (config SetChatPhotoConfig) files() []RequestFile { + return []RequestFile{{ + Name: "photo", + Data: config.File, + }} +} + +// DeleteChatPhotoConfig allows you to delete a group, supergroup, or channel's photo. +type DeleteChatPhotoConfig struct { + ChatID int64 + ChannelUsername string +} + +func (config DeleteChatPhotoConfig) method() string { + return "deleteChatPhoto" +} + +func (config DeleteChatPhotoConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + + return params, nil +} + +// SetChatTitleConfig allows you to set the title of something other than a private chat. +type SetChatTitleConfig struct { + ChatID int64 + ChannelUsername string + + Title string +} + +func (config SetChatTitleConfig) method() string { + return "setChatTitle" +} + +func (config SetChatTitleConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params["title"] = config.Title + + return params, nil +} + +// SetChatDescriptionConfig allows you to set the description of a supergroup or channel. +type SetChatDescriptionConfig struct { + ChatID int64 + ChannelUsername string + + Description string +} + +func (config SetChatDescriptionConfig) method() string { + return "setChatDescription" +} + +func (config SetChatDescriptionConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params["description"] = config.Description + + return params, nil +} + +// GetStickerSetConfig allows you to get the stickers in a set. +type GetStickerSetConfig struct { + Name string +} + +func (config GetStickerSetConfig) method() string { + return "getStickerSet" +} + +func (config GetStickerSetConfig) params() (Params, error) { + params := make(Params) + + params["name"] = config.Name + + return params, nil +} + +// UploadStickerConfig allows you to upload a sticker for use in a set later. +type UploadStickerConfig struct { + UserID int64 + PNGSticker RequestFileData +} + +func (config UploadStickerConfig) method() string { + return "uploadStickerFile" +} + +func (config UploadStickerConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserID) + + return params, nil +} + +func (config UploadStickerConfig) files() []RequestFile { + return []RequestFile{{ + Name: "png_sticker", + Data: config.PNGSticker, + }} +} + +// NewStickerSetConfig allows creating a new sticker set. +// +// You must set either PNGSticker or TGSSticker. +type NewStickerSetConfig struct { + UserID int64 + Name string + Title string + PNGSticker RequestFileData + TGSSticker RequestFileData + Emojis string + ContainsMasks bool + MaskPosition *MaskPosition +} + +func (config NewStickerSetConfig) method() string { + return "createNewStickerSet" +} + +func (config NewStickerSetConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserID) + params["name"] = config.Name + params["title"] = config.Title + + params["emojis"] = config.Emojis + + params.AddBool("contains_masks", config.ContainsMasks) + + err := params.AddInterface("mask_position", config.MaskPosition) + + return params, err +} + +func (config NewStickerSetConfig) files() []RequestFile { + if config.PNGSticker != nil { + return []RequestFile{{ + Name: "png_sticker", + Data: config.PNGSticker, + }} + } + + return []RequestFile{{ + Name: "tgs_sticker", + Data: config.TGSSticker, + }} +} + +// AddStickerConfig allows you to add a sticker to a set. +type AddStickerConfig struct { + UserID int64 + Name string + PNGSticker RequestFileData + TGSSticker RequestFileData + Emojis string + MaskPosition *MaskPosition +} + +func (config AddStickerConfig) method() string { + return "addStickerToSet" +} + +func (config AddStickerConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero64("user_id", config.UserID) + params["name"] = config.Name + params["emojis"] = config.Emojis + + err := params.AddInterface("mask_position", config.MaskPosition) + + return params, err +} + +func (config AddStickerConfig) files() []RequestFile { + if config.PNGSticker != nil { + return []RequestFile{{ + Name: "png_sticker", + Data: config.PNGSticker, + }} + } + + return []RequestFile{{ + Name: "tgs_sticker", + Data: config.TGSSticker, + }} + +} + +// SetStickerPositionConfig allows you to change the position of a sticker in a set. +type SetStickerPositionConfig struct { + Sticker string + Position int +} + +func (config SetStickerPositionConfig) method() string { + return "setStickerPositionInSet" +} + +func (config SetStickerPositionConfig) params() (Params, error) { + params := make(Params) + + params["sticker"] = config.Sticker + params.AddNonZero("position", config.Position) + + return params, nil +} + +// DeleteStickerConfig allows you to delete a sticker from a set. +type DeleteStickerConfig struct { + Sticker string +} + +func (config DeleteStickerConfig) method() string { + return "deleteStickerFromSet" +} + +func (config DeleteStickerConfig) params() (Params, error) { + params := make(Params) + + params["sticker"] = config.Sticker + + return params, nil +} + +// SetStickerSetThumbConfig allows you to set the thumbnail for a sticker set. +type SetStickerSetThumbConfig struct { + Name string + UserID int64 + Thumb RequestFileData +} + +func (config SetStickerSetThumbConfig) method() string { + return "setStickerSetThumb" +} + +func (config SetStickerSetThumbConfig) params() (Params, error) { + params := make(Params) + + params["name"] = config.Name + params.AddNonZero64("user_id", config.UserID) + + return params, nil +} + +func (config SetStickerSetThumbConfig) files() []RequestFile { + return []RequestFile{{ + Name: "thumb", + Data: config.Thumb, + }} +} + +// SetChatStickerSetConfig allows you to set the sticker set for a supergroup. +type SetChatStickerSetConfig struct { + ChatID int64 + SuperGroupUsername string + + StickerSetName string +} + +func (config SetChatStickerSetConfig) method() string { + return "setChatStickerSet" +} + +func (config SetChatStickerSetConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params["sticker_set_name"] = config.StickerSetName + + return params, nil +} + +// DeleteChatStickerSetConfig allows you to remove a supergroup's sticker set. +type DeleteChatStickerSetConfig struct { + ChatID int64 + SuperGroupUsername string +} + +func (config DeleteChatStickerSetConfig) method() string { + return "deleteChatStickerSet" +} + +func (config DeleteChatStickerSetConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + + return params, nil +} + +// MediaGroupConfig allows you to send a group of media. +// +// Media consist of InputMedia items (InputMediaPhoto, InputMediaVideo). +type MediaGroupConfig struct { + ChatID int64 + ChannelUsername string + + Media []interface{} + DisableNotification bool + ReplyToMessageID int +} + +func (config MediaGroupConfig) method() string { + return "sendMediaGroup" +} + +func (config MediaGroupConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddBool("disable_notification", config.DisableNotification) + params.AddNonZero("reply_to_message_id", config.ReplyToMessageID) + + err := params.AddInterface("media", prepareInputMediaForParams(config.Media)) + + return params, err +} + +func (config MediaGroupConfig) files() []RequestFile { + return prepareInputMediaForFiles(config.Media) +} + +// DiceConfig contains information about a sendDice request. +type DiceConfig struct { + BaseChat + // Emoji on which the dice throw animation is based. + // Currently, must be one of 🎲, 🎯, 🏀, ⚽, 🎳, or 🎰. + // Dice can have values 1-6 for 🎲, 🎯, and 🎳, values 1-5 for 🏀 and ⚽, + // and values 1-64 for 🎰. + // Defaults to “🎲” + Emoji string +} + +func (config DiceConfig) method() string { + return "sendDice" +} + +func (config DiceConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddNonEmpty("emoji", config.Emoji) + + return params, err +} + +// GetMyCommandsConfig gets a list of the currently registered commands. +type GetMyCommandsConfig struct { + Scope *BotCommandScope + LanguageCode string +} + +func (config GetMyCommandsConfig) method() string { + return "getMyCommands" +} + +func (config GetMyCommandsConfig) params() (Params, error) { + params := make(Params) + + err := params.AddInterface("scope", config.Scope) + params.AddNonEmpty("language_code", config.LanguageCode) + + return params, err +} + +// SetMyCommandsConfig sets a list of commands the bot understands. +type SetMyCommandsConfig struct { + Commands []BotCommand + Scope *BotCommandScope + LanguageCode string +} + +func (config SetMyCommandsConfig) method() string { + return "setMyCommands" +} + +func (config SetMyCommandsConfig) params() (Params, error) { + params := make(Params) + + if err := params.AddInterface("commands", config.Commands); err != nil { + return params, err + } + err := params.AddInterface("scope", config.Scope) + params.AddNonEmpty("language_code", config.LanguageCode) + + return params, err +} + +type DeleteMyCommandsConfig struct { + Scope *BotCommandScope + LanguageCode string +} + +func (config DeleteMyCommandsConfig) method() string { + return "deleteMyCommands" +} + +func (config DeleteMyCommandsConfig) params() (Params, error) { + params := make(Params) + + err := params.AddInterface("scope", config.Scope) + params.AddNonEmpty("language_code", config.LanguageCode) + + return params, err +} + +// prepareInputMediaParam evaluates a single InputMedia and determines if it +// needs to be modified for a successful upload. If it returns nil, then the +// value does not need to be included in the params. Otherwise, it will return +// the same type as was originally provided. +// +// The idx is used to calculate the file field name. If you only have a single +// file, 0 may be used. It is formatted into "attach://file-%d" for the primary +// media and "attach://file-%d-thumb" for thumbnails. +// +// It is expected to be used in conjunction with prepareInputMediaFile. +func prepareInputMediaParam(inputMedia interface{}, idx int) interface{} { + switch m := inputMedia.(type) { + case InputMediaPhoto: + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) + } + + return m + case InputMediaVideo: + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) + } + + if m.Thumb != nil && m.Thumb.NeedsUpload() { + m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx)) + } + + return m + case InputMediaAudio: + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) + } + + if m.Thumb != nil && m.Thumb.NeedsUpload() { + m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx)) + } + + return m + case InputMediaDocument: + if m.Media.NeedsUpload() { + m.Media = fileAttach(fmt.Sprintf("attach://file-%d", idx)) + } + + if m.Thumb != nil && m.Thumb.NeedsUpload() { + m.Thumb = fileAttach(fmt.Sprintf("attach://file-%d-thumb", idx)) + } + + return m + } + + return nil +} + +// prepareInputMediaFile generates an array of RequestFile to provide for +// Fileable's files method. It returns an array as a single InputMedia may have +// multiple files, for the primary media and a thumbnail. +// +// The idx parameter is used to generate file field names. It uses the names +// "file-%d" for the main file and "file-%d-thumb" for the thumbnail. +// +// It is expected to be used in conjunction with prepareInputMediaParam. +func prepareInputMediaFile(inputMedia interface{}, idx int) []RequestFile { + files := []RequestFile{} + + switch m := inputMedia.(type) { + case InputMediaPhoto: + if m.Media.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Media, + }) + } + case InputMediaVideo: + if m.Media.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Media, + }) + } + + if m.Thumb != nil && m.Thumb.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Thumb, + }) + } + case InputMediaDocument: + if m.Media.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Media, + }) + } + + if m.Thumb != nil && m.Thumb.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Thumb, + }) + } + case InputMediaAudio: + if m.Media.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Media, + }) + } + + if m.Thumb != nil && m.Thumb.NeedsUpload() { + files = append(files, RequestFile{ + Name: fmt.Sprintf("file-%d", idx), + Data: m.Thumb, + }) + } + } + + return files +} + +// prepareInputMediaForParams calls prepareInputMediaParam for each item +// provided and returns a new array with the correct params for a request. +// +// It is expected that files will get data from the associated function, +// prepareInputMediaForFiles. +func prepareInputMediaForParams(inputMedia []interface{}) []interface{} { + newMedia := make([]interface{}, len(inputMedia)) + copy(newMedia, inputMedia) + + for idx, media := range inputMedia { + if param := prepareInputMediaParam(media, idx); param != nil { + newMedia[idx] = param + } + } + + return newMedia +} + +// prepareInputMediaForFiles calls prepareInputMediaFile for each item +// provided and returns a new array with the correct files for a request. +// +// It is expected that params will get data from the associated function, +// prepareInputMediaForParams. +func prepareInputMediaForFiles(inputMedia []interface{}) []RequestFile { + files := []RequestFile{} + + for idx, media := range inputMedia { + if file := prepareInputMediaFile(media, idx); file != nil { + files = append(files, file...) + } + } + + return files +} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/helpers.go index d2fb165a..8220e74e 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/helpers.go +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/helpers.go @@ -52,269 +52,177 @@ func NewForward(chatID int64, fromChatID int64, messageID int) ForwardConfig { } } -// NewPhotoUpload creates a new photo uploader. +// NewCopyMessage creates a new copy message. +// +// chatID is where to send it, fromChatID is the source chat, +// and messageID is the ID of the original message. +func NewCopyMessage(chatID int64, fromChatID int64, messageID int) CopyMessageConfig { + return CopyMessageConfig{ + BaseChat: BaseChat{ChatID: chatID}, + FromChatID: fromChatID, + MessageID: messageID, + } +} + +// NewPhoto creates a new sendPhoto request. // // chatID is where to send it, file is a string path to the file, // FileReader, or FileBytes. // // Note that you must send animated GIFs as a document. -func NewPhotoUpload(chatID int64, file interface{}) PhotoConfig { +func NewPhoto(chatID int64, file RequestFileData) PhotoConfig { return PhotoConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// NewPhotoShare shares an existing photo. -// You may use this to reshare an existing photo without reuploading it. +// NewPhotoToChannel creates a new photo uploader to send a photo to a channel. // -// chatID is where to send it, fileID is the ID of the file -// already uploaded. -func NewPhotoShare(chatID int64, fileID string) PhotoConfig { +// Note that you must send animated GIFs as a document. +func NewPhotoToChannel(username string, file RequestFileData) PhotoConfig { return PhotoConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, - }, - } -} - -// NewAudioUpload creates a new audio uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -func NewAudioUpload(chatID int64, file interface{}) AudioConfig { - return AudioConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ + ChannelUsername: username, + }, + File: file, }, } } -// NewAudioShare shares an existing audio file. -// 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. -func NewAudioShare(chatID int64, fileID string) AudioConfig { +// NewAudio creates a new sendAudio request. +func NewAudio(chatID int64, file RequestFileData) AudioConfig { return AudioConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, - }, - } -} - -// NewDocumentUpload creates a new document uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -func NewDocumentUpload(chatID int64, file interface{}) DocumentConfig { - return DocumentConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// NewDocumentShare shares an existing document. -// 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. -func NewDocumentShare(chatID int64, fileID string) DocumentConfig { +// NewDocument creates a new sendDocument request. +func NewDocument(chatID int64, file RequestFileData) DocumentConfig { return DocumentConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, - }, - } -} - -// NewStickerUpload creates a new sticker uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -func NewStickerUpload(chatID int64, file interface{}) StickerConfig { - return StickerConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// NewStickerShare shares an existing sticker. -// 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. -func NewStickerShare(chatID int64, fileID string) StickerConfig { +// NewSticker creates a new sendSticker request. +func NewSticker(chatID int64, file RequestFileData) StickerConfig { return StickerConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// NewVideoUpload creates a new video uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -func NewVideoUpload(chatID int64, file interface{}) VideoConfig { +// NewVideo creates a new sendVideo request. +func NewVideo(chatID int64, file RequestFileData) VideoConfig { return VideoConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// 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. -func NewVideoShare(chatID int64, fileID string) VideoConfig { - return VideoConfig{ +// NewAnimation creates a new sendAnimation request. +func NewAnimation(chatID int64, file RequestFileData) AnimationConfig { + return AnimationConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// NewAnimationUpload creates a new animation uploader. +// NewVideoNote creates a new sendVideoNote request. // // chatID is where to send it, file is a string path to the file, // FileReader, or FileBytes. -func NewAnimationUpload(chatID int64, file interface{}) AnimationConfig { - return AnimationConfig{ +func NewVideoNote(chatID int64, length int, file RequestFileData) VideoNoteConfig { + return VideoNoteConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, + Length: length, } } -// NewAnimationShare shares an existing animation. -// You may use this to reshare an existing animation without reuploading it. -// -// chatID is where to send it, fileID is the ID of the animation -// already uploaded. -func NewAnimationShare(chatID int64, fileID string) AnimationConfig { - return AnimationConfig{ +// NewVoice creates a new sendVoice request. +func NewVoice(chatID int64, file RequestFileData) VoiceConfig { + return VoiceConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, + BaseChat: BaseChat{ChatID: chatID}, + File: file, }, } } -// NewVideoNoteUpload creates a new video note uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -func NewVideoNoteUpload(chatID int64, length int, file interface{}) VideoNoteConfig { - return VideoNoteConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, - }, - Length: length, +// NewMediaGroup creates a new media group. Files should be an array of +// two to ten InputMediaPhoto or InputMediaVideo. +func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig { + return MediaGroupConfig{ + ChatID: chatID, + Media: files, } } -// NewVideoNoteShare 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. -func NewVideoNoteShare(chatID int64, length int, fileID string) VideoNoteConfig { - return VideoNoteConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, +// NewInputMediaPhoto creates a new InputMediaPhoto. +func NewInputMediaPhoto(media RequestFileData) InputMediaPhoto { + return InputMediaPhoto{ + BaseInputMedia{ + Type: "photo", + Media: media, }, - Length: length, } } -// NewVoiceUpload creates a new voice uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -func NewVoiceUpload(chatID int64, file interface{}) VoiceConfig { - return VoiceConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, +// NewInputMediaVideo creates a new InputMediaVideo. +func NewInputMediaVideo(media RequestFileData) InputMediaVideo { + return InputMediaVideo{ + BaseInputMedia: BaseInputMedia{ + Type: "video", + Media: media, }, } } -// 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. -func NewVoiceShare(chatID int64, fileID string) VoiceConfig { - return VoiceConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, +// NewInputMediaAnimation creates a new InputMediaAnimation. +func NewInputMediaAnimation(media RequestFileData) InputMediaAnimation { + return InputMediaAnimation{ + BaseInputMedia: BaseInputMedia{ + Type: "animation", + Media: media, }, } } -// NewMediaGroup creates a new media group. Files should be an array of -// two to ten InputMediaPhoto or InputMediaVideo. -func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig { - return MediaGroupConfig{ - BaseChat: BaseChat{ - ChatID: chatID, +// NewInputMediaAudio creates a new InputMediaAudio. +func NewInputMediaAudio(media RequestFileData) InputMediaAudio { + return InputMediaAudio{ + BaseInputMedia: BaseInputMedia{ + Type: "audio", + Media: media, }, - InputMedia: files, } } -// NewInputMediaPhoto creates a new InputMediaPhoto. -func NewInputMediaPhoto(media string) InputMediaPhoto { - return InputMediaPhoto{ - Type: "photo", - Media: media, - } -} - -// NewInputMediaVideo creates a new InputMediaVideo. -func NewInputMediaVideo(media string) InputMediaVideo { - return InputMediaVideo{ - Type: "video", - Media: media, +// NewInputMediaDocument creates a new InputMediaDocument. +func NewInputMediaDocument(media RequestFileData) InputMediaDocument { + return InputMediaDocument{ + BaseInputMedia: BaseInputMedia{ + Type: "document", + Media: media, + }, } } @@ -369,7 +277,7 @@ func NewChatAction(chatID int64, action string) ChatActionConfig { // NewUserProfilePhotos gets user profile photos. // // userID is the ID of the user you wish to get profile photos from. -func NewUserProfilePhotos(userID int) UserProfilePhotosConfig { +func NewUserProfilePhotos(userID int64) UserProfilePhotosConfig { return UserProfilePhotosConfig{ UserID: userID, Offset: 0, @@ -392,25 +300,33 @@ func NewUpdate(offset int) UpdateConfig { // NewWebhook creates a new webhook. // // link is the url parsable link you wish to get the updates. -func NewWebhook(link string) WebhookConfig { - u, _ := url.Parse(link) +func NewWebhook(link string) (WebhookConfig, error) { + u, err := url.Parse(link) + + if err != nil { + return WebhookConfig{}, err + } return WebhookConfig{ URL: u, - } + }, nil } // NewWebhookWithCert creates a new webhook with a certificate. // // link is the url you wish to get webhooks, // file contains a string to a file, FileReader, or FileBytes. -func NewWebhookWithCert(link string, file interface{}) WebhookConfig { - u, _ := url.Parse(link) +func NewWebhookWithCert(link string, file RequestFileData) (WebhookConfig, error) { + u, err := url.Parse(link) + + if err != nil { + return WebhookConfig{}, err + } return WebhookConfig{ URL: u, Certificate: file, - } + }, nil } // NewInlineQueryResultArticle creates a new inline query article. @@ -438,6 +354,19 @@ func NewInlineQueryResultArticleMarkdown(id, title, messageText string) InlineQu } } +// NewInlineQueryResultArticleMarkdownV2 creates a new inline query article with MarkdownV2 parsing. +func NewInlineQueryResultArticleMarkdownV2(id, title, messageText string) InlineQueryResultArticle { + return InlineQueryResultArticle{ + Type: "article", + ID: id, + Title: title, + InputMessageContent: InputTextMessageContent{ + Text: messageText, + ParseMode: "MarkdownV2", + }, + } +} + // NewInlineQueryResultArticleHTML creates a new inline query article with HTML parsing. func NewInlineQueryResultArticleHTML(id, title, messageText string) InlineQueryResultArticle { return InlineQueryResultArticle{ @@ -465,7 +394,7 @@ func NewInlineQueryResultCachedGIF(id, gifID string) InlineQueryResultCachedGIF return InlineQueryResultCachedGIF{ Type: "gif", ID: id, - GifID: gifID, + GIFID: gifID, } } @@ -478,12 +407,12 @@ func NewInlineQueryResultMPEG4GIF(id, url string) InlineQueryResultMPEG4GIF { } } -// NewInlineQueryResultCachedPhoto create a new inline query with cached photo. -func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GifID string) InlineQueryResultCachedMpeg4Gif { - return InlineQueryResultCachedMpeg4Gif{ - Type: "mpeg4_gif", - ID: id, - MGifID: MPEG4GifID, +// NewInlineQueryResultCachedMPEG4GIF create a new inline query with cached MPEG4 GIF. +func NewInlineQueryResultCachedMPEG4GIF(id, MPEG4GIFID string) InlineQueryResultCachedMPEG4GIF { + return InlineQueryResultCachedMPEG4GIF{ + Type: "mpeg4_gif", + ID: id, + MPEG4FileID: MPEG4GIFID, } } @@ -534,6 +463,16 @@ func NewInlineQueryResultCachedVideo(id, videoID, title string) InlineQueryResul } } +// NewInlineQueryResultCachedSticker create a new inline query with cached sticker. +func NewInlineQueryResultCachedSticker(id, stickerID, title string) InlineQueryResultCachedSticker { + return InlineQueryResultCachedSticker{ + Type: "sticker", + ID: id, + StickerID: stickerID, + Title: title, + } +} + // NewInlineQueryResultAudio creates a new inline query audio. func NewInlineQueryResultAudio(id, url, title string) InlineQueryResultAudio { return InlineQueryResultAudio{ @@ -628,6 +567,18 @@ func NewEditMessageText(chatID int64, messageID int, text string) EditMessageTex } } +// NewEditMessageTextAndMarkup allows you to edit the text and replymarkup of a message. +func NewEditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) EditMessageTextConfig { + return EditMessageTextConfig{ + BaseEdit: BaseEdit{ + ChatID: chatID, + MessageID: messageID, + ReplyMarkup: &replyMarkup, + }, + Text: text, + } +} + // NewEditMessageCaption allows you to edit the caption of a message. func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMessageCaptionConfig { return EditMessageCaptionConfig{ @@ -651,17 +602,6 @@ func NewEditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKe } } -// NewHideKeyboard hides the keyboard, with the option for being selective -// or hiding for everyone. -func NewHideKeyboard(selective bool) ReplyKeyboardHide { - log.Println("NewHideKeyboard is deprecated, please use NewRemoveKeyboard") - - return ReplyKeyboardHide{ - HideKeyboard: true, - Selective: selective, - } -} - // NewRemoveKeyboard hides the keyboard, with the option for being selective // or hiding for everyone. func NewRemoveKeyboard(selective bool) ReplyKeyboardRemove { @@ -717,6 +657,13 @@ func NewReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup { } } +// NewOneTimeReplyKeyboard creates a new one time keyboard. +func NewOneTimeReplyKeyboard(rows ...[]KeyboardButton) ReplyKeyboardMarkup { + markup := NewReplyKeyboard(rows...) + markup.OneTimeKeyboard = true + return markup +} + // NewInlineKeyboardButtonData creates an inline keyboard button with text // and data for a callback. func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton { @@ -726,6 +673,15 @@ func NewInlineKeyboardButtonData(text, data string) InlineKeyboardButton { } } +// NewInlineKeyboardButtonLoginURL creates an inline keyboard button with text +// which goes to a LoginURL. +func NewInlineKeyboardButtonLoginURL(text string, loginURL LoginURL) InlineKeyboardButton { + return InlineKeyboardButton{ + Text: text, + LoginURL: &loginURL, + } +} + // NewInlineKeyboardButtonURL creates an inline keyboard button with text // which goes to a URL. func NewInlineKeyboardButtonURL(text, url string) InlineKeyboardButton { @@ -784,7 +740,7 @@ func NewCallbackWithAlert(id, text string) CallbackConfig { } // NewInvoice creates a new Invoice request to the user. -func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices *[]LabeledPrice) InvoiceConfig { +func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices []LabeledPrice) InvoiceConfig { return InvoiceConfig{ BaseChat: BaseChat{ChatID: chatID}, Title: title, @@ -796,33 +752,176 @@ func NewInvoice(chatID int64, title, description, payload, providerToken, startP Prices: prices} } -// NewSetChatPhotoUpload creates a new chat photo uploader. -// -// chatID is where to send it, file is a string path to the file, -// FileReader, or FileBytes. -// -// Note that you must send animated GIFs as a document. -func NewSetChatPhotoUpload(chatID int64, file interface{}) SetChatPhotoConfig { +// NewChatTitle allows you to update the title of a chat. +func NewChatTitle(chatID int64, title string) SetChatTitleConfig { + return SetChatTitleConfig{ + ChatID: chatID, + Title: title, + } +} + +// NewChatDescription allows you to update the description of a chat. +func NewChatDescription(chatID int64, description string) SetChatDescriptionConfig { + return SetChatDescriptionConfig{ + ChatID: chatID, + Description: description, + } +} + +// NewChatPhoto allows you to update the photo for a chat. +func NewChatPhoto(chatID int64, photo RequestFileData) SetChatPhotoConfig { return SetChatPhotoConfig{ BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - File: file, - UseExisting: false, + BaseChat: BaseChat{ + ChatID: chatID, + }, + File: photo, }, } } -// NewSetChatPhotoShare shares an existing photo. -// You may use this to reshare an existing photo without reuploading it. +// NewDeleteChatPhoto allows you to delete the photo for a chat. +func NewDeleteChatPhoto(chatID int64) DeleteChatPhotoConfig { + return DeleteChatPhotoConfig{ + ChatID: chatID, + } +} + +// NewPoll allows you to create a new poll. +func NewPoll(chatID int64, question string, options ...string) SendPollConfig { + return SendPollConfig{ + BaseChat: BaseChat{ + ChatID: chatID, + }, + Question: question, + Options: options, + IsAnonymous: true, // This is Telegram's default. + } +} + +// NewStopPoll allows you to stop a poll. +func NewStopPoll(chatID int64, messageID int) StopPollConfig { + return StopPollConfig{ + BaseEdit{ + ChatID: chatID, + MessageID: messageID, + }, + } +} + +// NewDice allows you to send a random dice roll. +func NewDice(chatID int64) DiceConfig { + return DiceConfig{ + BaseChat: BaseChat{ + ChatID: chatID, + }, + } +} + +// NewDiceWithEmoji allows you to send a random roll of one of many types. // -// chatID is where to send it, fileID is the ID of the file -// already uploaded. -func NewSetChatPhotoShare(chatID int64, fileID string) SetChatPhotoConfig { - return SetChatPhotoConfig{ - BaseFile: BaseFile{ - BaseChat: BaseChat{ChatID: chatID}, - FileID: fileID, - UseExisting: true, +// Emoji may be 🎲 (1-6), 🎯 (1-6), or 🏀 (1-5). +func NewDiceWithEmoji(chatID int64, emoji string) DiceConfig { + return DiceConfig{ + BaseChat: BaseChat{ + ChatID: chatID, }, + Emoji: emoji, } } + +// NewBotCommandScopeDefault represents the default scope of bot commands. +func NewBotCommandScopeDefault() BotCommandScope { + return BotCommandScope{Type: "default"} +} + +// NewBotCommandScopeAllPrivateChats represents the scope of bot commands, +// covering all private chats. +func NewBotCommandScopeAllPrivateChats() BotCommandScope { + return BotCommandScope{Type: "all_private_chats"} +} + +// NewBotCommandScopeAllGroupChats represents the scope of bot commands, +// covering all group and supergroup chats. +func NewBotCommandScopeAllGroupChats() BotCommandScope { + return BotCommandScope{Type: "all_group_chats"} +} + +// NewBotCommandScopeAllChatAdministrators represents the scope of bot commands, +// covering all group and supergroup chat administrators. +func NewBotCommandScopeAllChatAdministrators() BotCommandScope { + return BotCommandScope{Type: "all_chat_administrators"} +} + +// NewBotCommandScopeChat represents the scope of bot commands, covering a +// specific chat. +func NewBotCommandScopeChat(chatID int64) BotCommandScope { + return BotCommandScope{ + Type: "chat", + ChatID: chatID, + } +} + +// NewBotCommandScopeChatAdministrators represents the scope of bot commands, +// covering all administrators of a specific group or supergroup chat. +func NewBotCommandScopeChatAdministrators(chatID int64) BotCommandScope { + return BotCommandScope{ + Type: "chat_administrators", + ChatID: chatID, + } +} + +// NewBotCommandScopeChatMember represents the scope of bot commands, covering a +// specific member of a group or supergroup chat. +func NewBotCommandScopeChatMember(chatID, userID int64) BotCommandScope { + return BotCommandScope{ + Type: "chat_member", + ChatID: chatID, + UserID: userID, + } +} + +// NewGetMyCommandsWithScope allows you to set the registered commands for a +// given scope. +func NewGetMyCommandsWithScope(scope BotCommandScope) GetMyCommandsConfig { + return GetMyCommandsConfig{Scope: &scope} +} + +// NewGetMyCommandsWithScopeAndLanguage allows you to set the registered +// commands for a given scope and language code. +func NewGetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) GetMyCommandsConfig { + return GetMyCommandsConfig{Scope: &scope, LanguageCode: languageCode} +} + +// NewSetMyCommands allows you to set the registered commands. +func NewSetMyCommands(commands ...BotCommand) SetMyCommandsConfig { + return SetMyCommandsConfig{Commands: commands} +} + +// NewSetMyCommandsWithScope allows you to set the registered commands for a given scope. +func NewSetMyCommandsWithScope(scope BotCommandScope, commands ...BotCommand) SetMyCommandsConfig { + return SetMyCommandsConfig{Commands: commands, Scope: &scope} +} + +// NewSetMyCommandsWithScopeAndLanguage allows you to set the registered commands for a given scope +// and language code. +func NewSetMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string, commands ...BotCommand) SetMyCommandsConfig { + return SetMyCommandsConfig{Commands: commands, Scope: &scope, LanguageCode: languageCode} +} + +// NewDeleteMyCommands allows you to delete the registered commands. +func NewDeleteMyCommands() DeleteMyCommandsConfig { + return DeleteMyCommandsConfig{} +} + +// NewDeleteMyCommandsWithScope allows you to delete the registered commands for a given +// scope. +func NewDeleteMyCommandsWithScope(scope BotCommandScope) DeleteMyCommandsConfig { + return DeleteMyCommandsConfig{Scope: &scope} +} + +// NewDeleteMyCommandsWithScopeAndLanguage allows you to delete the registered commands for a given +// scope and language code. +func NewDeleteMyCommandsWithScopeAndLanguage(scope BotCommandScope, languageCode string) DeleteMyCommandsConfig { + return DeleteMyCommandsConfig{Scope: &scope, LanguageCode: languageCode} +} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/log.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/log.go index 18725514..18725514 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/log.go +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/log.go diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/params.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/params.go new file mode 100644 index 00000000..134f85e4 --- /dev/null +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/params.go @@ -0,0 +1,97 @@ +package tgbotapi + +import ( + "encoding/json" + "reflect" + "strconv" +) + +// Params represents a set of parameters that gets passed to a request. +type Params map[string]string + +// AddNonEmpty adds a value if it not an empty string. +func (p Params) AddNonEmpty(key, value string) { + if value != "" { + p[key] = value + } +} + +// AddNonZero adds a value if it is not zero. +func (p Params) AddNonZero(key string, value int) { + if value != 0 { + p[key] = strconv.Itoa(value) + } +} + +// AddNonZero64 is the same as AddNonZero except uses an int64. +func (p Params) AddNonZero64(key string, value int64) { + if value != 0 { + p[key] = strconv.FormatInt(value, 10) + } +} + +// AddBool adds a value of a bool if it is true. +func (p Params) AddBool(key string, value bool) { + if value { + p[key] = strconv.FormatBool(value) + } +} + +// AddNonZeroFloat adds a floating point value that is not zero. +func (p Params) AddNonZeroFloat(key string, value float64) { + if value != 0 { + p[key] = strconv.FormatFloat(value, 'f', 6, 64) + } +} + +// AddInterface adds an interface if it is not nil and can be JSON marshalled. +func (p Params) AddInterface(key string, value interface{}) error { + if value == nil || (reflect.ValueOf(value).Kind() == reflect.Ptr && reflect.ValueOf(value).IsNil()) { + return nil + } + + b, err := json.Marshal(value) + if err != nil { + return err + } + + p[key] = string(b) + + return nil +} + +// AddFirstValid attempts to add the first item that is not a default value. +// +// For example, AddFirstValid(0, "", "test") would add "test". +func (p Params) AddFirstValid(key string, args ...interface{}) error { + for _, arg := range args { + switch v := arg.(type) { + case int: + if v != 0 { + p[key] = strconv.Itoa(v) + return nil + } + case int64: + if v != 0 { + p[key] = strconv.FormatInt(v, 10) + return nil + } + case string: + if v != "" { + p[key] = v + return nil + } + case nil: + default: + b, err := json.Marshal(arg) + if err != nil { + return err + } + + p[key] = string(b) + return nil + } + } + + return nil +} diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/passport.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/passport.go index 5f55006d..4fedb965 100644 --- a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/passport.go +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/passport.go @@ -54,13 +54,15 @@ type ( Credentials *EncryptedCredentials `json:"credentials"` } - // PassportFile represents a file uploaded to Telegram Passport. Currently all + // PassportFile represents a file uploaded to Telegram Passport. Currently, all // Telegram Passport files are in JPEG format when decrypted and don't exceed // 10MB. PassportFile struct { // Unique identifier for this file FileID string `json:"file_id"` + FileUniqueID string `json:"file_unique_id"` + // File size FileSize int `json:"file_size"` @@ -212,7 +214,7 @@ type ( // PassportElementErrorFile represents an issue with a document scan. The // error is considered resolved when the file with the document scan changes. PassportElementErrorFile struct { - // Error source, must be file + // Error source, must be a file Source string `json:"source"` // The section of the user's Telegram Passport which has the issue, one diff --git a/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/types.go b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/types.go new file mode 100644 index 00000000..18b4decb --- /dev/null +++ b/vendor/github.com/go-telegram-bot-api/telegram-bot-api/v5/types.go @@ -0,0 +1,3225 @@ +package tgbotapi + +import ( + "encoding/json" + "errors" + "fmt" + "net/url" + "strings" + "time" +) + +// 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,omitempty"` + ErrorCode int `json:"error_code,omitempty"` + Description string `json:"description,omitempty"` + Parameters *ResponseParameters `json:"parameters,omitempty"` +} + +// Error is an error containing extra information returned by the Telegram API. +type Error struct { + Code int + Message string + ResponseParameters +} + +// Error message string. +func (e Error) Error() string { + return e.Message +} + +// Update is an update response, from GetUpdates. +type Update struct { + // UpdateID is the update's unique identifier. + // Update identifiers start from a certain positive number and increase + // sequentially. + // This ID becomes especially handy if you're using Webhooks, + // since it allows you to ignore repeated updates or to restore + // the correct update sequence, should they get out of order. + // If there are no new updates for at least a week, then identifier + // of the next update will be chosen randomly instead of sequentially. + UpdateID int `json:"update_id"` + // Message new incoming message of any kind — text, photo, sticker, etc. + // + // optional + Message *Message `json:"message,omitempty"` + // EditedMessage new version of a message that is known to the bot and was + // edited + // + // optional + EditedMessage *Message `json:"edited_message,omitempty"` + // ChannelPost new version of a message that is known to the bot and was + // edited + // + // optional + ChannelPost *Message `json:"channel_post,omitempty"` + // EditedChannelPost new incoming channel post of any kind — text, photo, + // sticker, etc. + // + // optional + EditedChannelPost *Message `json:"edited_channel_post,omitempty"` + // InlineQuery new incoming inline query + // + // optional + InlineQuery *InlineQuery `json:"inline_query,omitempty"` + // ChosenInlineResult is the result of an inline query + // that was chosen by a user and sent to their chat partner. + // Please see our documentation on the feedback collecting + // for details on how to enable these updates for your bot. + // + // optional + ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result,omitempty"` + // CallbackQuery new incoming callback query + // + // optional + CallbackQuery *CallbackQuery `json:"callback_query,omitempty"` + // ShippingQuery new incoming shipping query. Only for invoices with + // flexible price + // + // optional + ShippingQuery *ShippingQuery `json:"shipping_query,omitempty"` + // PreCheckoutQuery new incoming pre-checkout query. Contains full + // information about checkout + // + // optional + PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query,omitempty"` + // Pool new poll state. Bots receive only updates about stopped polls and + // polls, which are sent by the bot + // + // optional + Poll *Poll `json:"poll,omitempty"` + // PollAnswer user changed their answer in a non-anonymous poll. Bots + // receive new votes only in polls that were sent by the bot itself. + // + // optional + PollAnswer *PollAnswer `json:"poll_answer,omitempty"` + // MyChatMember is the bot's chat member status was updated in a chat. For + // private chats, this update is received only when the bot is blocked or + // unblocked by the user. + // + // optional + MyChatMember *ChatMemberUpdated `json:"my_chat_member"` + // ChatMember is a chat member's status was updated in a chat. The bot must + // be an administrator in the chat and must explicitly specify "chat_member" + // in the list of allowed_updates to receive these updates. + // + // optional + ChatMember *ChatMemberUpdated `json:"chat_member"` + // ChatJoinRequest is a request to join the chat has been sent. The bot must + // have the can_invite_users administrator right in the chat to receive + // these updates. + // + // optional + ChatJoinRequest *ChatJoinRequest `json:"chat_join_request"` +} + +// SentFrom returns the user who sent an update. Can be nil, if Telegram did not provide information +// about the user in the update object. +func (u *Update) SentFrom() *User { + switch { + case u.Message != nil: + return u.Message.From + case u.EditedMessage != nil: + return u.EditedMessage.From + case u.InlineQuery != nil: + return u.InlineQuery.From + case u.ChosenInlineResult != nil: + return u.ChosenInlineResult.From + case u.CallbackQuery != nil: + return u.CallbackQuery.From + case u.ShippingQuery != nil: + return u.ShippingQuery.From + case u.PreCheckoutQuery != nil: + return u.PreCheckoutQuery.From + default: + return nil + } +} + +// CallbackData returns the callback query data, if it exists. +func (u *Update) CallbackData() string { + if u.CallbackQuery != nil { + return u.CallbackQuery.Data + } + return "" +} + +// FromChat returns the chat where an update occurred. +func (u *Update) FromChat() *Chat { + switch { + case u.Message != nil: + return u.Message.Chat + case u.EditedMessage != nil: + return u.EditedMessage.Chat + case u.ChannelPost != nil: + return u.ChannelPost.Chat + case u.EditedChannelPost != nil: + return u.EditedChannelPost.Chat + case u.CallbackQuery != nil: + return u.CallbackQuery.Message.Chat + default: + return nil + } +} + +// UpdatesChannel is the channel for getting updates. +type UpdatesChannel <-chan Update + +// Clear discards all unprocessed incoming updates. +func (ch UpdatesChannel) Clear() { + for len(ch) != 0 { + <-ch + } +} + +// User represents a Telegram user or bot. +type User struct { + // ID is a unique identifier for this user or bot + ID int64 `json:"id"` + // IsBot true, if this user is a bot + // + // optional + IsBot bool `json:"is_bot,omitempty"` + // FirstName user's or bot's first name + FirstName string `json:"first_name"` + // LastName user's or bot's last name + // + // optional + LastName string `json:"last_name,omitempty"` + // UserName user's or bot's username + // + // optional + UserName string `json:"username,omitempty"` + // LanguageCode IETF language tag of the user's language + // more info: https://en.wikipedia.org/wiki/IETF_language_tag + // + // optional + LanguageCode string `json:"language_code,omitempty"` + // CanJoinGroups is true, if the bot can be invited to groups. + // Returned only in getMe. + // + // optional + CanJoinGroups bool `json:"can_join_groups,omitempty"` + // CanReadAllGroupMessages is true, if privacy mode is disabled for the bot. + // Returned only in getMe. + // + // optional + CanReadAllGroupMessages bool `json:"can_read_all_group_messages,omitempty"` + // SupportsInlineQueries is true, if the bot supports inline queries. + // Returned only in getMe. + // + // optional + SupportsInlineQueries bool `json:"supports_inline_queries,omitempty"` +} + +// 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. +func (u *User) String() string { + if u == nil { + return "" + } + if u.UserName != "" { + return u.UserName + } + + name := u.FirstName + if u.LastName != "" { + name += " " + u.LastName + } + + return name +} + +// Chat represents a chat. +type Chat struct { + // ID is a unique identifier for this chat + ID int64 `json:"id"` + // Type of chat, can be either “private”, “group”, “supergroup” or “channel” + Type string `json:"type"` + // Title for supergroups, channels and group chats + // + // optional + Title string `json:"title,omitempty"` + // UserName for private chats, supergroups and channels if available + // + // optional + UserName string `json:"username,omitempty"` + // FirstName of the other party in a private chat + // + // optional + FirstName string `json:"first_name,omitempty"` + // LastName of the other party in a private chat + // + // optional + LastName string `json:"last_name,omitempty"` + // Photo is a chat photo + Photo *ChatPhoto `json:"photo"` + // Bio is the bio of the other party in a private chat. Returned only in + // getChat + // + // optional + Bio string `json:"bio,omitempty"` + // HasPrivateForwards is true if privacy settings of the other party in the + // private chat allows to use tg://user?id=<user_id> links only in chats + // with the user. Returned only in getChat. + // + // optional + HasPrivateForwards bool `json:"has_private_forwards,omitempty"` + // Description for groups, supergroups and channel chats + // + // optional + Description string `json:"description,omitempty"` + // InviteLink is a chat invite link, for groups, supergroups and channel chats. + // Each administrator in a chat generates their own invite links, + // so the bot must first generate the link using exportChatInviteLink + // + // optional + InviteLink string `json:"invite_link,omitempty"` + // PinnedMessage is the pinned message, for groups, supergroups and channels + // + // optional + PinnedMessage *Message `json:"pinned_message,omitempty"` + // Permissions are default chat member permissions, for groups and + // supergroups. Returned only in getChat. + // + // optional + Permissions *ChatPermissions `json:"permissions,omitempty"` + // SlowModeDelay is for supergroups, the minimum allowed delay between + // consecutive messages sent by each unpriviledged user. Returned only in + // getChat. + // + // optional + SlowModeDelay int `json:"slow_mode_delay,omitempty"` + // MessageAutoDeleteTime is the time after which all messages sent to the + // chat will be automatically deleted; in seconds. Returned only in getChat. + // + // optional + MessageAutoDeleteTime int `json:"message_auto_delete_time,omitempty"` + // HasProtectedContent is true if messages from the chat can't be forwarded + // to other chats. Returned only in getChat. + // + // optional + HasProtectedContent bool `json:"has_protected_content,omitempty"` + // StickerSetName is for supergroups, name of group sticker set.Returned + // only in getChat. + // + // optional + StickerSetName string `json:"sticker_set_name,omitempty"` + // CanSetStickerSet is true, if the bot can change the group sticker set. + // Returned only in getChat. + // + // optional + CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` + // LinkedChatID is a unique identifier for the linked chat, i.e. the + // discussion group identifier for a channel and vice versa; for supergroups + // and channel chats. + // + // optional + LinkedChatID int64 `json:"linked_chat_id,omitempty"` + // Location is for supergroups, the location to which the supergroup is + // connected. Returned only in getChat. + // + // optional + Location *ChatLocation `json:"location"` +} + +// IsPrivate returns if the Chat is a private conversation. +func (c Chat) IsPrivate() bool { + return c.Type == "private" +} + +// IsGroup returns if the Chat is a group. +func (c Chat) IsGroup() bool { + return c.Type == "group" +} + +// IsSuperGroup returns if the Chat is a supergroup. +func (c Chat) IsSuperGroup() bool { + return c.Type == "supergroup" +} + +// IsChannel returns if the Chat is a channel. +func (c Chat) IsChannel() bool { + return c.Type == "channel" +} + +// ChatConfig returns a ChatConfig struct for chat related methods. +func (c Chat) ChatConfig() ChatConfig { + return ChatConfig{ChatID: c.ID} +} + +// Message represents a message. +type Message struct { + // MessageID is a unique message identifier inside this chat + MessageID int `json:"message_id"` + // From is a sender, empty for messages sent to channels; + // + // optional + From *User `json:"from,omitempty"` + // SenderChat is the sender of the message, sent on behalf of a chat. The + // channel itself for channel messages. The supergroup itself for messages + // from anonymous group administrators. The linked channel for messages + // automatically forwarded to the discussion group + // + // optional + SenderChat *Chat `json:"sender_chat,omitempty"` + // Date of the message was sent in Unix time + Date int `json:"date"` + // Chat is the conversation the message belongs to + Chat *Chat `json:"chat"` + // ForwardFrom for forwarded messages, sender of the original message; + // + // optional + ForwardFrom *User `json:"forward_from,omitempty"` + // ForwardFromChat for messages forwarded from channels, + // information about the original channel; + // + // optional + ForwardFromChat *Chat `json:"forward_from_chat,omitempty"` + // ForwardFromMessageID for messages forwarded from channels, + // identifier of the original message in the channel; + // + // optional + ForwardFromMessageID int `json:"forward_from_message_id,omitempty"` + // ForwardSignature for messages forwarded from channels, signature of the + // post author if present + // + // optional + ForwardSignature string `json:"forward_signature,omitempty"` + // ForwardSenderName is the sender's name for messages forwarded from users + // who disallow adding a link to their account in forwarded messages + // + // optional + ForwardSenderName string `json:"forward_sender_name,omitempty"` + // ForwardDate for forwarded messages, date the original message was sent in Unix time; + // + // optional + ForwardDate int `json:"forward_date,omitempty"` + // IsAutomaticForward is true if the message is a channel post that was + // automatically forwarded to the connected discussion group. + // + // optional + IsAutomaticForward bool `json:"is_automatic_forward,omitempty"` + // ReplyToMessage for replies, the original message. + // Note that the Message object in this field will not contain further ReplyToMessage fields + // even if it itself is a reply; + // + // optional + ReplyToMessage *Message `json:"reply_to_message,omitempty"` + // ViaBot through which the message was sent; + // + // optional + ViaBot *User `json:"via_bot,omitempty"` + // EditDate of the message was last edited in Unix time; + // + // optional + EditDate int `json:"edit_date,omitempty"` + // HasProtectedContent is true if the message can't be forwarded. + // + // optional + HasProtectedContent bool `json:"has_protected_content,omitempty"` + // MediaGroupID is the unique identifier of a media message group this message belongs to; + // + // optional + MediaGroupID string `json:"media_group_id,omitempty"` + // AuthorSignature is the signature of the post author for messages in channels; + // + // optional + AuthorSignature string `json:"author_signature,omitempty"` + // Text is for text messages, the actual UTF-8 text of the message, 0-4096 characters; + // + // optional + Text string `json:"text,omitempty"` + // Entities are for text messages, special entities like usernames, + // URLs, bot commands, etc. that appear in the text; + // + // optional + Entities []MessageEntity `json:"entities,omitempty"` + // Animation message is an animation, information about the animation. + // For backward compatibility, when this field is set, the document field will also be set; + // + // optional + Animation *Animation `json:"animation,omitempty"` + // Audio message is an audio file, information about the file; + // + // optional + Audio *Audio `json:"audio,omitempty"` + // Document message is a general file, information about the file; + // + // optional + Document *Document `json:"document,omitempty"` + // Photo message is a photo, available sizes of the photo; + // + // optional + Photo []PhotoSize `json:"photo,omitempty"` + // Sticker message is a sticker, information about the sticker; + // + // optional + Sticker *Sticker `json:"sticker,omitempty"` + // Video message is a video, information about the video; + // + // optional + Video *Video `json:"video,omitempty"` + // VideoNote message is a video note, information about the video message; + // + // optional + VideoNote *VideoNote `json:"video_note,omitempty"` + // Voice message is a voice message, information about the file; + // + // optional + Voice *Voice `json:"voice,omitempty"` + // Caption for the animation, audio, document, photo, video or voice, 0-1024 characters; + // + // optional + Caption string `json:"caption,omitempty"` + // CaptionEntities; + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // Contact message is a shared contact, information about the contact; + // + // optional + Contact *Contact `json:"contact,omitempty"` + // Dice is a dice with random value; + // + // optional + Dice *Dice `json:"dice,omitempty"` + // Game message is a game, information about the game; + // + // optional + Game *Game `json:"game,omitempty"` + // Poll is a native poll, information about the poll; + // + // optional + Poll *Poll `json:"poll,omitempty"` + // Venue message is a venue, information about the venue. + // For backward compatibility, when this field is set, the location field + // will also be set; + // + // optional + Venue *Venue `json:"venue,omitempty"` + // Location message is a shared location, information about the location; + // + // optional + Location *Location `json:"location,omitempty"` + // NewChatMembers that were added to the group or supergroup + // and information about them (the bot itself may be one of these members); + // + // optional + NewChatMembers []User `json:"new_chat_members,omitempty"` + // LeftChatMember is a member was removed from the group, + // information about them (this member may be the bot itself); + // + // optional + LeftChatMember *User `json:"left_chat_member,omitempty"` + // NewChatTitle is a chat title was changed to this value; + // + // optional + NewChatTitle string `json:"new_chat_title,omitempty"` + // NewChatPhoto is a chat photo was change to this value; + // + // optional + NewChatPhoto []PhotoSize `json:"new_chat_photo,omitempty"` + // DeleteChatPhoto is a service message: the chat photo was deleted; + // + // optional + DeleteChatPhoto bool `json:"delete_chat_photo,omitempty"` + // GroupChatCreated is a service message: the group has been created; + // + // optional + GroupChatCreated bool `json:"group_chat_created,omitempty"` + // SuperGroupChatCreated is a service message: the supergroup has been created. + // This field can't be received in a message coming through updates, + // because bot can't be a member of a supergroup when it is created. + // It can only be found in ReplyToMessage if someone replies to a very first message + // in a directly created supergroup; + // + // optional + SuperGroupChatCreated bool `json:"supergroup_chat_created,omitempty"` + // ChannelChatCreated is a service message: the channel has been created. + // This field can't be received in a message coming through updates, + // because bot can't be a member of a channel when it is created. + // It can only be found in ReplyToMessage + // if someone replies to a very first message in a channel; + // + // optional + ChannelChatCreated bool `json:"channel_chat_created,omitempty"` + // MessageAutoDeleteTimerChanged is a service message: auto-delete timer + // settings changed in the chat. + // + // optional + MessageAutoDeleteTimerChanged *MessageAutoDeleteTimerChanged `json:"message_auto_delete_timer_changed"` + // MigrateToChatID is the group has been migrated to a supergroup with the specified identifier. + // This number may be greater than 32 bits and some programming languages + // may have difficulty/silent defects in interpreting it. + // But it is smaller than 52 bits, so a signed 64-bit integer + // or double-precision float type are safe for storing this identifier; + // + // optional + MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` + // MigrateFromChatID is the supergroup has been migrated from a group with the specified identifier. + // This number may be greater than 32 bits and some programming languages + // may have difficulty/silent defects in interpreting it. + // But it is smaller than 52 bits, so a signed 64-bit integer + // or double-precision float type are safe for storing this identifier; + // + // optional + MigrateFromChatID int64 `json:"migrate_from_chat_id,omitempty"` + // PinnedMessage is a specified message was pinned. + // Note that the Message object in this field will not contain further ReplyToMessage + // fields even if it is itself a reply; + // + // optional + PinnedMessage *Message `json:"pinned_message,omitempty"` + // Invoice message is an invoice for a payment; + // + // optional + Invoice *Invoice `json:"invoice,omitempty"` + // SuccessfulPayment message is a service message about a successful payment, + // information about the payment; + // + // optional + SuccessfulPayment *SuccessfulPayment `json:"successful_payment,omitempty"` + // ConnectedWebsite is the domain name of the website on which the user has + // logged in; + // + // optional + ConnectedWebsite string `json:"connected_website,omitempty"` + // PassportData is a Telegram Passport data; + // + // optional + PassportData *PassportData `json:"passport_data,omitempty"` + // ProximityAlertTriggered is a service message. A user in the chat + // triggered another user's proximity alert while sharing Live Location + // + // optional + ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered"` + // VoiceChatScheduled is a service message: voice chat scheduled. + // + // optional + VoiceChatScheduled *VoiceChatScheduled `json:"voice_chat_scheduled"` + // VoiceChatStarted is a service message: voice chat started. + // + // optional + VoiceChatStarted *VoiceChatStarted `json:"voice_chat_started"` + // VoiceChatEnded is a service message: voice chat ended. + // + // optional + VoiceChatEnded *VoiceChatEnded `json:"voice_chat_ended"` + // VoiceChatParticipantsInvited is a service message: new participants + // invited to a voice chat. + // + // optional + VoiceChatParticipantsInvited *VoiceChatParticipantsInvited `json:"voice_chat_participants_invited"` + // ReplyMarkup is the Inline keyboard attached to the message. + // login_url buttons are represented as ordinary url buttons. + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` +} + +// Time converts the message timestamp into a Time. +func (m *Message) Time() time.Time { + return time.Unix(int64(m.Date), 0) +} + +// IsCommand returns true if message starts with a "bot_command" entity. +func (m *Message) IsCommand() bool { + if m.Entities == nil || len(m.Entities) == 0 { + return false + } + + entity := m.Entities[0] + return entity.Offset == 0 && entity.IsCommand() +} + +// 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. +// +// If the command contains the at name syntax, it is removed. Use +// CommandWithAt() if you do not want that. +func (m *Message) Command() string { + command := m.CommandWithAt() + + if i := strings.Index(command, "@"); i != -1 { + command = command[:i] + } + + return command +} + +// CommandWithAt 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. +// +// If the command contains the at name syntax, it is not removed. Use Command() +// if you want that. +func (m *Message) CommandWithAt() string { + if !m.IsCommand() { + return "" + } + + // IsCommand() checks that the message begins with a bot_command entity + entity := m.Entities[0] + return m.Text[1:entity.Length] +} + +// 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. +// +// Note: The first character after the command name is omitted: +// - "/foo bar baz" yields "bar baz", not " bar baz" +// - "/foo-bar baz" yields "bar baz", too +// Even though the latter is not a command conforming to the spec, the API +// marks "/foo" as command entity. +func (m *Message) CommandArguments() string { + if !m.IsCommand() { + return "" + } + + // IsCommand() checks that the message begins with a bot_command entity + entity := m.Entities[0] + + if len(m.Text) == entity.Length { + return "" // The command makes up the whole message + } + + return m.Text[entity.Length+1:] +} + +// MessageID represents a unique message identifier. +type MessageID struct { + MessageID int `json:"message_id"` +} + +// MessageEntity represents one special entity in a text message. +type MessageEntity struct { + // Type of the entity. + // Can be: + // “mention” (@username), + // “hashtag” (#hashtag), + // “cashtag” ($USD), + // “bot_command” (/start@jobs_bot), + // “url” (https://telegram.org), + // “email” (do-not-reply@telegram.org), + // “phone_number” (+1-212-555-0123), + // “bold” (bold text), + // “italic” (italic text), + // “underline” (underlined text), + // “strikethrough” (strikethrough text), + // “code” (monowidth string), + // “pre” (monowidth block), + // “text_link” (for clickable text URLs), + // “text_mention” (for users without usernames) + Type string `json:"type"` + // Offset in UTF-16 code units to the start of the entity + Offset int `json:"offset"` + // Length + Length int `json:"length"` + // URL for “text_link” only, url that will be opened after user taps on the text + // + // optional + URL string `json:"url,omitempty"` + // User for “text_mention” only, the mentioned user + // + // optional + User *User `json:"user,omitempty"` + // Language for “pre” only, the programming language of the entity text + // + // optional + Language string `json:"language,omitempty"` +} + +// ParseURL attempts to parse a URL contained within a MessageEntity. +func (e MessageEntity) ParseURL() (*url.URL, error) { + if e.URL == "" { + return nil, errors.New(ErrBadURL) + } + + return url.Parse(e.URL) +} + +// IsMention returns true if the type of the message entity is "mention" (@username). +func (e MessageEntity) IsMention() bool { + return e.Type == "mention" +} + +// IsHashtag returns true if the type of the message entity is "hashtag". +func (e MessageEntity) IsHashtag() bool { + return e.Type == "hashtag" +} + +// IsCommand returns true if the type of the message entity is "bot_command". +func (e MessageEntity) IsCommand() bool { + return e.Type == "bot_command" +} + +// IsURL returns true if the type of the message entity is "url". +func (e MessageEntity) IsURL() bool { + return e.Type == "url" +} + +// IsEmail returns true if the type of the message entity is "email". +func (e MessageEntity) IsEmail() bool { + return e.Type == "email" +} + +// IsBold returns true if the type of the message entity is "bold" (bold text). +func (e MessageEntity) IsBold() bool { + return e.Type == "bold" +} + +// IsItalic returns true if the type of the message entity is "italic" (italic text). +func (e MessageEntity) IsItalic() bool { + return e.Type == "italic" +} + +// IsCode returns true if the type of the message entity is "code" (monowidth string). +func (e MessageEntity) IsCode() bool { + return e.Type == "code" +} + +// IsPre returns true if the type of the message entity is "pre" (monowidth block). +func (e MessageEntity) IsPre() bool { + return e.Type == "pre" +} + +// IsTextLink returns true if the type of the message entity is "text_link" (clickable text URL). +func (e MessageEntity) IsTextLink() bool { + return e.Type == "text_link" +} + +// PhotoSize represents one size of a photo or a file / sticker thumbnail. +type PhotoSize struct { + // FileID identifier for this file, which can be used to download or reuse + // the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Width photo width + Width int `json:"width"` + // Height photo height + Height int `json:"height"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// Animation represents an animation file. +type Animation struct { + // FileID is the identifier for this file, which can be used to download or reuse + // the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Width video width as defined by sender + Width int `json:"width"` + // Height video height as defined by sender + Height int `json:"height"` + // Duration of the video in seconds as defined by sender + Duration int `json:"duration"` + // Thumbnail animation thumbnail as defined by sender + // + // optional + Thumbnail *PhotoSize `json:"thumb,omitempty"` + // FileName original animation filename as defined by sender + // + // optional + FileName string `json:"file_name,omitempty"` + // MimeType of the file as defined by sender + // + // optional + MimeType string `json:"mime_type,omitempty"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// Audio represents an audio file to be treated as music by the Telegram clients. +type Audio struct { + // FileID is an identifier for this file, which can be used to download or + // reuse the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Duration of the audio in seconds as defined by sender + Duration int `json:"duration"` + // Performer of the audio as defined by sender or by audio tags + // + // optional + Performer string `json:"performer,omitempty"` + // Title of the audio as defined by sender or by audio tags + // + // optional + Title string `json:"title,omitempty"` + // FileName is the original filename as defined by sender + // + // optional + FileName string `json:"file_name,omitempty"` + // MimeType of the file as defined by sender + // + // optional + MimeType string `json:"mime_type,omitempty"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` + // Thumbnail is the album cover to which the music file belongs + // + // optional + Thumbnail *PhotoSize `json:"thumb,omitempty"` +} + +// Document represents a general file. +type Document struct { + // FileID is an identifier for this file, which can be used to download or + // reuse the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Thumbnail document thumbnail as defined by sender + // + // optional + Thumbnail *PhotoSize `json:"thumb,omitempty"` + // FileName original filename as defined by sender + // + // optional + FileName string `json:"file_name,omitempty"` + // MimeType of the file as defined by sender + // + // optional + MimeType string `json:"mime_type,omitempty"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// Video represents a video file. +type Video struct { + // FileID identifier for this file, which can be used to download or reuse + // the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Width video width as defined by sender + Width int `json:"width"` + // Height video height as defined by sender + Height int `json:"height"` + // Duration of the video in seconds as defined by sender + Duration int `json:"duration"` + // Thumbnail video thumbnail + // + // optional + Thumbnail *PhotoSize `json:"thumb,omitempty"` + // FileName is the original filename as defined by sender + // + // optional + FileName string `json:"file_name,omitempty"` + // MimeType of a file as defined by sender + // + // optional + MimeType string `json:"mime_type,omitempty"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// VideoNote object represents a video message. +type VideoNote struct { + // FileID identifier for this file, which can be used to download or reuse the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Length video width and height (diameter of the video message) as defined by sender + Length int `json:"length"` + // Duration of the video in seconds as defined by sender + Duration int `json:"duration"` + // Thumbnail video thumbnail + // + // optional + Thumbnail *PhotoSize `json:"thumb,omitempty"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// Voice represents a voice note. +type Voice struct { + // FileID identifier for this file, which can be used to download or reuse the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Duration of the audio in seconds as defined by sender + Duration int `json:"duration"` + // MimeType of the file as defined by sender + // + // optional + MimeType string `json:"mime_type,omitempty"` + // FileSize file size + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// Contact represents a phone contact. +// +// Note that LastName and UserID may be empty. +type Contact struct { + // PhoneNumber contact's phone number + PhoneNumber string `json:"phone_number"` + // FirstName contact's first name + FirstName string `json:"first_name"` + // LastName contact's last name + // + // optional + LastName string `json:"last_name,omitempty"` + // UserID contact's user identifier in Telegram + // + // optional + UserID int64 `json:"user_id,omitempty"` + // VCard is additional data about the contact in the form of a vCard. + // + // optional + VCard string `json:"vcard,omitempty"` +} + +// Dice represents an animated emoji that displays a random value. +type Dice struct { + // Emoji on which the dice throw animation is based + Emoji string `json:"emoji"` + // Value of the dice + Value int `json:"value"` +} + +// PollOption contains information about one answer option in a poll. +type PollOption struct { + // Text is the option text, 1-100 characters + Text string `json:"text"` + // VoterCount is the number of users that voted for this option + VoterCount int `json:"voter_count"` +} + +// PollAnswer represents an answer of a user in a non-anonymous poll. +type PollAnswer struct { + // PollID is the unique poll identifier + PollID string `json:"poll_id"` + // User who changed the answer to the poll + User User `json:"user"` + // OptionIDs is the 0-based identifiers of poll options chosen by the user. + // May be empty if user retracted vote. + OptionIDs []int `json:"option_ids"` +} + +// Poll contains information about a poll. +type Poll struct { + // ID is the unique poll identifier + ID string `json:"id"` + // Question is the poll question, 1-255 characters + Question string `json:"question"` + // Options is the list of poll options + Options []PollOption `json:"options"` + // TotalVoterCount is the total numbers of users who voted in the poll + TotalVoterCount int `json:"total_voter_count"` + // IsClosed is if the poll is closed + IsClosed bool `json:"is_closed"` + // IsAnonymous is if the poll is anonymous + IsAnonymous bool `json:"is_anonymous"` + // Type is the poll type, currently can be "regular" or "quiz" + Type string `json:"type"` + // AllowsMultipleAnswers is true, if the poll allows multiple answers + AllowsMultipleAnswers bool `json:"allows_multiple_answers"` + // CorrectOptionID is the 0-based identifier of the correct answer option. + // Available only for polls in quiz mode, which are closed, or was sent (not + // forwarded) by the bot or to the private chat with the bot. + // + // optional + CorrectOptionID int `json:"correct_option_id,omitempty"` + // Explanation is text that is shown when a user chooses an incorrect answer + // or taps on the lamp icon in a quiz-style poll, 0-200 characters + // + // optional + Explanation string `json:"explanation,omitempty"` + // ExplanationEntities are special entities like usernames, URLs, bot + // commands, etc. that appear in the explanation + // + // optional + ExplanationEntities []MessageEntity `json:"explanation_entities,omitempty"` + // OpenPeriod is the amount of time in seconds the poll will be active + // after creation + // + // optional + OpenPeriod int `json:"open_period,omitempty"` + // CloseDate is the point in time (unix timestamp) when the poll will be + // automatically closed + // + // optional + CloseDate int `json:"close_date,omitempty"` +} + +// Location represents a point on the map. +type Location struct { + // Longitude as defined by sender + Longitude float64 `json:"longitude"` + // Latitude as defined by sender + Latitude float64 `json:"latitude"` + // HorizontalAccuracy is the radius of uncertainty for the location, + // measured in meters; 0-1500 + // + // optional + HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` + // LivePeriod is time relative to the message sending date, during which the + // location can be updated, in seconds. For active live locations only. + // + // optional + LivePeriod int `json:"live_period,omitempty"` + // Heading is the direction in which user is moving, in degrees; 1-360. For + // active live locations only. + // + // optional + Heading int `json:"heading,omitempty"` + // ProximityAlertRadius is the maximum distance for proximity alerts about + // approaching another chat member, in meters. For sent live locations only. + // + // optional + ProximityAlertRadius int `json:"proximity_alert_radius,omitempty"` +} + +// Venue represents a venue. +type Venue struct { + // Location is the venue location + Location Location `json:"location"` + // Title is the name of the venue + Title string `json:"title"` + // Address of the venue + Address string `json:"address"` + // FoursquareID is the foursquare identifier of the venue + // + // optional + FoursquareID string `json:"foursquare_id,omitempty"` + // FoursquareType is the foursquare type of the venue + // + // optional + FoursquareType string `json:"foursquare_type,omitempty"` + // GooglePlaceID is the Google Places identifier of the venue + // + // optional + GooglePlaceID string `json:"google_place_id,omitempty"` + // GooglePlaceType is the Google Places type of the venue + // + // optional + GooglePlaceType string `json:"google_place_type,omitempty"` +} + +// ProximityAlertTriggered represents a service message sent when a user in the +// chat triggers a proximity alert sent by another user. +type ProximityAlertTriggered struct { + // Traveler is the user that triggered the alert + Traveler User `json:"traveler"` + // Watcher is the user that set the alert + Watcher User `json:"watcher"` + // Distance is the distance between the users + Distance int `json:"distance"` +} + +// MessageAutoDeleteTimerChanged represents a service message about a change in +// auto-delete timer settings. +type MessageAutoDeleteTimerChanged struct { + // New auto-delete time for messages in the chat. + MessageAutoDeleteTime int `json:"message_auto_delete_time"` +} + +// VoiceChatScheduled represents a service message about a voice chat scheduled +// in the chat. +type VoiceChatScheduled struct { + // Point in time (Unix timestamp) when the voice chat is supposed to be + // started by a chat administrator + StartDate int `json:"start_date"` +} + +// Time converts the scheduled start date into a Time. +func (m *VoiceChatScheduled) Time() time.Time { + return time.Unix(int64(m.StartDate), 0) +} + +// VoiceChatStarted represents a service message about a voice chat started in +// the chat. +type VoiceChatStarted struct{} + +// VoiceChatEnded represents a service message about a voice chat ended in the +// chat. +type VoiceChatEnded struct { + // Voice chat duration; in seconds. + Duration int `json:"duration"` +} + +// VoiceChatParticipantsInvited represents a service message about new members +// invited to a voice chat. +type VoiceChatParticipantsInvited struct { + // New members that were invited to the voice chat. + // + // optional + Users []User `json:"users"` +} + +// UserProfilePhotos contains a set of user profile photos. +type UserProfilePhotos struct { + // TotalCount total number of profile pictures the target user has + TotalCount int `json:"total_count"` + // Photos requested profile pictures (in up to 4 sizes each) + Photos [][]PhotoSize `json:"photos"` +} + +// File contains information about a file to download from Telegram. +type File struct { + // FileID identifier for this file, which can be used to download or reuse + // the file + FileID string `json:"file_id"` + // FileUniqueID is the unique identifier for this file, which is supposed to + // be the same over time and for different bots. Can't be used to download + // or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // FileSize file size, if known + // + // optional + FileSize int `json:"file_size,omitempty"` + // FilePath file path + // + // optional + FilePath string `json:"file_path,omitempty"` +} + +// Link returns a full path to the download URL for a File. +// +// It requires the Bot token to create the link. +func (f *File) Link(token string) string { + return fmt.Sprintf(FileEndpoint, token, f.FilePath) +} + +// ReplyKeyboardMarkup represents a custom keyboard with reply options. +type ReplyKeyboardMarkup struct { + // Keyboard is an array of button rows, each represented by an Array of KeyboardButton objects + Keyboard [][]KeyboardButton `json:"keyboard"` + // ResizeKeyboard requests clients to resize the keyboard vertically for optimal fit + // (e.g., make the keyboard smaller if there are just two rows of buttons). + // Defaults to false, in which case the custom keyboard + // is always of the same height as the app's standard keyboard. + // + // optional + ResizeKeyboard bool `json:"resize_keyboard,omitempty"` + // OneTimeKeyboard requests clients to hide the keyboard as soon as it's been used. + // The keyboard will still be available, but clients will automatically display + // the usual letter-keyboard in the chat – the user can press a special button + // in the input field to see the custom keyboard again. + // Defaults to false. + // + // optional + OneTimeKeyboard bool `json:"one_time_keyboard,omitempty"` + // InputFieldPlaceholder is the placeholder to be shown in the input field when + // the keyboard is active; 1-64 characters. + // + // optional + InputFieldPlaceholder string `json:"input_field_placeholder,omitempty"` + // Selective use this parameter if you want to show the keyboard to specific users only. + // Targets: + // 1) users that are @mentioned in the text of the Message object; + // 2) if the bot's message is a reply (has Message.ReplyToMessage not nil), sender of the original message. + // + // Example: A user requests to change the bot's language, + // bot replies to the request with a keyboard to select the new language. + // Other users in the group don't see the keyboard. + // + // optional + Selective bool `json:"selective,omitempty"` +} + +// KeyboardButton represents one button of the reply keyboard. For simple text +// buttons String can be used instead of this object to specify text of the +// button. Optional fields request_contact, request_location, and request_poll +// are mutually exclusive. +type KeyboardButton struct { + // Text of the button. If none of the optional fields are used, + // it will be sent as a message when the button is pressed. + Text string `json:"text"` + // RequestContact if True, the user's phone number will be sent + // as a contact when the button is pressed. + // Available in private chats only. + // + // optional + RequestContact bool `json:"request_contact,omitempty"` + // RequestLocation if True, the user's current location will be sent when + // the button is pressed. + // Available in private chats only. + // + // optional + RequestLocation bool `json:"request_location,omitempty"` + // RequestPoll if True, the user will be asked to create a poll and send it + // to the bot when the button is pressed. Available in private chats only + // + // optional + RequestPoll *KeyboardButtonPollType `json:"request_poll,omitempty"` +} + +// KeyboardButtonPollType represents type of poll, which is allowed to +// be created and sent when the corresponding button is pressed. +type KeyboardButtonPollType struct { + // Type is if quiz is passed, the user will be allowed to create only polls + // in the quiz mode. If regular is passed, only regular polls will be + // allowed. Otherwise, the user will be allowed to create a poll of any type. + Type string `json:"type"` +} + +// ReplyKeyboardRemove Upon receiving a message with this object, Telegram +// clients will remove the current custom keyboard and display the default +// letter-keyboard. By default, custom keyboards are displayed until a new +// keyboard is sent by a bot. An exception is made for one-time keyboards +// that are hidden immediately after the user presses a button. +type ReplyKeyboardRemove struct { + // RemoveKeyboard requests clients to remove the custom keyboard + // (user will not be able to summon this keyboard; + // if you want to hide the keyboard from sight but keep it accessible, + // use one_time_keyboard in ReplyKeyboardMarkup). + RemoveKeyboard bool `json:"remove_keyboard"` + // Selective use this parameter if you want to remove the keyboard for specific users only. + // Targets: + // 1) users that are @mentioned in the text of the Message object; + // 2) if the bot's message is a reply (has Message.ReplyToMessage not nil), sender of the original message. + // + // Example: A user votes in a poll, bot returns confirmation message + // in reply to the vote and removes the keyboard for that user, + // while still showing the keyboard with poll options to users who haven't voted yet. + // + // optional + Selective bool `json:"selective,omitempty"` +} + +// InlineKeyboardMarkup represents an inline keyboard that appears right next to +// the message it belongs to. +type InlineKeyboardMarkup struct { + // InlineKeyboard array of button rows, each represented by an Array of + // InlineKeyboardButton objects + InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"` +} + +// InlineKeyboardButton represents one button of an inline keyboard. You must +// use exactly one of the optional fields. +// +// Note that some values are references as even an empty string +// will change behavior. +// +// CallbackGame, if set, MUST be first button in first row. +type InlineKeyboardButton struct { + // Text label text on the button + Text string `json:"text"` + // URL HTTP or tg:// url to be opened when button is pressed. + // + // optional + URL *string `json:"url,omitempty"` + // LoginURL is an HTTP URL used to automatically authorize the user. Can be + // used as a replacement for the Telegram Login Widget + // + // optional + LoginURL *LoginURL `json:"login_url,omitempty"` + // CallbackData data to be sent in a callback query to the bot when button is pressed, 1-64 bytes. + // + // optional + CallbackData *string `json:"callback_data,omitempty"` + // SwitchInlineQuery if set, pressing the button will prompt the user to select one of their chats, + // open that chat and insert the bot's username and the specified inline query in the input field. + // Can be empty, in which case just the bot's username will be inserted. + // + // This offers an easy way for users to start using your bot + // in inline mode when they are currently in a private chat with it. + // Especially useful when combined with switch_pm… actions – in this case + // the user will be automatically returned to the chat they switched from, + // skipping the chat selection screen. + // + // optional + SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` + // SwitchInlineQueryCurrentChat if set, pressing the button will insert the bot's username + // and the specified inline query in the current chat's input field. + // Can be empty, in which case only the bot's username will be inserted. + // + // This offers a quick way for the user to open your bot in inline mode + // in the same chat – good for selecting something from multiple options. + // + // optional + SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` + // CallbackGame description of the game that will be launched when the user presses the button. + // + // optional + CallbackGame *CallbackGame `json:"callback_game,omitempty"` + // Pay specify True, to send a Pay button. + // + // NOTE: This type of button must always be the first button in the first row. + // + // optional + Pay bool `json:"pay,omitempty"` +} + +// LoginURL represents a parameter of the inline keyboard button used to +// automatically authorize a user. Serves as a great replacement for the +// Telegram Login Widget when the user is coming from Telegram. All the user +// needs to do is tap/click a button and confirm that they want to log in. +type LoginURL struct { + // URL is an HTTP URL to be opened with user authorization data added to the + // query string when the button is pressed. If the user refuses to provide + // authorization data, the original URL without information about the user + // will be opened. The data added is the same as described in Receiving + // authorization data. + // + // NOTE: You must always check the hash of the received data to verify the + // authentication and the integrity of the data as described in Checking + // authorization. + URL string `json:"url"` + // ForwardText is the new text of the button in forwarded messages + // + // optional + ForwardText string `json:"forward_text,omitempty"` + // BotUsername is the username of a bot, which will be used for user + // authorization. See Setting up a bot for more details. If not specified, + // the current bot's username will be assumed. The url's domain must be the + // same as the domain linked with the bot. See Linking your domain to the + // bot for more details. + // + // optional + BotUsername string `json:"bot_username,omitempty"` + // RequestWriteAccess if true requests permission for your bot to send + // messages to the user + // + // optional + RequestWriteAccess bool `json:"request_write_access,omitempty"` +} + +// CallbackQuery represents an incoming callback query from a callback button in +// an inline keyboard. If the button that originated the query was attached to a +// message sent by the bot, the field message will be present. If the button was +// attached to a message sent via the bot (in inline mode), the field +// inline_message_id will be present. Exactly one of the fields data or +// game_short_name will be present. +type CallbackQuery struct { + // ID unique identifier for this query + ID string `json:"id"` + // From sender + From *User `json:"from"` + // Message with the callback button that originated the query. + // Note that message content and message date will not be available if the + // message is too old. + // + // optional + Message *Message `json:"message,omitempty"` + // InlineMessageID identifier of the message sent via the bot in inline + // mode, that originated the query. + // + // optional + InlineMessageID string `json:"inline_message_id,omitempty"` + // ChatInstance global identifier, uniquely corresponding to the chat to + // which the message with the callback button was sent. Useful for high + // scores in games. + ChatInstance string `json:"chat_instance"` + // Data associated with the callback button. Be aware that + // a bad client can send arbitrary data in this field. + // + // optional + Data string `json:"data,omitempty"` + // GameShortName short name of a Game to be returned, serves as the unique identifier for the game. + // + // optional + GameShortName string `json:"game_short_name,omitempty"` +} + +// ForceReply when receiving a message with this object, Telegram clients will +// display a reply interface to the user (act as if the user has selected the +// bot's message and tapped 'Reply'). This can be extremely useful if you want +// to create user-friendly step-by-step interfaces without having to sacrifice +// privacy mode. +type ForceReply struct { + // ForceReply shows reply interface to the user, + // as if they manually selected the bot's message and tapped 'Reply'. + ForceReply bool `json:"force_reply"` + // InputFieldPlaceholder is the placeholder to be shown in the input field when + // the reply is active; 1-64 characters. + // + // optional + InputFieldPlaceholder string `json:"input_field_placeholder,omitempty"` + // Selective use this parameter if you want to force reply from specific users only. + // Targets: + // 1) users that are @mentioned in the text of the Message object; + // 2) if the bot's message is a reply (has Message.ReplyToMessage not nil), sender of the original message. + // + // optional + Selective bool `json:"selective,omitempty"` +} + +// ChatPhoto represents a chat photo. +type ChatPhoto struct { + // SmallFileID is a file identifier of small (160x160) chat photo. + // This file_id can be used only for photo download and + // only for as long as the photo is not changed. + SmallFileID string `json:"small_file_id"` + // SmallFileUniqueID is a unique file identifier of small (160x160) chat + // photo, which is supposed to be the same over time and for different bots. + // Can't be used to download or reuse the file. + SmallFileUniqueID string `json:"small_file_unique_id"` + // BigFileID is a file identifier of big (640x640) chat photo. + // This file_id can be used only for photo download and + // only for as long as the photo is not changed. + BigFileID string `json:"big_file_id"` + // BigFileUniqueID is a file identifier of big (640x640) chat photo, which + // is supposed to be the same over time and for different bots. Can't be + // used to download or reuse the file. + BigFileUniqueID string `json:"big_file_unique_id"` +} + +// ChatInviteLink represents an invite link for a chat. +type ChatInviteLink struct { + // InviteLink is the invite link. If the link was created by another chat + // administrator, then the second part of the link will be replaced with “…”. + InviteLink string `json:"invite_link"` + // Creator of the link. + Creator User `json:"creator"` + // CreatesJoinRequest is true if users joining the chat via the link need to + // be approved by chat administrators. + // + // optional + CreatesJoinRequest bool `json:"creates_join_request"` + // IsPrimary is true, if the link is primary. + IsPrimary bool `json:"is_primary"` + // IsRevoked is true, if the link is revoked. + IsRevoked bool `json:"is_revoked"` + // Name is the name of the invite link. + // + // optional + Name string `json:"name"` + // ExpireDate is the point in time (Unix timestamp) when the link will + // expire or has been expired. + // + // optional + ExpireDate int `json:"expire_date"` + // MemberLimit is the maximum number of users that can be members of the + // chat simultaneously after joining the chat via this invite link; 1-99999. + // + // optional + MemberLimit int `json:"member_limit"` + // PendingJoinRequestCount is the number of pending join requests created + // using this link. + // + // optional + PendingJoinRequestCount int `json:"pending_join_request_count"` +} + +// ChatMember contains information about one member of a chat. +type ChatMember struct { + // User information about the user + User *User `json:"user"` + // Status the member's status in the chat. + // Can be + // “creator”, + // “administrator”, + // “member”, + // “restricted”, + // “left” or + // “kicked” + Status string `json:"status"` + // CustomTitle owner and administrators only. Custom title for this user + // + // optional + CustomTitle string `json:"custom_title,omitempty"` + // IsAnonymous owner and administrators only. True, if the user's presence + // in the chat is hidden + // + // optional + IsAnonymous bool `json:"is_anonymous"` + // UntilDate restricted and kicked only. + // Date when restrictions will be lifted for this user; + // unix time. + // + // optional + UntilDate int64 `json:"until_date,omitempty"` + // CanBeEdited administrators only. + // True, if the bot is allowed to edit administrator privileges of that user. + // + // optional + CanBeEdited bool `json:"can_be_edited,omitempty"` + // CanManageChat administrators only. + // True, if the administrator can access the chat event log, chat + // statistics, message statistics in channels, see channel members, see + // anonymous administrators in supergroups and ignore slow mode. Implied by + // any other administrator privilege. + // + // optional + CanManageChat bool `json:"can_manage_chat"` + // CanPostMessages administrators only. + // True, if the administrator can post in the channel; + // channels only. + // + // optional + CanPostMessages bool `json:"can_post_messages,omitempty"` + // CanEditMessages administrators only. + // True, if the administrator can edit messages of other users and can pin messages; + // channels only. + // + // optional + CanEditMessages bool `json:"can_edit_messages,omitempty"` + // CanDeleteMessages administrators only. + // True, if the administrator can delete messages of other users. + // + // optional + CanDeleteMessages bool `json:"can_delete_messages,omitempty"` + // CanManageVoiceChats administrators only. + // True, if the administrator can manage voice chats. + // + // optional + CanManageVoiceChats bool `json:"can_manage_voice_chats"` + // CanRestrictMembers administrators only. + // True, if the administrator can restrict, ban or unban chat members. + // + // optional + CanRestrictMembers bool `json:"can_restrict_members,omitempty"` + // CanPromoteMembers administrators only. + // True, if the administrator can add new administrators + // with a subset of their own privileges or demote administrators that he has promoted, + // directly or indirectly (promoted by administrators that were appointed by the user). + // + // optional + CanPromoteMembers bool `json:"can_promote_members,omitempty"` + // CanChangeInfo administrators and restricted only. + // True, if the user is allowed to change the chat title, photo and other settings. + // + // optional + CanChangeInfo bool `json:"can_change_info,omitempty"` + // CanInviteUsers administrators and restricted only. + // True, if the user is allowed to invite new users to the chat. + // + // optional + CanInviteUsers bool `json:"can_invite_users,omitempty"` + // CanPinMessages administrators and restricted only. + // True, if the user is allowed to pin messages; groups and supergroups only + // + // optional + CanPinMessages bool `json:"can_pin_messages,omitempty"` + // IsMember is true, if the user is a member of the chat at the moment of + // the request + IsMember bool `json:"is_member"` + // CanSendMessages + // + // optional + CanSendMessages bool `json:"can_send_messages,omitempty"` + // CanSendMediaMessages restricted only. + // True, if the user is allowed to send text messages, contacts, locations and venues + // + // optional + CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` + // CanSendPolls restricted only. + // True, if the user is allowed to send polls + // + // optional + CanSendPolls bool `json:"can_send_polls,omitempty"` + // CanSendOtherMessages restricted only. + // True, if the user is allowed to send audios, documents, + // photos, videos, video notes and voice notes. + // + // optional + CanSendOtherMessages bool `json:"can_send_other_messages,omitempty"` + // CanAddWebPagePreviews restricted only. + // True, if the user is allowed to add web page previews to their messages. + // + // optional + CanAddWebPagePreviews bool `json:"can_add_web_page_previews,omitempty"` +} + +// IsCreator returns if the ChatMember was the creator of the chat. +func (chat ChatMember) IsCreator() bool { return chat.Status == "creator" } + +// IsAdministrator returns if the ChatMember is a chat administrator. +func (chat ChatMember) IsAdministrator() bool { return chat.Status == "administrator" } + +// HasLeft returns if the ChatMember left the chat. +func (chat ChatMember) HasLeft() bool { return chat.Status == "left" } + +// WasKicked returns if the ChatMember was kicked from the chat. +func (chat ChatMember) WasKicked() bool { return chat.Status == "kicked" } + +// ChatMemberUpdated represents changes in the status of a chat member. +type ChatMemberUpdated struct { + // Chat the user belongs to. + Chat Chat `json:"chat"` + // From is the performer of the action, which resulted in the change. + From User `json:"from"` + // Date the change was done in Unix time. + Date int `json:"date"` + // Previous information about the chat member. + OldChatMember ChatMember `json:"old_chat_member"` + // New information about the chat member. + NewChatMember ChatMember `json:"new_chat_member"` + // InviteLink is the link which was used by the user to join the chat; + // for joining by invite link events only. + // + // optional + InviteLink *ChatInviteLink `json:"invite_link"` +} + +// ChatJoinRequest represents a join request sent to a chat. +type ChatJoinRequest struct { + // Chat to which the request was sent. + Chat Chat `json:"chat"` + // User that sent the join request. + From User `json:"user"` + // Date the request was sent in Unix time. + Date int `json:"date"` + // Bio of the user. + // + // optional + Bio string `json:"bio"` + // InviteLink is the link that was used by the user to send the join request. + // + // optional + InviteLink *ChatInviteLink `json:"invite_link"` +} + +// ChatPermissions describes actions that a non-administrator user is +// allowed to take in a chat. All fields are optional. +type ChatPermissions struct { + // CanSendMessages is true, if the user is allowed to send text messages, + // contacts, locations and venues + // + // optional + CanSendMessages bool `json:"can_send_messages,omitempty"` + // CanSendMediaMessages is true, if the user is allowed to send audios, + // documents, photos, videos, video notes and voice notes, implies + // can_send_messages + // + // optional + CanSendMediaMessages bool `json:"can_send_media_messages,omitempty"` + // CanSendPolls is true, if the user is allowed to send polls, implies + // can_send_messages + // + // optional + CanSendPolls bool `json:"can_send_polls,omitempty"` + // CanSendOtherMessages is true, if the user is allowed to send animations, + // games, stickers and use inline bots, implies can_send_media_messages + // + // optional + CanSendOtherMessages bool `json:"can_send_other_messages,omitempty"` + // CanAddWebPagePreviews is true, if the user is allowed to add web page + // previews to their messages, implies can_send_media_messages + // + // optional + CanAddWebPagePreviews bool `json:"can_add_web_page_previews,omitempty"` + // CanChangeInfo is true, if the user is allowed to change the chat title, + // photo and other settings. Ignored in public supergroups + // + // optional + CanChangeInfo bool `json:"can_change_info,omitempty"` + // CanInviteUsers is true, if the user is allowed to invite new users to the + // chat + // + // optional + CanInviteUsers bool `json:"can_invite_users,omitempty"` + // CanPinMessages is true, if the user is allowed to pin messages. Ignored + // in public supergroups + // + // optional + CanPinMessages bool `json:"can_pin_messages,omitempty"` +} + +// ChatLocation represents a location to which a chat is connected. +type ChatLocation struct { + // Location is the location to which the supergroup is connected. Can't be a + // live location. + Location Location `json:"location"` + // Address is the location address; 1-64 characters, as defined by the chat + // owner + Address string `json:"address"` +} + +// BotCommand represents a bot command. +type BotCommand struct { + // Command text of the command, 1-32 characters. + // Can contain only lowercase English letters, digits and underscores. + Command string `json:"command"` + // Description of the command, 3-256 characters. + Description string `json:"description"` +} + +// BotCommandScope represents the scope to which bot commands are applied. +// +// It contains the fields for all types of scopes, different types only support +// specific (or no) fields. +type BotCommandScope struct { + Type string `json:"type"` + ChatID int64 `json:"chat_id,omitempty"` + UserID int64 `json:"user_id,omitempty"` +} + +// ResponseParameters are various errors that can be returned in APIResponse. +type ResponseParameters struct { + // The group has been migrated to a supergroup with the specified identifier. + // + // optional + MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` + // In case of exceeding flood control, the number of seconds left to wait + // before the request can be repeated. + // + // optional + RetryAfter int `json:"retry_after,omitempty"` +} + +// BaseInputMedia is a base type for the InputMedia types. +type BaseInputMedia struct { + // Type of the result. + Type string `json:"type"` + // Media file to send. Pass a file_id to send a file + // that exists on the Telegram servers (recommended), + // pass an HTTP URL for Telegram to get a file from the Internet, + // or pass “attach://<file_attach_name>” to upload a new one + // using multipart/form-data under <file_attach_name> name. + Media RequestFileData `json:"media"` + // thumb intentionally missing as it is not currently compatible + + // Caption of the video to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities"` +} + +// InputMediaPhoto is a photo to send as part of a media group. +type InputMediaPhoto struct { + BaseInputMedia +} + +// InputMediaVideo is a video to send as part of a media group. +type InputMediaVideo struct { + BaseInputMedia + // Thumbnail of the file sent; can be ignored if thumbnail generation for + // the file is supported server-side. + // + // optional + Thumb RequestFileData `json:"thumb,omitempty"` + // Width video width + // + // optional + Width int `json:"width,omitempty"` + // Height video height + // + // optional + Height int `json:"height,omitempty"` + // Duration video duration + // + // optional + Duration int `json:"duration,omitempty"` + // SupportsStreaming pass True, if the uploaded video is suitable for streaming. + // + // optional + SupportsStreaming bool `json:"supports_streaming,omitempty"` +} + +// InputMediaAnimation is an animation to send as part of a media group. +type InputMediaAnimation struct { + BaseInputMedia + // Thumbnail of the file sent; can be ignored if thumbnail generation for + // the file is supported server-side. + // + // optional + Thumb RequestFileData `json:"thumb,omitempty"` + // Width video width + // + // optional + Width int `json:"width,omitempty"` + // Height video height + // + // optional + Height int `json:"height,omitempty"` + // Duration video duration + // + // optional + Duration int `json:"duration,omitempty"` +} + +// InputMediaAudio is an audio to send as part of a media group. +type InputMediaAudio struct { + BaseInputMedia + // Thumbnail of the file sent; can be ignored if thumbnail generation for + // the file is supported server-side. + // + // optional + Thumb RequestFileData `json:"thumb,omitempty"` + // Duration of the audio in seconds + // + // optional + Duration int `json:"duration,omitempty"` + // Performer of the audio + // + // optional + Performer string `json:"performer,omitempty"` + // Title of the audio + // + // optional + Title string `json:"title,omitempty"` +} + +// InputMediaDocument is a general file to send as part of a media group. +type InputMediaDocument struct { + BaseInputMedia + // Thumbnail of the file sent; can be ignored if thumbnail generation for + // the file is supported server-side. + // + // optional + Thumb RequestFileData `json:"thumb,omitempty"` + // DisableContentTypeDetection disables automatic server-side content type + // detection for files uploaded using multipart/form-data. Always true, if + // the document is sent as part of an album + // + // optional + DisableContentTypeDetection bool `json:"disable_content_type_detection,omitempty"` +} + +// Sticker represents a sticker. +type Sticker struct { + // FileID is an identifier for this file, which can be used to download or + // reuse the file + FileID string `json:"file_id"` + // FileUniqueID is a unique identifier for this file, + // which is supposed to be the same over time and for different bots. + // Can't be used to download or reuse the file. + FileUniqueID string `json:"file_unique_id"` + // Width sticker width + Width int `json:"width"` + // Height sticker height + Height int `json:"height"` + // IsAnimated true, if the sticker is animated + // + // optional + IsAnimated bool `json:"is_animated,omitempty"` + // Thumbnail sticker thumbnail in the .WEBP or .JPG format + // + // optional + Thumbnail *PhotoSize `json:"thumb,omitempty"` + // Emoji associated with the sticker + // + // optional + Emoji string `json:"emoji,omitempty"` + // SetName of the sticker set to which the sticker belongs + // + // optional + SetName string `json:"set_name,omitempty"` + // MaskPosition is for mask stickers, the position where the mask should be + // placed + // + // optional + MaskPosition *MaskPosition `json:"mask_position,omitempty"` + // FileSize + // + // optional + FileSize int `json:"file_size,omitempty"` +} + +// StickerSet represents a sticker set. +type StickerSet struct { + // Name sticker set name + Name string `json:"name"` + // Title sticker set title + Title string `json:"title"` + // IsAnimated true, if the sticker set contains animated stickers + IsAnimated bool `json:"is_animated"` + // ContainsMasks true, if the sticker set contains masks + ContainsMasks bool `json:"contains_masks"` + // Stickers list of all set stickers + Stickers []Sticker `json:"stickers"` + // Thumb is the sticker set thumbnail in the .WEBP or .TGS format + Thumbnail *PhotoSize `json:"thumb"` +} + +// MaskPosition describes the position on faces where a mask should be placed +// by default. +type MaskPosition struct { + // The part of the face relative to which the mask should be placed. + // One of “forehead”, “eyes”, “mouth”, or “chin”. + Point string `json:"point"` + // Shift by X-axis measured in widths of the mask scaled to the face size, + // from left to right. For example, choosing -1.0 will place mask just to + // the left of the default mask position. + XShift float64 `json:"x_shift"` + // Shift by Y-axis measured in heights of the mask scaled to the face size, + // from top to bottom. For example, 1.0 will place the mask just below the + // default mask position. + YShift float64 `json:"y_shift"` + // Mask scaling coefficient. For example, 2.0 means double size. + Scale float64 `json:"scale"` +} + +// Game represents a game. Use BotFather to create and edit games, their short +// names will act as unique identifiers. +type Game struct { + // Title of the game + Title string `json:"title"` + // Description of the game + Description string `json:"description"` + // Photo that will be displayed in the game message in chats. + Photo []PhotoSize `json:"photo"` + // Text a brief description of the game or high scores included in the game message. + // Can be automatically edited to include current high scores for the game + // when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters. + // + // optional + Text string `json:"text,omitempty"` + // TextEntities special entities that appear in text, such as usernames, URLs, bot commands, etc. + // + // optional + TextEntities []MessageEntity `json:"text_entities,omitempty"` + // Animation is an animation that will be displayed in the game message in chats. + // Upload via BotFather (https://t.me/botfather). + // + // optional + Animation Animation `json:"animation,omitempty"` +} + +// GameHighScore is a user's score and position on the leaderboard. +type GameHighScore struct { + // Position in high score table for the game + Position int `json:"position"` + // User user + User User `json:"user"` + // Score score + Score int `json:"score"` +} + +// CallbackGame is for starting a game in an inline keyboard button. +type CallbackGame struct{} + +// WebhookInfo is information about a currently set webhook. +type WebhookInfo struct { + // URL webhook URL, may be empty if webhook is not set up. + URL string `json:"url"` + // HasCustomCertificate true, if a custom certificate was provided for webhook certificate checks. + HasCustomCertificate bool `json:"has_custom_certificate"` + // PendingUpdateCount number of updates awaiting delivery. + PendingUpdateCount int `json:"pending_update_count"` + // IPAddress is the currently used webhook IP address + // + // optional + IPAddress string `json:"ip_address,omitempty"` + // LastErrorDate unix time for the most recent error + // that happened when trying to deliver an update via webhook. + // + // optional + LastErrorDate int `json:"last_error_date,omitempty"` + // LastErrorMessage error message in human-readable format for the most recent error + // that happened when trying to deliver an update via webhook. + // + // optional + LastErrorMessage string `json:"last_error_message,omitempty"` + // MaxConnections maximum allowed number of simultaneous + // HTTPS connections to the webhook for update delivery. + // + // optional + MaxConnections int `json:"max_connections,omitempty"` + // AllowedUpdates is a list of update types the bot is subscribed to. + // Defaults to all update types + // + // optional + AllowedUpdates []string `json:"allowed_updates,omitempty"` +} + +// IsSet returns true if a webhook is currently set. +func (info WebhookInfo) IsSet() bool { + return info.URL != "" +} + +// InlineQuery is a Query from Telegram for an inline request. +type InlineQuery struct { + // ID unique identifier for this query + ID string `json:"id"` + // From sender + From *User `json:"from"` + // Query text of the query (up to 256 characters). + Query string `json:"query"` + // Offset of the results to be returned, can be controlled by the bot. + Offset string `json:"offset"` + // Type of the chat, from which the inline query was sent. Can be either + // “sender” for a private chat with the inline query sender, “private”, + // “group”, “supergroup”, or “channel”. The chat type should be always known + // for requests sent from official clients and most third-party clients, + // unless the request was sent from a secret chat + // + // optional + ChatType string `json:"chat_type"` + // Location sender location, only for bots that request user location. + // + // optional + Location *Location `json:"location,omitempty"` +} + +// InlineQueryResultCachedAudio is an inline query response with cached audio. +type InlineQueryResultCachedAudio struct { + // Type of the result, must be audio + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // AudioID a valid file identifier for the audio file + AudioID string `json:"audio_file_id"` + // Caption 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the audio + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedDocument is an inline query response with cached document. +type InlineQueryResultCachedDocument struct { + // Type of the result, must be a document + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // DocumentID a valid file identifier for the file + DocumentID string `json:"document_file_id"` + // Title for the result + // + // optional + Title string `json:"title,omitempty"` + // Caption of the document to be sent, 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // Description short description of the result + // + // optional + Description string `json:"description,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // // See formatting options for more details + // // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the file + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedGIF is an inline query response with cached gif. +type InlineQueryResultCachedGIF struct { + // Type of the result, must be gif. + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes. + ID string `json:"id"` + // GifID a valid file identifier for the GIF file. + GIFID string `json:"gif_file_id"` + // Title for the result + // + // optional + Title string `json:"title,omitempty"` + // Caption of the GIF file to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message. + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the GIF animation. + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedMPEG4GIF is an inline query response with cached +// H.264/MPEG-4 AVC video without sound gif. +type InlineQueryResultCachedMPEG4GIF struct { + // Type of the result, must be mpeg4_gif + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // MPEG4FileID a valid file identifier for the MP4 file + MPEG4FileID string `json:"mpeg4_file_id"` + // Title for the result + // + // optional + Title string `json:"title,omitempty"` + // Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message. + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the video animation. + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedPhoto is an inline query response with cached photo. +type InlineQueryResultCachedPhoto struct { + // Type of the result, must be a photo. + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes. + ID string `json:"id"` + // PhotoID a valid file identifier of the photo. + PhotoID string `json:"photo_file_id"` + // Title for the result. + // + // optional + Title string `json:"title,omitempty"` + // Description short description of the result. + // + // optional + Description string `json:"description,omitempty"` + // Caption of the photo to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the photo caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message. + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the photo. + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedSticker is an inline query response with cached sticker. +type InlineQueryResultCachedSticker struct { + // Type of the result, must be a sticker + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // StickerID a valid file identifier of the sticker + StickerID string `json:"sticker_file_id"` + // Title is a title + Title string `json:"title"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the sticker + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedVideo is an inline query response with cached video. +type InlineQueryResultCachedVideo struct { + // Type of the result, must be video + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // VideoID a valid file identifier for the video file + VideoID string `json:"video_file_id"` + // Title for the result + Title string `json:"title"` + // Description short description of the result + // + // optional + Description string `json:"description,omitempty"` + // Caption of the video to be sent, 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the video + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultCachedVoice is an inline query response with cached voice. +type InlineQueryResultCachedVoice struct { + // Type of the result, must be voice + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // VoiceID a valid file identifier for the voice message + VoiceID string `json:"voice_file_id"` + // Title voice message title + Title string `json:"title"` + // Caption 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the voice message + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultArticle represents a link to an article or web page. +type InlineQueryResultArticle struct { + // Type of the result, must be article. + Type string `json:"type"` + // ID unique identifier for this result, 1-64 Bytes. + ID string `json:"id"` + // Title of the result + Title string `json:"title"` + // InputMessageContent content of the message to be sent. + InputMessageContent interface{} `json:"input_message_content,omitempty"` + // ReplyMarkup Inline keyboard attached to the message. + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // URL of the result. + // + // optional + URL string `json:"url,omitempty"` + // HideURL pass True, if you don't want the URL to be shown in the message. + // + // optional + HideURL bool `json:"hide_url,omitempty"` + // Description short description of the result. + // + // optional + Description string `json:"description,omitempty"` + // ThumbURL url of the thumbnail for the result + // + // optional + ThumbURL string `json:"thumb_url,omitempty"` + // ThumbWidth thumbnail width + // + // optional + ThumbWidth int `json:"thumb_width,omitempty"` + // ThumbHeight thumbnail height + // + // optional + ThumbHeight int `json:"thumb_height,omitempty"` +} + +// InlineQueryResultAudio is an inline query response audio. +type InlineQueryResultAudio struct { + // Type of the result, must be audio + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // URL a valid url for the audio file + URL string `json:"audio_url"` + // Title is a title + Title string `json:"title"` + // Caption 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // Performer is a performer + // + // optional + Performer string `json:"performer,omitempty"` + // Duration audio duration in seconds + // + // optional + Duration int `json:"audio_duration,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the audio + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultContact is an inline query response contact. +type InlineQueryResultContact struct { + Type string `json:"type"` // required + ID string `json:"id"` // required + PhoneNumber string `json:"phone_number"` // required + FirstName string `json:"first_name"` // required + LastName string `json:"last_name"` + VCard string `json:"vcard"` + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + InputMessageContent interface{} `json:"input_message_content,omitempty"` + ThumbURL string `json:"thumb_url"` + ThumbWidth int `json:"thumb_width"` + ThumbHeight int `json:"thumb_height"` +} + +// InlineQueryResultGame is an inline query response game. +type InlineQueryResultGame struct { + // Type of the result, must be game + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // GameShortName short name of the game + GameShortName string `json:"game_short_name"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` +} + +// InlineQueryResultDocument is an inline query response document. +type InlineQueryResultDocument struct { + // Type of the result, must be a document + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // Title for the result + Title string `json:"title"` + // Caption of the document to be sent, 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // URL a valid url for the file + URL string `json:"document_url"` + // MimeType of the content of the file, either “application/pdf” or “application/zip” + MimeType string `json:"mime_type"` + // Description short description of the result + // + // optional + Description string `json:"description,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the file + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` + // ThumbURL url of the thumbnail (jpeg only) for the file + // + // optional + ThumbURL string `json:"thumb_url,omitempty"` + // ThumbWidth thumbnail width + // + // optional + ThumbWidth int `json:"thumb_width,omitempty"` + // ThumbHeight thumbnail height + // + // optional + ThumbHeight int `json:"thumb_height,omitempty"` +} + +// InlineQueryResultGIF is an inline query response GIF. +type InlineQueryResultGIF struct { + // Type of the result, must be gif. + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes. + ID string `json:"id"` + // URL a valid URL for the GIF file. File size must not exceed 1MB. + URL string `json:"gif_url"` + // ThumbURL url of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result. + ThumbURL string `json:"thumb_url"` + // Width of the GIF + // + // optional + Width int `json:"gif_width,omitempty"` + // Height of the GIF + // + // optional + Height int `json:"gif_height,omitempty"` + // Duration of the GIF + // + // optional + Duration int `json:"gif_duration,omitempty"` + // Title for the result + // + // optional + Title string `json:"title,omitempty"` + // Caption of the GIF file to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the GIF animation. + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultLocation is an inline query response location. +type InlineQueryResultLocation struct { + // Type of the result, must be location + Type string `json:"type"` + // ID unique identifier for this result, 1-64 Bytes + ID string `json:"id"` + // Latitude of the location in degrees + Latitude float64 `json:"latitude"` + // Longitude of the location in degrees + Longitude float64 `json:"longitude"` + // Title of the location + Title string `json:"title"` + // HorizontalAccuracy is the radius of uncertainty for the location, + // measured in meters; 0-1500 + // + // optional + HorizontalAccuracy float64 `json:"horizontal_accuracy"` + // LivePeriod is the period in seconds for which the location can be + // updated, should be between 60 and 86400. + // + // optional + LivePeriod int `json:"live_period"` + // Heading is for live locations, a direction in which the user is moving, + // in degrees. Must be between 1 and 360 if specified. + // + // optional + Heading int `json:"heading"` + // ProximityAlertRadius is for live locations, a maximum distance for + // proximity alerts about approaching another chat member, in meters. Must + // be between 1 and 100000 if specified. + // + // optional + ProximityAlertRadius int `json:"proximity_alert_radius"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the location + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` + // ThumbURL url of the thumbnail for the result + // + // optional + ThumbURL string `json:"thumb_url,omitempty"` + // ThumbWidth thumbnail width + // + // optional + ThumbWidth int `json:"thumb_width,omitempty"` + // ThumbHeight thumbnail height + // + // optional + ThumbHeight int `json:"thumb_height,omitempty"` +} + +// InlineQueryResultMPEG4GIF is an inline query response MPEG4 GIF. +type InlineQueryResultMPEG4GIF struct { + // Type of the result, must be mpeg4_gif + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // URL a valid URL for the MP4 file. File size must not exceed 1MB + URL string `json:"mpeg4_url"` + // Width video width + // + // optional + Width int `json:"mpeg4_width"` + // Height vVideo height + // + // optional + Height int `json:"mpeg4_height"` + // Duration video duration + // + // optional + Duration int `json:"mpeg4_duration"` + // ThumbURL url of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result. + ThumbURL string `json:"thumb_url"` + // Title for the result + // + // optional + Title string `json:"title,omitempty"` + // Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the video animation + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultPhoto is an inline query response photo. +type InlineQueryResultPhoto struct { + // Type of the result, must be article. + Type string `json:"type"` + // ID unique identifier for this result, 1-64 Bytes. + ID string `json:"id"` + // URL a valid URL of the photo. Photo must be in jpeg format. + // Photo size must not exceed 5MB. + URL string `json:"photo_url"` + // MimeType + MimeType string `json:"mime_type"` + // Width of the photo + // + // optional + Width int `json:"photo_width,omitempty"` + // Height of the photo + // + // optional + Height int `json:"photo_height,omitempty"` + // ThumbURL url of the thumbnail for the photo. + // + // optional + ThumbURL string `json:"thumb_url,omitempty"` + // Title for the result + // + // optional + Title string `json:"title,omitempty"` + // Description short description of the result + // + // optional + Description string `json:"description,omitempty"` + // Caption of the photo to be sent, 0-1024 characters after entities parsing. + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the photo caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // ReplyMarkup inline keyboard attached to the message. + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // InputMessageContent content of the message to be sent instead of the photo. + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultVenue is an inline query response venue. +type InlineQueryResultVenue struct { + // Type of the result, must be venue + Type string `json:"type"` + // ID unique identifier for this result, 1-64 Bytes + ID string `json:"id"` + // Latitude of the venue location in degrees + Latitude float64 `json:"latitude"` + // Longitude of the venue location in degrees + Longitude float64 `json:"longitude"` + // Title of the venue + Title string `json:"title"` + // Address of the venue + Address string `json:"address"` + // FoursquareID foursquare identifier of the venue if known + // + // optional + FoursquareID string `json:"foursquare_id,omitempty"` + // FoursquareType foursquare type of the venue, if known. + // (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.) + // + // optional + FoursquareType string `json:"foursquare_type,omitempty"` + // GooglePlaceID is the Google Places identifier of the venue + // + // optional + GooglePlaceID string `json:"google_place_id,omitempty"` + // GooglePlaceType is the Google Places type of the venue + // + // optional + GooglePlaceType string `json:"google_place_type,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the venue + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` + // ThumbURL url of the thumbnail for the result + // + // optional + ThumbURL string `json:"thumb_url,omitempty"` + // ThumbWidth thumbnail width + // + // optional + ThumbWidth int `json:"thumb_width,omitempty"` + // ThumbHeight thumbnail height + // + // optional + ThumbHeight int `json:"thumb_height,omitempty"` +} + +// InlineQueryResultVideo is an inline query response video. +type InlineQueryResultVideo struct { + // Type of the result, must be video + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // URL a valid url for the embedded video player or video file + URL string `json:"video_url"` + // MimeType of the content of video url, “text/html” or “video/mp4” + MimeType string `json:"mime_type"` + // + // ThumbURL url of the thumbnail (jpeg only) for the video + // optional + ThumbURL string `json:"thumb_url,omitempty"` + // Title for the result + Title string `json:"title"` + // Caption of the video to be sent, 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // Width video width + // + // optional + Width int `json:"video_width,omitempty"` + // Height video height + // + // optional + Height int `json:"video_height,omitempty"` + // Duration video duration in seconds + // + // optional + Duration int `json:"video_duration,omitempty"` + // Description short description of the result + // + // optional + Description string `json:"description,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the video. + // This field is required if InlineQueryResultVideo is used to send + // an HTML-page as a result (e.g., a YouTube video). + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// InlineQueryResultVoice is an inline query response voice. +type InlineQueryResultVoice struct { + // Type of the result, must be voice + Type string `json:"type"` + // ID unique identifier for this result, 1-64 bytes + ID string `json:"id"` + // URL a valid URL for the voice recording + URL string `json:"voice_url"` + // Title recording title + Title string `json:"title"` + // Caption 0-1024 characters after entities parsing + // + // optional + Caption string `json:"caption,omitempty"` + // ParseMode mode for parsing entities in the video caption. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // CaptionEntities is a list of special entities that appear in the caption, + // which can be specified instead of parse_mode + // + // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` + // Duration recording duration in seconds + // + // optional + Duration int `json:"voice_duration,omitempty"` + // ReplyMarkup inline keyboard attached to the message + // + // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + // InputMessageContent content of the message to be sent instead of the voice recording + // + // optional + InputMessageContent interface{} `json:"input_message_content,omitempty"` +} + +// ChosenInlineResult is an inline query result chosen by a User +type ChosenInlineResult struct { + // ResultID the unique identifier for the result that was chosen + ResultID string `json:"result_id"` + // From the user that chose the result + From *User `json:"from"` + // Location sender location, only for bots that require user location + // + // optional + Location *Location `json:"location,omitempty"` + // InlineMessageID identifier of the sent inline message. + // Available only if there is an inline keyboard attached to the message. + // Will be also received in callback queries and can be used to edit the message. + // + // optional + InlineMessageID string `json:"inline_message_id,omitempty"` + // Query the query that was used to obtain the result + Query string `json:"query"` +} + +// InputTextMessageContent contains text for displaying +// as an inline query result. +type InputTextMessageContent struct { + // Text of the message to be sent, 1-4096 characters + Text string `json:"message_text"` + // ParseMode mode for parsing entities in the message text. + // See formatting options for more details + // (https://core.telegram.org/bots/api#formatting-options). + // + // optional + ParseMode string `json:"parse_mode,omitempty"` + // Entities is a list of special entities that appear in message text, which + // can be specified instead of parse_mode + // + // optional + Entities []MessageEntity `json:"entities,omitempty"` + // DisableWebPagePreview disables link previews for links in the sent message + // + // optional + DisableWebPagePreview bool `json:"disable_web_page_preview,omitempty"` +} + +// InputLocationMessageContent contains a location for displaying +// as an inline query result. +type InputLocationMessageContent struct { + // Latitude of the location in degrees + Latitude float64 `json:"latitude"` + // Longitude of the location in degrees + Longitude float64 `json:"longitude"` + // HorizontalAccuracy is the radius of uncertainty for the location, + // measured in meters; 0-1500 + // + // optional + HorizontalAccuracy float64 `json:"horizontal_accuracy"` + // LivePeriod is the period in seconds for which the location can be + // updated, should be between 60 and 86400 + // + // optional + LivePeriod int `json:"live_period,omitempty"` + // Heading is for live locations, a direction in which the user is moving, + // in degrees. Must be between 1 and 360 if specified. + // + // optional + Heading int `json:"heading"` + // ProximityAlertRadius is for live locations, a maximum distance for + // proximity alerts about approaching another chat member, in meters. Must + // be between 1 and 100000 if specified. + // + // optional + ProximityAlertRadius int `json:"proximity_alert_radius"` +} + +// InputVenueMessageContent contains a venue for displaying +// as an inline query result. +type InputVenueMessageContent struct { + // Latitude of the venue in degrees + Latitude float64 `json:"latitude"` + // Longitude of the venue in degrees + Longitude float64 `json:"longitude"` + // Title name of the venue + Title string `json:"title"` + // Address of the venue + Address string `json:"address"` + // FoursquareID foursquare identifier of the venue, if known + // + // optional + FoursquareID string `json:"foursquare_id,omitempty"` + // FoursquareType Foursquare type of the venue, if known + // + // optional + FoursquareType string `json:"foursquare_type,omitempty"` + // GooglePlaceID is the Google Places identifier of the venue + // + // optional + GooglePlaceID string `json:"google_place_id"` + // GooglePlaceType is the Google Places type of the venue + // + // optional + GooglePlaceType string `json:"google_place_type"` +} + +// InputContactMessageContent contains a contact for displaying +// as an inline query result. +type InputContactMessageContent struct { + // PhoneNumber contact's phone number + PhoneNumber string `json:"phone_number"` + // FirstName contact's first name + FirstName string `json:"first_name"` + // LastName contact's last name + // + // optional + LastName string `json:"last_name,omitempty"` + // Additional data about the contact in the form of a vCard + // + // optional + VCard string `json:"vcard,omitempty"` +} + +// InputInvoiceMessageContent represents the content of an invoice message to be +// sent as the result of an inline query. +type InputInvoiceMessageContent struct { + // Product name, 1-32 characters + Title string `json:"title"` + // Product description, 1-255 characters + Description string `json:"description"` + // Bot-defined invoice payload, 1-128 bytes. This will not be displayed to + // the user, use for your internal processes. + Payload string `json:"payload"` + // Payment provider token, obtained via Botfather + ProviderToken string `json:"provider_token"` + // Three-letter ISO 4217 currency code + Currency string `json:"currency"` + // Price breakdown, a JSON-serialized list of components (e.g. product + // price, tax, discount, delivery cost, delivery tax, bonus, etc.) + Prices []LabeledPrice `json:"prices"` + // The maximum accepted amount for tips in the smallest units of the + // currency (integer, not float/double). + // + // optional + MaxTipAmount int `json:"max_tip_amount,omitempty"` + // An array of suggested amounts of tip in the smallest units of the + // currency (integer, not float/double). At most 4 suggested tip amounts can + // be specified. The suggested tip amounts must be positive, passed in a + // strictly increased order and must not exceed max_tip_amount. + // + // optional + SuggestedTipAmounts []int `json:"suggested_tip_amounts,omitempty"` + // A JSON-serialized object for data about the invoice, which will be shared + // with the payment provider. A detailed description of the required fields + // should be provided by the payment provider. + // + // optional + ProviderData string `json:"provider_data,omitempty"` + // URL of the product photo for the invoice. Can be a photo of the goods or + // a marketing image for a service. People like it better when they see what + // they are paying for. + // + // optional + PhotoURL string `json:"photo_url,omitempty"` + // Photo size + // + // optional + PhotoSize int `json:"photo_size,omitempty"` + // Photo width + // + // optional + PhotoWidth int `json:"photo_width,omitempty"` + // Photo height + // + // optional + PhotoHeight int `json:"photo_height,omitempty"` + // Pass True, if you require the user's full name to complete the order + // + // optional + NeedName bool `json:"need_name,omitempty"` + // Pass True, if you require the user's phone number to complete the order + // + // optional + NeedPhoneNumber bool `json:"need_phone_number,omitempty"` + // Pass True, if you require the user's email address to complete the order + // + // optional + NeedEmail bool `json:"need_email,omitempty"` + // Pass True, if you require the user's shipping address to complete the order + // + // optional + NeedShippingAddress bool `json:"need_shipping_address,omitempty"` + // Pass True, if user's phone number should be sent to provider + // + // optional + SendPhoneNumberToProvider bool `json:"send_phone_number_to_provider,omitempty"` + // Pass True, if user's email address should be sent to provider + // + // optional + SendEmailToProvider bool `json:"send_email_to_provider,omitempty"` + // Pass True, if the final price depends on the shipping method + // + // optional + IsFlexible bool `json:"is_flexible,omitempty"` +} + +// LabeledPrice represents a portion of the price for goods or services. +type LabeledPrice struct { + // Label portion label + Label string `json:"label"` + // Amount price of the product in the smallest units of the currency (integer, not float/double). + // For example, for a price of US$ 1.45 pass amount = 145. + // See the exp parameter in currencies.json + // (https://core.telegram.org/bots/payments/currencies.json), + // it shows the number of digits past the decimal point + // for each currency (2 for the majority of currencies). + Amount int `json:"amount"` +} + +// Invoice contains basic information about an invoice. +type Invoice struct { + // Title product name + Title string `json:"title"` + // Description product description + Description string `json:"description"` + // StartParameter unique bot deep-linking parameter that can be used to generate this invoice + StartParameter string `json:"start_parameter"` + // Currency three-letter ISO 4217 currency code + // (see https://core.telegram.org/bots/payments#supported-currencies) + Currency string `json:"currency"` + // TotalAmount total price in the smallest units of the currency (integer, not float/double). + // For example, for a price of US$ 1.45 pass amount = 145. + // See the exp parameter in currencies.json + // (https://core.telegram.org/bots/payments/currencies.json), + // it shows the number of digits past the decimal point + // for each currency (2 for the majority of currencies). + TotalAmount int `json:"total_amount"` +} + +// ShippingAddress represents a shipping address. +type ShippingAddress struct { + // CountryCode ISO 3166-1 alpha-2 country code + CountryCode string `json:"country_code"` + // State if applicable + State string `json:"state"` + // City city + City string `json:"city"` + // StreetLine1 first line for the address + StreetLine1 string `json:"street_line1"` + // StreetLine2 second line for the address + StreetLine2 string `json:"street_line2"` + // PostCode address post code + PostCode string `json:"post_code"` +} + +// OrderInfo represents information about an order. +type OrderInfo struct { + // Name user name + // + // optional + Name string `json:"name,omitempty"` + // PhoneNumber user's phone number + // + // optional + PhoneNumber string `json:"phone_number,omitempty"` + // Email user email + // + // optional + Email string `json:"email,omitempty"` + // ShippingAddress user shipping address + // + // optional + ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"` +} + +// ShippingOption represents one shipping option. +type ShippingOption struct { + // ID shipping option identifier + ID string `json:"id"` + // Title option title + Title string `json:"title"` + // Prices list of price portions + Prices []LabeledPrice `json:"prices"` +} + +// SuccessfulPayment contains basic information about a successful payment. +type SuccessfulPayment struct { + // Currency three-letter ISO 4217 currency code + // (see https://core.telegram.org/bots/payments#supported-currencies) + Currency string `json:"currency"` + // TotalAmount total price in the smallest units of the currency (integer, not float/double). + // For example, for a price of US$ 1.45 pass amount = 145. + // See the exp parameter in currencies.json, + // (https://core.telegram.org/bots/payments/currencies.json) + // it shows the number of digits past the decimal point + // for each currency (2 for the majority of currencies). + TotalAmount int `json:"total_amount"` + // InvoicePayload bot specified invoice payload + InvoicePayload string `json:"invoice_payload"` + // ShippingOptionID identifier of the shipping option chosen by the user + // + // optional + ShippingOptionID string `json:"shipping_option_id,omitempty"` + // OrderInfo order info provided by the user + // + // optional + OrderInfo *OrderInfo `json:"order_info,omitempty"` + // TelegramPaymentChargeID telegram payment identifier + TelegramPaymentChargeID string `json:"telegram_payment_charge_id"` + // ProviderPaymentChargeID provider payment identifier + ProviderPaymentChargeID string `json:"provider_payment_charge_id"` +} + +// ShippingQuery contains information about an incoming shipping query. +type ShippingQuery struct { + // ID unique query identifier + ID string `json:"id"` + // From user who sent the query + From *User `json:"from"` + // InvoicePayload bot specified invoice payload + InvoicePayload string `json:"invoice_payload"` + // ShippingAddress user specified shipping address + ShippingAddress *ShippingAddress `json:"shipping_address"` +} + +// PreCheckoutQuery contains information about an incoming pre-checkout query. +type PreCheckoutQuery struct { + // ID unique query identifier + ID string `json:"id"` + // From user who sent the query + From *User `json:"from"` + // Currency three-letter ISO 4217 currency code + // // (see https://core.telegram.org/bots/payments#supported-currencies) + Currency string `json:"currency"` + // TotalAmount total price in the smallest units of the currency (integer, not float/double). + // // For example, for a price of US$ 1.45 pass amount = 145. + // // See the exp parameter in currencies.json, + // // (https://core.telegram.org/bots/payments/currencies.json) + // // it shows the number of digits past the decimal point + // // for each currency (2 for the majority of currencies). + TotalAmount int `json:"total_amount"` + // InvoicePayload bot specified invoice payload + InvoicePayload string `json:"invoice_payload"` + // ShippingOptionID identifier of the shipping option chosen by the user + // + // optional + ShippingOptionID string `json:"shipping_option_id,omitempty"` + // OrderInfo order info provided by the user + // + // optional + OrderInfo *OrderInfo `json:"order_info,omitempty"` +} |