From 65947daaab88cefc8f87b936cb1fafb60d09115d Mon Sep 17 00:00:00 2001 From: pr0head Date: Fri, 13 Oct 2017 17:00:04 +0300 Subject: [PATCH 01/29] Added `live_period` for Location --- configs.go | 8 ++++++-- helpers.go | 20 +++++++++++--------- types.go | 21 ++++++++++++--------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/configs.go b/configs.go index c0293ce..84912ab 100644 --- a/configs.go +++ b/configs.go @@ -571,8 +571,9 @@ func (config VoiceConfig) method() string { // LocationConfig contains information about a SendLocation request. type LocationConfig struct { BaseChat - Latitude float64 // required - Longitude float64 // required + Latitude float64 // required + Longitude float64 // required + LivePeriod int // optional } // values returns a url.Values representation of LocationConfig. @@ -584,6 +585,9 @@ func (config LocationConfig) values() (url.Values, error) { v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64)) v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64)) + if config.LivePeriod != 0 { + v.Add("live_period", strconv.Itoa(config.LivePeriod)) + } return v, nil } diff --git a/helpers.go b/helpers.go index 132d957..2c5d046 100644 --- a/helpers.go +++ b/helpers.go @@ -268,13 +268,14 @@ func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig { // NewLocation shares your location. // // chatID is where to send it, latitude and longitude are coordinates. -func NewLocation(chatID int64, latitude float64, longitude float64) LocationConfig { +func NewLocation(chatID int64, latitude float64, longitude float64, live_period int) LocationConfig { return LocationConfig{ BaseChat: BaseChat{ ChatID: chatID, }, - Latitude: latitude, - Longitude: longitude, + Latitude: latitude, + Longitude: longitude, + LivePeriod: live_period, } } @@ -465,13 +466,14 @@ func NewInlineQueryResultDocument(id, url, title, mimeType string) InlineQueryRe } // NewInlineQueryResultLocation creates a new inline query location. -func NewInlineQueryResultLocation(id, title string, latitude, longitude float64) InlineQueryResultLocation { +func NewInlineQueryResultLocation(id, title string, latitude, longitude float64, live_period int) InlineQueryResultLocation { return InlineQueryResultLocation{ - Type: "location", - ID: id, - Title: title, - Latitude: latitude, - Longitude: longitude, + Type: "location", + ID: id, + Title: title, + Latitude: latitude, + Longitude: longitude, + LivePeriod: live_period, } } diff --git a/types.go b/types.go index 91875bb..691a645 100644 --- a/types.go +++ b/types.go @@ -331,8 +331,9 @@ type Contact struct { // Location contains information about a place. type Location struct { - Longitude float64 `json:"longitude"` - Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` + LivePeriod int `json:"live_period"` } // Venue contains information about a venue, including its Location. @@ -639,11 +640,12 @@ type InlineQueryResultDocument struct { // 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 + Type string `json:"type"` // required + ID string `json:"id"` // required + Latitude float64 `json:"latitude"` // required + Longitude float64 `json:"longitude"` // required + LivePeriod int `json:"live_period"` // optional + Title string `json:"title"` // required ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` ThumbURL string `json:"thumb_url"` @@ -679,8 +681,9 @@ type InputTextMessageContent struct { // InputLocationMessageContent contains a location for displaying // as an inline query result. type InputLocationMessageContent struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + LivePeriod int `json:"live_period"` } // InputVenueMessageContent contains a venue for displaying From 7031d820be302f01d2e6380912101e3be6736878 Mon Sep 17 00:00:00 2001 From: pr0head Date: Fri, 13 Oct 2017 22:53:47 +0300 Subject: [PATCH 02/29] Added `live_period` for Location (tests) --- bot_test.go | 2 +- helpers_test.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bot_test.go b/bot_test.go index a811a27..968f78b 100644 --- a/bot_test.go +++ b/bot_test.go @@ -266,7 +266,7 @@ func TestSendWithContact(t *testing.T) { func TestSendWithLocation(t *testing.T) { bot, _ := getBot(t) - _, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40)) + _, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40, 86400)) if err != nil { t.Error(err) diff --git a/helpers_test.go b/helpers_test.go index 9542f02..7c510e9 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -126,13 +126,14 @@ func TestNewInlineQueryResultDocument(t *testing.T) { } func TestNewInlineQueryResultLocation(t *testing.T) { - result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50) + result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50, 86400) if result.Type != "location" || result.ID != "id" || result.Title != "name" || result.Latitude != 40 || - result.Longitude != 50 { + result.Longitude != 50 || + result.LivePeriod != 86400 { t.Fail() } } From 5cbecde819a8e220f3fafcf748aef01cf4cffcd8 Mon Sep 17 00:00:00 2001 From: pr0head Date: Sat, 14 Oct 2017 01:05:24 +0300 Subject: [PATCH 03/29] Added `editMessageLiveLocation` and `stopMessageLiveLocation` methods --- configs.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ types.go | 21 +++++++++------------ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/configs.go b/configs.go index 84912ab..337c601 100644 --- a/configs.go +++ b/configs.go @@ -597,6 +597,51 @@ func (config LocationConfig) method() string { return "sendLocation" } +// LocationConfig contains information about a SendLocation request. +type EditMessageLiveLocationConfig struct { + BaseEdit + Latitude float64 // required + Longitude float64 // required +} + +// values returns a url.Values representation of EditMessageLiveLocationConfig. +func (config EditMessageLiveLocationConfig) values() (url.Values, error) { + v, err := config.BaseEdit.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 edit message Live Location. +func (config EditMessageLiveLocationConfig) method() string { + return "editMessageLiveLocation" +} + +// LocationConfig contains information about a StopMessageLiveLocation request. +type StopMessageLiveLocationConfig struct { + BaseEdit +} + +// values returns a url.Values representation of StopMessageLiveLocationConfig. +func (config StopMessageLiveLocationConfig) values() (url.Values, error) { + v, err := config.BaseEdit.values() + if err != nil { + return v, err + } + + return v, nil +} + +// method returns Telegram API method name for stop message Live Location. +func (config StopMessageLiveLocationConfig) method() string { + return "stopMessageLiveLocation" +} + // VenueConfig contains information about a SendVenue request. type VenueConfig struct { BaseChat diff --git a/types.go b/types.go index 691a645..91875bb 100644 --- a/types.go +++ b/types.go @@ -331,9 +331,8 @@ type Contact struct { // Location contains information about a place. type Location struct { - Longitude float64 `json:"longitude"` - Latitude float64 `json:"latitude"` - LivePeriod int `json:"live_period"` + Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` } // Venue contains information about a venue, including its Location. @@ -640,12 +639,11 @@ type InlineQueryResultDocument struct { // 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 - LivePeriod int `json:"live_period"` // optional - Title string `json:"title"` // required + 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"` @@ -681,9 +679,8 @@ type InputTextMessageContent struct { // InputLocationMessageContent contains a location for displaying // as an inline query result. type InputLocationMessageContent struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - LivePeriod int `json:"live_period"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` } // InputVenueMessageContent contains a venue for displaying From dffc002f9e2338af6f66ecd72df5db722c9e285d Mon Sep 17 00:00:00 2001 From: pr0head Date: Tue, 17 Oct 2017 19:00:20 +0300 Subject: [PATCH 04/29] Fix struct for InlineQueryResultLocation --- types.go | 1 + 1 file changed, 1 insertion(+) diff --git a/types.go b/types.go index 91875bb..e59afbb 100644 --- a/types.go +++ b/types.go @@ -643,6 +643,7 @@ type InlineQueryResultLocation struct { ID string `json:"id"` // required Latitude float64 `json:"latitude"` // required Longitude float64 `json:"longitude"` // required + LivePeriod int `json:"live_period"` // optional Title string `json:"title"` // required ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` From e97c2417c99a9e301612ef45d9a549b628e062b5 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Thu, 28 Dec 2017 23:17:32 -0600 Subject: [PATCH 05/29] Resolve some linter issues. --- bot_test.go | 14 +++++++++++--- types.go | 7 ++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bot_test.go b/bot_test.go index 0fb0855..374773f 100644 --- a/bot_test.go +++ b/bot_test.go @@ -373,7 +373,10 @@ func TestSendWithNewStickerAndKeyboardHide(t *testing.T) { bot, _ := getBot(t) msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg") - msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{true, false} + msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{ + RemoveKeyboard: true, + Selective: false, + } _, err := bot.Send(msg) if err != nil { @@ -386,7 +389,10 @@ func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) { bot, _ := getBot(t) msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID) - msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{true, false} + msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{ + RemoveKeyboard: true, + Selective: false, + } _, err := bot.Send(msg) @@ -399,7 +405,9 @@ func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) { func TestGetFile(t *testing.T) { bot, _ := getBot(t) - file := tgbotapi.FileConfig{ExistingPhotoFileID} + file := tgbotapi.FileConfig{ + FileID: ExistingPhotoFileID, + } _, err := bot.GetFile(file) diff --git a/types.go b/types.go index bef68b8..bc0fd00 100644 --- a/types.go +++ b/types.go @@ -230,11 +230,12 @@ func (m *Message) CommandArguments() string { // 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 - } else { - return m.Text[entity.Length+1:] } + + return m.Text[entity.Length+1:] } // MessageEntity contains information about data in a Message. @@ -410,7 +411,7 @@ type InlineKeyboardButton struct { 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 + Pay bool `json:"pay,omitempty"` // optional } // CallbackQuery is data sent when a keyboard button with callback data From 271adc4d9703392d15d9ea33f316393bb0362ba8 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 00:44:47 -0600 Subject: [PATCH 06/29] Bot API 3.2, introduce Request to replace APIResponse methods. --- bot.go | 190 ++++++--------------------- bot_test.go | 2 +- configs.go | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++- types.go | 30 ++++- 4 files changed, 433 insertions(+), 158 deletions(-) diff --git a/bot.go b/bot.go index e201944..f2e9877 100644 --- a/bot.go +++ b/bot.go @@ -109,21 +109,6 @@ func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) 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. @@ -262,86 +247,55 @@ func (bot *BotAPI) IsMessageToMe(message Message) bool { // // 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() - + resp, err := bot.Request(c) if err != nil { return Message{}, err } - message, err := bot.makeMessageRequest(method, v) - if err != nil { - return Message{}, err - } + var message Message + err = json.Unmarshal(resp.Result, &message) - return message, nil + return message, err } -// 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 - } +// Request makes a request to Telegram that returns an APIResponse, rather than +// a Message. +func (bot *BotAPI) Request(c Chattable) (APIResponse, error) { + switch t := c.(type) { + case Fileable: + if t.useExistingFile() { + v, err := t.values() + if err != nil { + return APIResponse{}, err + } - var message Message - json.Unmarshal(resp.Result, &message) + return bot.MakeRequest(t.method(), v) + } - bot.debugLog(method, nil, message) + p, err := t.params() + if err != nil { + return APIResponse{}, err + } - return message, nil -} + return bot.UploadFile(t.method(), p, t.name(), t.getFile()) + default: + v, err := c.values() + if err != nil { + return APIResponse{}, err + } -// 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.MakeRequest(c.method(), v) } - - 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 +// 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) } - - return message, nil } // GetUserProfilePhotos gets a user's profile photos. @@ -423,11 +377,6 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { return updates, nil } -// RemoveWebhook unsets the webhook. -func (bot *BotAPI) RemoveWebhook() (APIResponse, error) { - return bot.MakeRequest("setWebhook", url.Values{}) -} - // SetWebhook sets a webhook. // // If this is set, GetUpdates will not get any data! @@ -435,7 +384,6 @@ func (bot *BotAPI) RemoveWebhook() (APIResponse, error) { // 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()) @@ -806,54 +754,6 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh 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{} @@ -875,26 +775,20 @@ func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { return inviteLink, err } -// PinChatMessage pin message in supergroup -func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) { +// GetStickerSet returns a StickerSet. +func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { v, err := config.values() if err != nil { - return APIResponse{}, err + return StickerSet{}, nil } - 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() + resp, err := bot.MakeRequest(config.method(), v) if err != nil { - return APIResponse{}, err + return StickerSet{}, nil } - bot.debugLog(config.method(), v, nil) + var stickers StickerSet + err = json.Unmarshal(resp.Result, &stickers) - return bot.MakeRequest(config.method(), v) + return stickers, err } diff --git a/bot_test.go b/bot_test.go index 374773f..03f17b2 100644 --- a/bot_test.go +++ b/bot_test.go @@ -420,7 +420,7 @@ func TestGetFile(t *testing.T) { func TestSendChatConfig(t *testing.T) { bot, _ := getBot(t) - _, err := bot.Send(tgbotapi.NewChatAction(ChatID, tgbotapi.ChatTyping)) + _, err := bot.Request(tgbotapi.NewChatAction(ChatID, tgbotapi.ChatTyping)) if err != nil { t.Error(err) diff --git a/configs.go b/configs.go index c0293ce..6780b28 100644 --- a/configs.go +++ b/configs.go @@ -842,6 +842,18 @@ type WebhookConfig struct { MaxConnections int } +// RemoveWebhookConfig is a helper to remove a webhook. +type RemoveWebhookConfig struct { +} + +func (config RemoveWebhookConfig) method() string { + return "setWebhook" +} + +func (config RemoveWebhookConfig) values() (url.Values, error) { + return url.Values{}, nil +} + // FileBytes contains information about a set of bytes to upload // as a File. type FileBytes struct { @@ -1038,8 +1050,8 @@ func (config DeleteMessageConfig) values() (url.Values, error) { // PinChatMessageConfig contains information of a message in a chat to pin. type PinChatMessageConfig struct { - ChatID int64 - MessageID int + ChatID int64 + MessageID int DisableNotification bool } @@ -1072,4 +1084,355 @@ func (config UnpinChatMessageConfig) values() (url.Values, error) { v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) return v, nil -} \ No newline at end of file +} + +// SetChatPhotoConfig allows you to set a group, supergroup, or channel's photo. +type SetChatPhotoConfig struct { + ChatID int64 + ChannelUsername string + + Photo interface{} +} + +func (config SetChatPhotoConfig) method() string { + return "setChatPhoto" +} + +func (config SetChatPhotoConfig) name() string { + return "photo" +} + +func (config SetChatPhotoConfig) values() (url.Values, error) { + v := url.Values{} + + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } + + return v, nil +} + +func (config SetChatPhotoConfig) params() map[string]string { + params := make(map[string]string) + + if config.ChannelUsername == "" { + params["chat_id"] = strconv.FormatInt(config.ChatID, 10) + } else { + params["chat_id"] = config.ChannelUsername + } + + return params +} + +func (config SetChatPhotoConfig) getFile() interface{} { + return config.Photo +} + +func (config SetChatPhotoConfig) useExistingFile() bool { + return false +} + +// 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) values() (url.Values, error) { + v := url.Values{} + + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } + + return v, 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) values() (url.Values, error) { + v := url.Values{} + + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } + + v.Add("title", config.Title) + + return v, 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) values() (url.Values, error) { + v := url.Values{} + + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } + + v.Add("description", config.Description) + + return v, 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) values() (url.Values, error) { + v := url.Values{} + + v.Add("name", config.Name) + + return v, nil +} + +// UploadStickerConfig allows you to upload a sticker for use in a set later. +type UploadStickerConfig struct { + UserID int64 + PNGSticker interface{} +} + +func (config UploadStickerConfig) method() string { + return "uploadStickerFile" +} + +func (config UploadStickerConfig) values() (url.Values, error) { + v := url.Values{} + + v.Add("user_id", strconv.FormatInt(config.UserID, 10)) + + return v, nil +} + +func (config UploadStickerConfig) params() (map[string]string, error) { + params := make(map[string]string) + + params["user_id"] = strconv.FormatInt(config.UserID, 10) + + return params, nil +} + +func (config UploadStickerConfig) name() string { + return "png_sticker" +} + +func (config UploadStickerConfig) getFile() interface{} { + return config.PNGSticker +} + +func (config UploadStickerConfig) useExistingFile() bool { + return false +} + +// NewStickerSetConfig allows creating a new sticker set. +type NewStickerSetConfig struct { + UserID int64 + Name string + Title string + PNGSticker interface{} + Emojis string + ContainsMasks bool + MaskPosition *MaskPosition +} + +func (config NewStickerSetConfig) method() string { + return "createNewStickerSet" +} + +func (config NewStickerSetConfig) values() (url.Values, error) { + v := url.Values{} + + v.Add("user_id", strconv.FormatInt(config.UserID, 10)) + v.Add("name", config.Name) + v.Add("title", config.Title) + if sticker, ok := config.PNGSticker.(string); ok { + v.Add("png_sticker", sticker) + } + v.Add("emojis", config.Emojis) + if config.ContainsMasks { + v.Add("contains_masks", strconv.FormatBool(config.ContainsMasks)) + + data, err := json.Marshal(config.MaskPosition) + if err != nil { + return v, err + } + + v.Add("mask_position", string(data)) + } + + return v, nil +} + +func (config NewStickerSetConfig) params() (map[string]string, error) { + params := make(map[string]string) + + params["user_id"] = strconv.FormatInt(config.UserID, 10) + params["name"] = config.Name + params["title"] = config.Title + params["emojis"] = config.Emojis + if config.ContainsMasks { + params["contains_masks"] = strconv.FormatBool(config.ContainsMasks) + + data, err := json.Marshal(config.MaskPosition) + if err != nil { + return params, err + } + + params["mask_position"] = string(data) + } + + return params, nil +} + +func (config NewStickerSetConfig) getFile() interface{} { + return config.PNGSticker +} + +func (config NewStickerSetConfig) name() string { + return "png_sticker" +} + +func (config NewStickerSetConfig) useExistingFile() bool { + _, ok := config.PNGSticker.(string) + + return ok +} + +// AddStickerConfig allows you to add a sticker to a set. +type AddStickerConfig struct { + UserID int64 + Name string + PNGSticker interface{} + Emojis string + MaskPosition *MaskPosition +} + +func (config AddStickerConfig) method() string { + return "addStickerToSet" +} + +func (config AddStickerConfig) values() (url.Values, error) { + v := url.Values{} + + v.Add("user_id", strconv.FormatInt(config.UserID, 10)) + v.Add("name", config.Name) + if sticker, ok := config.PNGSticker.(string); ok { + v.Add("png_sticker", sticker) + } + v.Add("emojis", config.Emojis) + if config.MaskPosition != nil { + data, err := json.Marshal(config.MaskPosition) + if err != nil { + return v, err + } + + v.Add("mask_position", string(data)) + } + + return v, nil +} + +func (config AddStickerConfig) params() (map[string]string, error) { + params := make(map[string]string) + + params["user_id"] = strconv.FormatInt(config.UserID, 10) + params["name"] = config.Name + params["emojis"] = config.Emojis + if config.MaskPosition != nil { + data, err := json.Marshal(config.MaskPosition) + if err != nil { + return params, err + } + + params["mask_position"] = string(data) + } + + return params, nil +} + +func (config AddStickerConfig) name() string { + return "png_sticker" +} + +func (config AddStickerConfig) getFile() interface{} { + return config.PNGSticker +} + +func (config AddStickerConfig) useExistingFile() bool { + return false +} + +// 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) values() (url.Values, error) { + v := url.Values{} + + v.Add("sticker", config.Sticker) + v.Add("position", strconv.Itoa(config.Position)) + + return v, 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) values() (url.Values, error) { + v := url.Values{} + + v.Add("sticker", config.Sticker) + + return v, nil +} diff --git a/types.go b/types.go index bc0fd00..0828b31 100644 --- a/types.go +++ b/types.go @@ -285,12 +285,22 @@ type Document struct { // Sticker contains information about a sticker. type Sticker struct { - FileID string `json:"file_id"` - Width int `json:"width"` - Height int `json:"height"` - Thumbnail *PhotoSize `json:"thumb"` // optional - Emoji string `json:"emoji"` // optional - FileSize int `json:"file_size"` // optional + FileID string `json:"file_id"` + Width int `json:"width"` + Height int `json:"height"` + Thumbnail *PhotoSize `json:"thumb"` // optional + Emoji string `json:"emoji"` // optional + SetName string `json:"set_name"` // optional + MaskPosition MaskPosition `json:"mask_position"` //optional + FileSize int `json:"file_size"` // optional +} + +// MaskPosition is the position of a mask. +type MaskPosition struct { + Point string `json:"point"` + XShift float32 `json:"x_shift"` + YShift float32 `json:"y_shift"` + Scale float32 `json:"scale"` } // Video contains information about a video. @@ -772,3 +782,11 @@ type PreCheckoutQuery struct { ShippingOptionID string `json:"shipping_option_id,omitempty"` OrderInfo *OrderInfo `json:"order_info,omitempty"` } + +// StickerSet is a collection of stickers. +type StickerSet struct { + Name string `json:"name"` + Title string `json:"title"` + ContainsMasks bool `json:"contains_masks"` + Stickers []Sticker `json:"stickers"` +} From 95a923dc4c570e5aadd7dbed2be03499892b3f6b Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 00:50:45 -0600 Subject: [PATCH 07/29] Update tests. --- bot_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot_test.go b/bot_test.go index 03f17b2..9e64b43 100644 --- a/bot_test.go +++ b/bot_test.go @@ -467,7 +467,7 @@ func TestSetWebhookWithCert(t *testing.T) { time.Sleep(time.Second * 2) - bot.RemoveWebhook() + bot.Request(tgbotapi.RemoveWebhookConfig{}) wh := tgbotapi.NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") _, err := bot.SetWebhook(wh) @@ -476,7 +476,7 @@ func TestSetWebhookWithCert(t *testing.T) { t.Fail() } - bot.RemoveWebhook() + bot.Request(tgbotapi.RemoveWebhookConfig{}) } func TestSetWebhookWithoutCert(t *testing.T) { @@ -484,7 +484,7 @@ func TestSetWebhookWithoutCert(t *testing.T) { time.Sleep(time.Second * 2) - bot.RemoveWebhook() + bot.Request(tgbotapi.RemoveWebhookConfig{}) wh := tgbotapi.NewWebhook("https://example.com/tgbotapi-test/" + bot.Token) _, err := bot.SetWebhook(wh) @@ -493,7 +493,7 @@ func TestSetWebhookWithoutCert(t *testing.T) { t.Fail() } - bot.RemoveWebhook() + bot.Request(tgbotapi.RemoveWebhookConfig{}) } func TestUpdatesChan(t *testing.T) { @@ -611,7 +611,7 @@ func TestDeleteMessage(t *testing.T) { ChatID: message.Chat.ID, MessageID: message.MessageID, } - _, err := bot.DeleteMessage(deleteMessageConfig) + _, err := bot.Request(deleteMessageConfig) if err != nil { t.Error(err) @@ -631,7 +631,7 @@ func TestPinChatMessage(t *testing.T) { MessageID: message.MessageID, DisableNotification: false, } - _, err := bot.PinChatMessage(pinChatMessageConfig) + _, err := bot.Request(pinChatMessageConfig) if err != nil { t.Error(err) @@ -652,12 +652,12 @@ func TestUnpinChatMessage(t *testing.T) { MessageID: message.MessageID, DisableNotification: false, } - _, err := bot.PinChatMessage(pinChatMessageConfig) + _, err := bot.Request(pinChatMessageConfig) unpinChatMessageConfig := tgbotapi.UnpinChatMessageConfig{ ChatID: message.Chat.ID, } - _, err = bot.UnpinChatMessage(unpinChatMessageConfig) + _, err = bot.Request(unpinChatMessageConfig) if err != nil { t.Error(err) From ac87082c555ae9cd9cc2b0b07cf904744cf5841d Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 01:08:01 -0600 Subject: [PATCH 08/29] Remove remaining methods that returned an APIResponse. --- bot.go | 209 ----------------------------------------------- bot_test.go | 8 +- configs.go | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 213 deletions(-) diff --git a/bot.go b/bot.go index f2e9877..402b9ee 100644 --- a/bot.go +++ b/bot.go @@ -377,37 +377,6 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { return updates, nil } -// 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) { @@ -465,85 +434,6 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { 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{} @@ -640,105 +530,6 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) 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() diff --git a/bot_test.go b/bot_test.go index 9e64b43..cb584dc 100644 --- a/bot_test.go +++ b/bot_test.go @@ -470,7 +470,7 @@ func TestSetWebhookWithCert(t *testing.T) { bot.Request(tgbotapi.RemoveWebhookConfig{}) wh := tgbotapi.NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") - _, err := bot.SetWebhook(wh) + _, err := bot.Request(wh) if err != nil { t.Error(err) t.Fail() @@ -487,7 +487,7 @@ func TestSetWebhookWithoutCert(t *testing.T) { bot.Request(tgbotapi.RemoveWebhookConfig{}) wh := tgbotapi.NewWebhook("https://example.com/tgbotapi-test/" + bot.Token) - _, err := bot.SetWebhook(wh) + _, err := bot.Request(wh) if err != nil { t.Error(err) t.Fail() @@ -553,7 +553,7 @@ func ExampleNewWebhook() { log.Printf("Authorized on account %s", bot.Self.UserName) - _, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) + _, err = bot.Request(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) if err != nil { log.Fatal(err) } @@ -594,7 +594,7 @@ func ExampleAnswerInlineQuery() { Results: []interface{}{article}, } - if _, err := bot.AnswerInlineQuery(inlineConf); err != nil { + if _, err := bot.Request(inlineConf); err != nil { log.Println(err) } } diff --git a/configs.go b/configs.go index 6780b28..8981935 100644 --- a/configs.go +++ b/configs.go @@ -842,6 +842,48 @@ type WebhookConfig struct { MaxConnections int } +func (config WebhookConfig) method() string { + return "setWebhook" +} + +func (config WebhookConfig) values() (url.Values, error) { + v := url.Values{} + + if config.URL != nil { + v.Add("url", config.URL.String()) + } + if config.MaxConnections != 0 { + v.Add("max_connections", strconv.Itoa(config.MaxConnections)) + } + + return v, nil +} + +func (config WebhookConfig) params() (map[string]string, error) { + params := make(map[string]string) + + if config.URL != nil { + params["url"] = config.URL.String() + } + if config.MaxConnections != 0 { + params["max_connections"] = strconv.Itoa(config.MaxConnections) + } + + return params, nil +} + +func (config WebhookConfig) name() string { + return "certificate" +} + +func (config WebhookConfig) getFile() interface{} { + return config.Certificate +} + +func (config WebhookConfig) useExistingFile() bool { + return config.URL != nil +} + // RemoveWebhookConfig is a helper to remove a webhook. type RemoveWebhookConfig struct { } @@ -881,6 +923,28 @@ type InlineConfig struct { SwitchPMParameter string `json:"switch_pm_parameter"` } +func (config InlineConfig) method() string { + return "answerInlineQuery" +} + +func (config InlineConfig) values() (url.Values, 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 v, err + } + v.Add("results", string(data)) + v.Add("switch_pm_text", config.SwitchPMText) + v.Add("switch_pm_parameter", config.SwitchPMParameter) + + return v, nil +} + // CallbackConfig contains information on making a CallbackQuery response. type CallbackConfig struct { CallbackQueryID string `json:"callback_query_id"` @@ -890,6 +954,26 @@ type CallbackConfig struct { CacheTime int `json:"cache_time"` } +func (config CallbackConfig) method() string { + return "answerCallbackQuery" +} + +func (config CallbackConfig) values() (url.Values, 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)) + + return v, 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 { @@ -899,12 +983,57 @@ type ChatMemberConfig struct { UserID int } +// UnbanChatMemberConfig allows you to unban a user. +type UnbanChatMemberConfig struct { + ChatMemberConfig +} + +func (config UnbanChatMemberConfig) method() string { + return "unbanChatMember" +} + +func (config UnbanChatMemberConfig) values() (url.Values, 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)) + + return v, nil +} + // KickChatMemberConfig contains extra fields to kick user type KickChatMemberConfig struct { ChatMemberConfig UntilDate int64 } +func (config KickChatMemberConfig) method() string { + return "kickChatMember" +} + +func (config KickChatMemberConfig) values() (url.Values, 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)) + } + + return v, nil +} + // RestrictChatMemberConfig contains fields to restrict members of chat type RestrictChatMemberConfig struct { ChatMemberConfig @@ -915,6 +1044,41 @@ type RestrictChatMemberConfig struct { CanAddWebPagePreviews *bool } +func (config RestrictChatMemberConfig) method() string { + return "restrictChatMember" +} + +func (config RestrictChatMemberConfig) values() (url.Values, 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)) + } + + return v, nil +} + // PromoteChatMemberConfig contains fields to promote members of chat type PromoteChatMemberConfig struct { ChatMemberConfig @@ -928,12 +1092,78 @@ type PromoteChatMemberConfig struct { CanPromoteMembers *bool } +func (config PromoteChatMemberConfig) method() string { + return "promoteChatMember" +} + +func (config PromoteChatMemberConfig) values() (url.Values, 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)) + } + + return v, nil +} + // ChatConfig contains information about getting information on a chat. type ChatConfig struct { ChatID int64 SuperGroupUsername string } +// LeaveChatConfig allows you to leave a chat. +type LeaveChatConfig struct { + ChatID int64 + ChannelUsername string +} + +func (config LeaveChatConfig) method() string { + return "leaveChat" +} + +func (config LeaveChatConfig) values() (url.Values, error) { + v := url.Values{} + + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } + + return v, nil +} + // ChatConfigWithUser contains information about getting information on // a specific user within a chat. type ChatConfigWithUser struct { From ef374648bffe645f2b07f968f6d56636f7057a51 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 01:17:15 -0600 Subject: [PATCH 09/29] Bot API 3.3. --- types.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/types.go b/types.go index 0828b31..7fa174f 100644 --- a/types.go +++ b/types.go @@ -97,9 +97,12 @@ type Chat struct { 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 + Photo *ChatPhoto `json:"photo"` // optional + Description string `json:"description,omitempty"` // optional + InviteLink string `json:"invite_link,omitempty"` // optional + PinnedMessage *Message `json:"pinned_message"` // optional + StickerSetName string `json:"sticker_set_name"` // optional + CanSetStickerSet bool `json:"can_set_sticker_set"` // optional } // IsPrivate returns if the Chat is a private conversation. @@ -137,11 +140,15 @@ type Message struct { ForwardFrom *User `json:"forward_from"` // optional ForwardFromChat *Chat `json:"forward_from_chat"` // optional ForwardFromMessageID int `json:"forward_from_message_id"` // optional + ForwardSignature string `json:"forward_signature"` // optional ForwardDate int `json:"forward_date"` // optional ReplyToMessage *Message `json:"reply_to_message"` // optional EditDate int `json:"edit_date"` // optional + MediaGroupID string `json:"media_group_id"` // optional + AuthorSignature string `json:"author_signature"` // 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 Game *Game `json:"game"` // optional From 8b7b15afc2157f54f6c0ae05351f3a8ddfcd42be Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 01:26:54 -0600 Subject: [PATCH 10/29] Update README.md. --- README.md | 15 +++++++-------- bot.go | 32 +++++++++++++++----------------- helpers_test.go | 3 ++- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 266f4ed..e997939 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ without any additional features. There are other projects for creating something with plugins and command handlers without having to design all that yourself. -Use `github.com/go-telegram-bot-api/telegram-bot-api` for the latest -version, or use `gopkg.in/telegram-bot-api.v4` for the stable build. - Join [the development group](https://telegram.me/go_telegram_bot_api) if you want to ask questions or discuss development. @@ -32,7 +29,8 @@ package main import ( "log" - "gopkg.in/telegram-bot-api.v4" + + "github.com/go-telegram-bot-api/telegram-bot-api" ) func main() { @@ -72,9 +70,10 @@ you may use a slightly different method. package main import ( - "gopkg.in/telegram-bot-api.v4" "log" "net/http" + + "github.com/go-telegram-bot-api/telegram-bot-api" ) func main() { @@ -87,7 +86,7 @@ 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")) + _, err = bot.Request(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) if err != nil { log.Fatal(err) } @@ -108,5 +107,5 @@ properly signed. openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes -Now that [Let's Encrypt](https://letsencrypt.org) has entered public beta, -you may wish to generate your free TLS certificate there. +Now that [Let's Encrypt](https://letsencrypt.org) is available, you may +wish to generate your free TLS certificate there. diff --git a/bot.go b/bot.go index 402b9ee..0a18e3f 100644 --- a/bot.go +++ b/bot.go @@ -243,23 +243,7 @@ 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) { - resp, err := bot.Request(c) - if err != nil { - return Message{}, err - } - - var message Message - err = json.Unmarshal(resp.Result, &message) - - return message, err -} - -// Request makes a request to Telegram that returns an APIResponse, rather than -// a Message. +// Request sends a Chattable to Telegram, and returns the APIResponse. func (bot *BotAPI) Request(c Chattable) (APIResponse, error) { switch t := c.(type) { case Fileable: @@ -288,6 +272,20 @@ func (bot *BotAPI) Request(c Chattable) (APIResponse, error) { } } +// 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 +} + // 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. diff --git a/helpers_test.go b/helpers_test.go index 9542f02..7cb5c0b 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,8 +1,9 @@ package tgbotapi_test import ( - "github.com/go-telegram-bot-api/telegram-bot-api" "testing" + + "github.com/go-telegram-bot-api/telegram-bot-api" ) func TestNewInlineQueryResultArticle(t *testing.T) { From 0a654beca49403b8eddc1e3046b537bf4dd76390 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 01:38:45 -0600 Subject: [PATCH 11/29] Move debug output into a single location. --- bot.go | 74 ++++++++++++++-------------------------------------------- 1 file changed, 18 insertions(+), 56 deletions(-) diff --git a/bot.go b/bot.go index 0a18e3f..7985fc7 100644 --- a/bot.go +++ b/bot.go @@ -7,7 +7,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "io/ioutil" "log" "net/http" @@ -60,6 +59,10 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) { // MakeRequest makes a request to a specific endpoint with our token. func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) { + if bot.Debug { + log.Printf("Endpoint: %s, values: %v\n", endpoint, params) + } + method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) resp, err := bot.Client.PostForm(method, params) @@ -68,45 +71,26 @@ func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, } defer resp.Body.Close() - var apiResp APIResponse - bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp) + bytes, err := ioutil.ReadAll(resp.Body) if err != nil { - return apiResp, err + return APIResponse{}, err } if bot.Debug { - log.Printf("%s resp: %s", endpoint, bytes) - } - - if !apiResp.Ok { - return apiResp, errors.New(apiResp.Description) + log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) } - 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) + var apiResp APIResponse + err = json.Unmarshal(bytes, &apiResp) if err != nil { - return + return APIResponse{}, err } - err = json.Unmarshal(data, resp) - if err != nil { - return + if !apiResp.Ok { + return apiResp, errors.New(apiResp.Description) } - return data, nil + return apiResp, nil } // UploadFile makes a request to the API with a file. @@ -166,6 +150,10 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna return APIResponse{}, errors.New(ErrBadFileType) } + if bot.Debug { + log.Printf("Endpoint: %s, fieldname: %s, params: %v, file: %T\n", endpoint, fieldname, params, file) + } + method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) req, err := http.NewRequest("POST", method, nil) @@ -187,7 +175,7 @@ func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldna } if bot.Debug { - log.Println(string(bytes)) + log.Printf("Endpoint: %s, response: %s\n", endpoint, string(bytes)) } var apiResp APIResponse @@ -231,8 +219,6 @@ func (bot *BotAPI) GetMe() (User, error) { var user User json.Unmarshal(resp.Result, &user) - bot.debugLog("getMe", nil, user) - return user, nil } @@ -286,16 +272,6 @@ func (bot *BotAPI) Send(c Chattable) (Message, error) { return message, err } -// 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) - } -} - // GetUserProfilePhotos gets a user's profile photos. // // It requires UserID. @@ -318,8 +294,6 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro var profilePhotos UserProfilePhotos json.Unmarshal(resp.Result, &profilePhotos) - bot.debugLog("GetUserProfilePhoto", v, profilePhotos) - return profilePhotos, nil } @@ -338,8 +312,6 @@ func (bot *BotAPI) GetFile(config FileConfig) (File, error) { var file File json.Unmarshal(resp.Result, &file) - bot.debugLog("GetFile", v, file) - return file, nil } @@ -370,8 +342,6 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { var updates []Update json.Unmarshal(resp.Result, &updates) - bot.debugLog("getUpdates", v, updates) - return updates, nil } @@ -450,8 +420,6 @@ func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { var chat Chat err = json.Unmarshal(resp.Result, &chat) - bot.debugLog("getChat", v, chat) - return chat, err } @@ -476,8 +444,6 @@ func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error var members []ChatMember err = json.Unmarshal(resp.Result, &members) - bot.debugLog("getChatAdministrators", v, members) - return members, err } @@ -499,8 +465,6 @@ func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { var count int err = json.Unmarshal(resp.Result, &count) - bot.debugLog("getChatMembersCount", v, count) - return count, err } @@ -523,8 +487,6 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) var member ChatMember err = json.Unmarshal(resp.Result, &member) - bot.debugLog("getChatMember", v, member) - return member, err } From 72f87b43e3794101f980c610fcadd3fcdcf73bc9 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 13:00:02 -0600 Subject: [PATCH 12/29] Fix backwards compatibility for Live Location. --- bot_test.go | 2 +- configs.go | 4 ++-- helpers.go | 20 +++++++++----------- helpers_test.go | 5 ++--- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/bot_test.go b/bot_test.go index 5aa6802..cb584dc 100644 --- a/bot_test.go +++ b/bot_test.go @@ -266,7 +266,7 @@ func TestSendWithContact(t *testing.T) { func TestSendWithLocation(t *testing.T) { bot, _ := getBot(t) - _, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40, 86400)) + _, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40)) if err != nil { t.Error(err) diff --git a/configs.go b/configs.go index 0670a9b..651dabc 100644 --- a/configs.go +++ b/configs.go @@ -600,8 +600,8 @@ func (config LocationConfig) method() string { // LocationConfig contains information about a SendLocation request. type EditMessageLiveLocationConfig struct { BaseEdit - Latitude float64 // required - Longitude float64 // required + Latitude float64 // required + Longitude float64 // required } // values returns a url.Values representation of EditMessageLiveLocationConfig. diff --git a/helpers.go b/helpers.go index 2c5d046..132d957 100644 --- a/helpers.go +++ b/helpers.go @@ -268,14 +268,13 @@ func NewContact(chatID int64, phoneNumber, firstName string) ContactConfig { // NewLocation shares your location. // // chatID is where to send it, latitude and longitude are coordinates. -func NewLocation(chatID int64, latitude float64, longitude float64, live_period int) LocationConfig { +func NewLocation(chatID int64, latitude float64, longitude float64) LocationConfig { return LocationConfig{ BaseChat: BaseChat{ ChatID: chatID, }, - Latitude: latitude, - Longitude: longitude, - LivePeriod: live_period, + Latitude: latitude, + Longitude: longitude, } } @@ -466,14 +465,13 @@ func NewInlineQueryResultDocument(id, url, title, mimeType string) InlineQueryRe } // NewInlineQueryResultLocation creates a new inline query location. -func NewInlineQueryResultLocation(id, title string, latitude, longitude float64, live_period int) InlineQueryResultLocation { +func NewInlineQueryResultLocation(id, title string, latitude, longitude float64) InlineQueryResultLocation { return InlineQueryResultLocation{ - Type: "location", - ID: id, - Title: title, - Latitude: latitude, - Longitude: longitude, - LivePeriod: live_period, + Type: "location", + ID: id, + Title: title, + Latitude: latitude, + Longitude: longitude, } } diff --git a/helpers_test.go b/helpers_test.go index d2a9027..7cb5c0b 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -127,14 +127,13 @@ func TestNewInlineQueryResultDocument(t *testing.T) { } func TestNewInlineQueryResultLocation(t *testing.T) { - result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50, 86400) + result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50) if result.Type != "location" || result.ID != "id" || result.Title != "name" || result.Latitude != 40 || - result.Longitude != 50 || - result.LivePeriod != 86400 { + result.Longitude != 50 { t.Fail() } } From e840fa3b0f728e74733458cfb8a62e673f6a4bfd Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 13:06:58 -0600 Subject: [PATCH 13/29] Finish Bot API 3.4. --- configs.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ types.go | 10 +++++----- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/configs.go b/configs.go index 651dabc..8c8dca2 100644 --- a/configs.go +++ b/configs.go @@ -1715,3 +1715,51 @@ func (config DeleteStickerConfig) values() (url.Values, error) { return v, nil } + +// 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) values() (url.Values, 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("sticker_set_name", config.StickerSetName) + + return v, 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) values() (url.Values, error) { + v := url.Values{} + + if config.SuperGroupUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.SuperGroupUsername) + } + + return v, nil +} diff --git a/types.go b/types.go index 73ec6b6..2fce3dd 100644 --- a/types.go +++ b/types.go @@ -658,12 +658,12 @@ type InlineQueryResultDocument struct { // 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 + Type string `json:"type"` // required + ID string `json:"id"` // required + Latitude float64 `json:"latitude"` // required + Longitude float64 `json:"longitude"` // required LivePeriod int `json:"live_period"` // optional - Title string `json:"title"` // required + Title string `json:"title"` // required ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` ThumbURL string `json:"thumb_url"` From bb07769ea9a507112da471c1df8f0d28eacaef31 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 13:22:53 -0600 Subject: [PATCH 14/29] Bot API 3.5. --- configs.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++--- types.go | 21 +++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/configs.go b/configs.go index 8c8dca2..2c579ca 100644 --- a/configs.go +++ b/configs.go @@ -1231,6 +1231,7 @@ type InvoiceConfig struct { StartParameter string // required Currency string // required Prices *[]LabeledPrice // required + ProviderData string PhotoURL string PhotoSize int PhotoWidth int @@ -1258,6 +1259,9 @@ func (config InvoiceConfig) values() (url.Values, error) { return v, err } v.Add("prices", string(data)) + if config.ProviderData != "" { + v.Add("provider_data", config.ProviderData) + } if config.PhotoURL != "" { v.Add("photo_url", config.PhotoURL) } @@ -1330,6 +1334,7 @@ func (config DeleteMessageConfig) values() (url.Values, error) { // PinChatMessageConfig contains information of a message in a chat to pin. type PinChatMessageConfig struct { ChatID int64 + ChannelUsername string MessageID int DisableNotification bool } @@ -1341,7 +1346,11 @@ func (config PinChatMessageConfig) method() string { func (config PinChatMessageConfig) values() (url.Values, error) { v := url.Values{} - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + 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)) v.Add("disable_notification", strconv.FormatBool(config.DisableNotification)) @@ -1350,7 +1359,8 @@ func (config PinChatMessageConfig) values() (url.Values, error) { // UnpinChatMessageConfig contains information of chat to unpin. type UnpinChatMessageConfig struct { - ChatID int64 + ChatID int64 + ChannelUsername string } func (config UnpinChatMessageConfig) method() string { @@ -1360,7 +1370,11 @@ func (config UnpinChatMessageConfig) method() string { func (config UnpinChatMessageConfig) values() (url.Values, error) { v := url.Values{} - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } return v, nil } @@ -1763,3 +1777,42 @@ func (config DeleteChatStickerSetConfig) values() (url.Values, error) { return v, 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) values() (url.Values, error) { + v := url.Values{} + + if config.ChannelUsername == "" { + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + } else { + v.Add("chat_id", config.ChannelUsername) + } + bytes, err := json.Marshal(config.Media) + if err != nil { + return v, err + } + v.Add("media", string(bytes)) + if config.DisableNotification { + v.Add("disable_notification", strconv.FormatBool(config.DisableNotification)) + } + if config.ReplyToMessageID != 0 { + v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID)) + } + + return v, nil +} diff --git a/types.go b/types.go index 2fce3dd..c8f75a2 100644 --- a/types.go +++ b/types.go @@ -798,3 +798,24 @@ type StickerSet struct { ContainsMasks bool `json:"contains_masks"` Stickers []Sticker `json:"stickers"` } + +// InputMediaPhoto is a photo to send as part of a media group. +// +// Telegram recommends to use a file_id instead of uploading. +type InputMediaPhoto struct { + Type string `json:"type"` + Media string `json:"media"` + Caption string `json:"caption"` +} + +// InputMediaVideo is a video to send as part of a media group. +// +// Telegram recommends to use a file_id instead of uploading. +type InputMediaVideo struct { + Type string `json:"type"` + Media string `json:"media"` + Caption string `json:"caption,omitempty"` + Width int `json:"width,omitempty"` + Height int `json:"height,omitempty"` + Duration int `json:"duration,omitempty"` +} From c268ddc5b9ba4010cd532007877c301b6d4482c4 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 17:06:33 -0600 Subject: [PATCH 15/29] Fix comments that were copied. --- configs.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/configs.go b/configs.go index 2c579ca..bfbf0ca 100644 --- a/configs.go +++ b/configs.go @@ -597,7 +597,7 @@ func (config LocationConfig) method() string { return "sendLocation" } -// LocationConfig contains information about a SendLocation request. +// EditMessageLiveLocationConfig allows you to update a live location. type EditMessageLiveLocationConfig struct { BaseEdit Latitude float64 // required @@ -622,19 +622,14 @@ func (config EditMessageLiveLocationConfig) method() string { return "editMessageLiveLocation" } -// LocationConfig contains information about a StopMessageLiveLocation request. +// StopMessageLiveLocationConfig stops updating a live location. type StopMessageLiveLocationConfig struct { BaseEdit } // values returns a url.Values representation of StopMessageLiveLocationConfig. func (config StopMessageLiveLocationConfig) values() (url.Values, error) { - v, err := config.BaseEdit.values() - if err != nil { - return v, err - } - - return v, nil + return config.BaseEdit.values() } // method returns Telegram API method name for stop message Live Location. From 9653a4aad4bed727a828034c0ed5c371aada90af Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 29 Dec 2017 21:55:58 -0600 Subject: [PATCH 16/29] Handle some ignored errors. --- bot.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/bot.go b/bot.go index 7985fc7..63fb3e0 100644 --- a/bot.go +++ b/bot.go @@ -217,9 +217,9 @@ func (bot *BotAPI) GetMe() (User, error) { } var user User - json.Unmarshal(resp.Result, &user) + err = json.Unmarshal(resp.Result, &user) - return user, nil + return user, err } // IsMessageToMe returns true if message directed to this bot. @@ -292,9 +292,9 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro } var profilePhotos UserProfilePhotos - json.Unmarshal(resp.Result, &profilePhotos) + err = json.Unmarshal(resp.Result, &profilePhotos) - return profilePhotos, nil + return profilePhotos, err } // GetFile returns a File which can download a file from Telegram. @@ -310,9 +310,9 @@ func (bot *BotAPI) GetFile(config FileConfig) (File, error) { } var file File - json.Unmarshal(resp.Result, &file) + err = json.Unmarshal(resp.Result, &file) - return file, nil + return file, err } // GetUpdates fetches updates. @@ -340,9 +340,9 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { } var updates []Update - json.Unmarshal(resp.Result, &updates) + err = json.Unmarshal(resp.Result, &updates) - return updates, nil + return updates, err } // GetWebhookInfo allows you to fetch information about a webhook and if From 1aef8c8c45f5d2ab2c7b556d5b2445ac35daf48f Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 26 Mar 2018 12:30:16 -0500 Subject: [PATCH 17/29] Remove new methods that returned APIResponse. --- bot.go | 42 ------------------------------------------ helpers.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/bot.go b/bot.go index fde5b50..3599c86 100644 --- a/bot.go +++ b/bot.go @@ -572,45 +572,3 @@ func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) return stickers, err } - -// SetChatTitle change title of chat. -func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) { - v, err := config.values() - if err != nil { - return APIResponse{}, err - } - - 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 - } - - 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 - } - - return bot.MakeRequest(config.method(), v) -} diff --git a/helpers.go b/helpers.go index c23a3bf..41c43b2 100644 --- a/helpers.go +++ b/helpers.go @@ -684,3 +684,38 @@ func NewSetChatPhotoShare(chatID int64, fileID string) 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 interface{}) SetChatPhotoConfig { + return SetChatPhotoConfig{ + BaseFile: BaseFile{ + BaseChat: BaseChat{ + ChatID: chatID, + }, + File: photo, + }, + } +} + +// NewDeleteChatPhoto allows you to delete the photo for a chat. +func NewDeleteChatPhoto(chatID int64, photo interface{}) DeleteChatPhotoConfig { + return DeleteChatPhotoConfig{ + ChatID: chatID, + } +} From b728fa78fc94abdd39b0f24bf71dc1d245385059 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 26 Mar 2018 12:39:07 -0500 Subject: [PATCH 18/29] More various small code quality improvements. --- bot_test.go | 9 ++++++--- configs.go | 10 +++++----- types_test.go | 12 ++++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/bot_test.go b/bot_test.go index 1727396..0ca1bb2 100644 --- a/bot_test.go +++ b/bot_test.go @@ -682,14 +682,17 @@ func TestUnpinChatMessage(t *testing.T) { MessageID: message.MessageID, DisableNotification: false, } - _, err := bot.Request(pinChatMessageConfig) + + if _, err := bot.Request(pinChatMessageConfig); err != nil { + t.Error(err) + t.Fail() + } unpinChatMessageConfig := tgbotapi.UnpinChatMessageConfig{ ChatID: message.Chat.ID, } - _, err = bot.Request(unpinChatMessageConfig) - if err != nil { + if _, err := bot.Request(unpinChatMessageConfig); err != nil { t.Error(err) t.Fail() } diff --git a/configs.go b/configs.go index e8c4514..0f9bd10 100644 --- a/configs.go +++ b/configs.go @@ -1269,19 +1269,19 @@ func (config InvoiceConfig) values() (url.Values, error) { if config.PhotoHeight != 0 { v.Add("photo_height", strconv.Itoa(config.PhotoHeight)) } - if config.NeedName != false { + if config.NeedName { v.Add("need_name", strconv.FormatBool(config.NeedName)) } - if config.NeedPhoneNumber != false { + if config.NeedPhoneNumber { v.Add("need_phone_number", strconv.FormatBool(config.NeedPhoneNumber)) } - if config.NeedEmail != false { + if config.NeedEmail { v.Add("need_email", strconv.FormatBool(config.NeedEmail)) } - if config.NeedShippingAddress != false { + if config.NeedShippingAddress { v.Add("need_shipping_address", strconv.FormatBool(config.NeedShippingAddress)) } - if config.IsFlexible != false { + if config.IsFlexible { v.Add("is_flexible", strconv.FormatBool(config.IsFlexible)) } diff --git a/types_test.go b/types_test.go index bb7bb64..8a67c8e 100644 --- a/types_test.go +++ b/types_test.go @@ -49,7 +49,7 @@ func TestMessageIsCommandWithCommand(t *testing.T) { message := tgbotapi.Message{Text: "/command"} message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} - if message.IsCommand() != true { + if !message.IsCommand() { t.Fail() } } @@ -57,7 +57,7 @@ func TestMessageIsCommandWithCommand(t *testing.T) { func TestIsCommandWithText(t *testing.T) { message := tgbotapi.Message{Text: "some text"} - if message.IsCommand() != false { + if message.IsCommand() { t.Fail() } } @@ -65,7 +65,7 @@ func TestIsCommandWithText(t *testing.T) { func TestIsCommandWithEmptyText(t *testing.T) { message := tgbotapi.Message{Text: ""} - if message.IsCommand() != false { + if message.IsCommand() { t.Fail() } } @@ -162,7 +162,7 @@ func TestMessageEntityParseURLBad(t *testing.T) { func TestChatIsPrivate(t *testing.T) { chat := tgbotapi.Chat{ID: 10, Type: "private"} - if chat.IsPrivate() != true { + if !chat.IsPrivate() { t.Fail() } } @@ -170,7 +170,7 @@ func TestChatIsPrivate(t *testing.T) { func TestChatIsGroup(t *testing.T) { chat := tgbotapi.Chat{ID: 10, Type: "group"} - if chat.IsGroup() != true { + if !chat.IsGroup() { t.Fail() } } @@ -178,7 +178,7 @@ func TestChatIsGroup(t *testing.T) { func TestChatIsChannel(t *testing.T) { chat := tgbotapi.Chat{ID: 10, Type: "channel"} - if chat.IsChannel() != true { + if !chat.IsChannel() { t.Fail() } } From 03815bf5bd2b255ccbca2e4beefb563a65ce945e Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 8 Oct 2018 18:21:29 -0500 Subject: [PATCH 19/29] Unify params and values, introduce helpers to deal with fields. --- bot.go | 219 ++++------ configs.go | 1091 +++++++++++++++---------------------------------- params.go | 100 +++++ passport.go | 2 +- types_test.go | 41 ++ 5 files changed, 548 insertions(+), 905 deletions(-) create mode 100644 params.go diff --git a/bot.go b/bot.go index 5c1b6db..c01bb82 100644 --- a/bot.go +++ b/bot.go @@ -12,7 +12,6 @@ import ( "net/http" "net/url" "os" - "strconv" "strings" "time" @@ -59,15 +58,31 @@ func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) { return bot, nil } +func buildParams(in Params) (out url.Values) { + if in == nil { + return url.Values{} + } + + out = url.Values{} + + for key, value := range in { + out.Set(key, value) + } + + return +} + // MakeRequest makes a request to a specific endpoint with our token. -func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) { +func (bot *BotAPI) MakeRequest(endpoint string, params Params) (APIResponse, error) { if bot.Debug { - log.Printf("Endpoint: %s, values: %v\n", endpoint, params) + log.Printf("Endpoint: %s, params: %v\n", endpoint, params) } method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint) - resp, err := bot.Client.PostForm(method, params) + values := buildParams(params) + + resp, err := bot.Client.PostForm(method, values) if err != nil { return APIResponse{}, err } @@ -131,7 +146,7 @@ func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) // // Note that if your FileReader has a size set to -1, it will read // the file into memory to calculate a size. -func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) { +func (bot *BotAPI) UploadFile(endpoint string, params Params, fieldname string, file interface{}) (APIResponse, error) { ms := multipartstreamer.New() switch f := file.(type) { @@ -261,30 +276,20 @@ func (bot *BotAPI) IsMessageToMe(message Message) bool { // 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 APIResponse{}, err + } + switch t := c.(type) { case Fileable: if t.useExistingFile() { - v, err := t.values() - if err != nil { - return APIResponse{}, err - } - - return bot.MakeRequest(t.method(), v) + return bot.MakeRequest(t.method(), params) } - p, err := t.params() - if err != nil { - return APIResponse{}, err - } - - return bot.UploadFile(t.method(), p, t.name(), t.getFile()) + return bot.UploadFile(t.method(), params, t.name(), t.getFile()) default: - v, err := c.values() - if err != nil { - return APIResponse{}, err - } - - return bot.MakeRequest(c.method(), v) + return bot.MakeRequest(c.method(), params) } } @@ -307,16 +312,9 @@ func (bot *BotAPI) Send(c Chattable) (Message, error) { // 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)) - } + params, _ := config.params() - resp, err := bot.MakeRequest("getUserProfilePhotos", v) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return UserProfilePhotos{}, err } @@ -331,8 +329,9 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro // // Requires FileID. func (bot *BotAPI) GetFile(config FileConfig) (File, error) { - v := url.Values{} - v.Add("file_id", config.FileID) + v := make(Params) + + v["file_id"] = config.FileID resp, err := bot.MakeRequest("getFile", v) if err != nil { @@ -353,18 +352,9 @@ func (bot *BotAPI) GetFile(config FileConfig) (File, error) { // 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)) - } + params, _ := config.params() - resp, err := bot.MakeRequest("getUpdates", v) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return []Update{}, err } @@ -378,7 +368,7 @@ func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) { // 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{}) + resp, err := bot.MakeRequest("getWebhookInfo", nil) if err != nil { return WebhookInfo{}, err } @@ -448,13 +438,9 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { // GetChat gets information about a chat. func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { - v := url.Values{} + v := make(Params) - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) resp, err := bot.MakeRequest("getChat", v) if err != nil { @@ -472,13 +458,9 @@ func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { // 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{} + v := make(Params) - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) resp, err := bot.MakeRequest("getChatAdministrators", v) if err != nil { @@ -493,13 +475,9 @@ func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error // GetChatMembersCount gets the number of users in a chat. func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { - v := url.Values{} + v := make(Params) - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) resp, err := bot.MakeRequest("getChatMembersCount", v) if err != nil { @@ -514,14 +492,10 @@ func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { // GetChatMember gets a specific chat member. func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) { - v := url.Values{} + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + v.AddNonZero("user_id", config.UserID) resp, err := bot.MakeRequest("getChatMember", v) if err != nil { @@ -537,16 +511,10 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) // 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{} + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + v.AddNonZero("user_id", config.UserID) return bot.MakeRequest("unbanChatMember", v) } @@ -556,80 +524,45 @@ func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) //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{} + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + v.AddNonZero("user_id", 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)) - } + v.AddNonNilBool("can_send_messages", config.CanSendMessages) + v.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) + v.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) + v.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) + v.AddNonZero64("until_date", config.UntilDate) return bot.MakeRequest("restrictChatMember", v) } // PromoteChatMember add admin rights to user func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) { - v := url.Values{} + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + v.AddNonZero("user_id", 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)) - } + v.AddNonNilBool("can_change_info", config.CanChangeInfo) + v.AddNonNilBool("can_post_messages", config.CanPostMessages) + v.AddNonNilBool("can_edit_messages", config.CanEditMessages) + v.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) + v.AddNonNilBool("can_invite_members", config.CanInviteUsers) + v.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) + v.AddNonNilBool("can_pin_messages", config.CanPinMessages) + v.AddNonNilBool("can_promote_members", config.CanPromoteMembers) 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() + v, err := config.params() + if err != nil { + return nil, err + } resp, err := bot.MakeRequest(config.method(), v) if err != nil { @@ -644,13 +577,9 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh // GetInviteLink get InviteLink for a chat func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { - v := url.Values{} + v := make(Params) - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) resp, err := bot.MakeRequest("exportChatInviteLink", v) if err != nil { @@ -665,7 +594,7 @@ func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { // GetStickerSet returns a StickerSet. func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { - v, err := config.values() + v, err := config.params() if err != nil { return StickerSet{}, nil } diff --git a/configs.go b/configs.go index d620d0d..62c90cd 100644 --- a/configs.go +++ b/configs.go @@ -1,10 +1,8 @@ package tgbotapi import ( - "encoding/json" "io" "net/url" - "strconv" ) // Telegram constants @@ -49,14 +47,13 @@ const ( // Chattable is any config type that can be sent. type Chattable interface { - values() (url.Values, error) + params() (Params, 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 @@ -71,31 +68,17 @@ type BaseChat struct { 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)) - } +// params returns Params representation of BaseChat +func (chat *BaseChat) params() (Params, error) { + v := make(Params) - if chat.ReplyToMessageID != 0 { - v.Add("reply_to_message_id", strconv.Itoa(chat.ReplyToMessageID)) - } + v.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) + v.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) + v.AddBool("disable_notification", chat.DisableNotification) - if chat.ReplyMarkup != nil { - data, err := json.Marshal(chat.ReplyMarkup) - if err != nil { - return v, err - } - - v.Add("reply_markup", string(data)) - } + err := v.AddInterface("reply_markup", chat.ReplyMarkup) - v.Add("disable_notification", strconv.FormatBool(chat.DisableNotification)) - - return v, nil + return v, err } // BaseFile is a base type for all file config types. @@ -108,40 +91,14 @@ type BaseFile struct { 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 returns a Params representation of BaseFile. +func (file BaseFile) params() (Params, error) { + params, err := file.BaseChat.params() - params["disable_notification"] = strconv.FormatBool(file.DisableNotification) + params.AddNonEmpty("mime_type", file.MimeType) + params.AddNonZero("file_size", file.FileSize) - return params, nil + return params, err } // getFile returns the file. @@ -163,29 +120,19 @@ type BaseEdit struct { ReplyMarkup *InlineKeyboardMarkup } -func (edit BaseEdit) values() (url.Values, error) { - v := url.Values{} +func (edit BaseEdit) params() (Params, error) { + v := make(Params) - 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)) + if edit.InlineMessageID != "" { + v["inline_message_id"] = edit.InlineMessageID } else { - v.Add("inline_message_id", edit.InlineMessageID) + v.AddFirstValid("chat_id", edit.ChatID, edit.ChannelUsername) + v.AddNonZero("message_id", edit.MessageID) } - if edit.ReplyMarkup != nil { - data, err := json.Marshal(edit.ReplyMarkup) - if err != nil { - return v, err - } - v.Add("reply_markup", string(data)) - } + err := v.AddInterface("reply_markup", edit.ReplyMarkup) - return v, nil + return v, err } // MessageConfig contains information about a SendMessage request. @@ -197,16 +144,15 @@ type MessageConfig struct { } // values returns a url.Values representation of MessageConfig. -func (config MessageConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() +func (config MessageConfig) params() (Params, error) { + v, err := config.BaseChat.params() 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) - } + + v.AddNonEmpty("text", config.Text) + v.AddBool("disable_web_page_preview", config.DisableWebPagePreview) + v.AddNonEmpty("parse_mode", config.ParseMode) return v, nil } @@ -225,13 +171,15 @@ type ForwardConfig struct { } // values returns a url.Values representation of ForwardConfig. -func (config ForwardConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() +func (config ForwardConfig) params() (Params, error) { + v, err := config.BaseChat.params() if err != nil { return v, err } - v.Add("from_chat_id", strconv.FormatInt(config.FromChatID, 10)) - v.Add("message_id", strconv.Itoa(config.MessageID)) + + v.AddNonZero64("from_chat_id", config.FromChatID) + v.AddNonZero("message_id", config.MessageID) + return v, nil } @@ -248,35 +196,14 @@ type PhotoConfig struct { } // 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 - } +func (config PhotoConfig) params() (Params, error) { + params, err := config.BaseFile.params() - v.Add(config.name(), config.FileID) - if config.Caption != "" { - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } - } + params.AddNonEmpty(config.name(), config.FileID) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) - return v, nil + return params, err } // name returns the field name for the Photo. @@ -300,57 +227,22 @@ type AudioConfig struct { } // values returns a url.Values representation of AudioConfig. -func (config AudioConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() +func (config AudioConfig) params() (Params, error) { + v, err := config.BaseChat.params() 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) - } - } + v.AddNonEmpty(config.name(), config.FileID) + v.AddNonZero("duration", config.Duration) + v.AddNonEmpty("performer", config.Performer) + v.AddNonEmpty("title", config.Title) + v.AddNonEmpty("caption", config.Caption) + v.AddNonEmpty("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" @@ -368,36 +260,15 @@ type DocumentConfig struct { 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 - } - } +func (config DocumentConfig) params() (Params, error) { + params, err := config.BaseFile.params() - return params, nil + params.AddNonEmpty(config.name(), config.FileID) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + + return params, err } // name returns the field name for the Document. @@ -416,22 +287,12 @@ type StickerConfig struct { } // 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 -} +func (config StickerConfig) params() (Params, error) { + v, err := config.BaseChat.params() -// params returns a map[string]string representation of StickerConfig. -func (config StickerConfig) params() (map[string]string, error) { - params, _ := config.BaseFile.params() + v.AddNonEmpty(config.name(), config.FileID) - return params, nil + return v, err } // name returns the field name for the Sticker. @@ -453,38 +314,15 @@ type VideoConfig struct { } // 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 - } +func (config VideoConfig) params() (Params, error) { + v, err := config.BaseChat.params() - 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) - } - } + v.AddNonEmpty(config.name(), config.FileID) + v.AddNonZero("duration", config.Duration) + v.AddNonEmpty("caption", config.Caption) + v.AddNonEmpty("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 + return v, err } // name returns the field name for the Video. @@ -505,39 +343,16 @@ type AnimationConfig struct { 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() +// values returns a Params representation of AnimationConfig. +func (config AnimationConfig) params() (Params, error) { + v, err := config.BaseChat.params() - if config.Caption != "" { - params["caption"] = config.Caption - if config.ParseMode != "" { - params["parse_mode"] = config.ParseMode - } - } + v.AddNonEmpty(config.name(), config.FileID) + v.AddNonZero("duration", config.Duration) + v.AddNonEmpty("caption", config.Caption) + v.AddNonEmpty("parse_mode", config.ParseMode) - return params, nil + return v, err } // name returns the field name for the Animation. @@ -558,37 +373,14 @@ type VideoNoteConfig struct { } // 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)) - } +func (config VideoNoteConfig) params() (Params, error) { + v, err := config.BaseChat.params() - return v, nil -} - -// params returns a map[string]string representation of VideoNoteConfig. -func (config VideoNoteConfig) params() (map[string]string, error) { - params, _ := config.BaseFile.params() + v.AddNonEmpty(config.name(), config.FileID) + v.AddNonZero("duration", config.Duration) + v.AddNonZero("length", config.Length) - if config.Length != 0 { - params["length"] = strconv.Itoa(config.Length) - } - if config.Duration != 0 { - params["duration"] = strconv.Itoa(config.Duration) - } - - return params, nil + return v, err } // name returns the field name for the VideoNote. @@ -610,41 +402,15 @@ type VoiceConfig struct { } // 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() +func (config VoiceConfig) params() (Params, error) { + v, err := config.BaseChat.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 - } - } + v.AddNonEmpty(config.name(), config.FileID) + v.AddNonZero("duration", config.Duration) + v.AddNonEmpty("caption", config.Caption) + v.AddNonEmpty("parse_mode", config.ParseMode) - return params, nil + return v, err } // name returns the field name for the Voice. @@ -666,19 +432,14 @@ type LocationConfig struct { } // 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 - } +func (config LocationConfig) params() (Params, error) { + v, err := config.BaseChat.params() - v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64)) - v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64)) - if config.LivePeriod != 0 { - v.Add("live_period", strconv.Itoa(config.LivePeriod)) - } + v.AddNonZeroFloat("latitude", config.Latitude) + v.AddNonZeroFloat("longitude", config.Longitude) + v.AddNonZero("live_period", config.LivePeriod) - return v, nil + return v, err } // method returns Telegram API method name for sending Location. @@ -694,16 +455,13 @@ type EditMessageLiveLocationConfig struct { } // values returns a url.Values representation of EditMessageLiveLocationConfig. -func (config EditMessageLiveLocationConfig) values() (url.Values, error) { - v, err := config.BaseEdit.values() - if err != nil { - return v, err - } +func (config EditMessageLiveLocationConfig) params() (Params, error) { + v, err := config.BaseEdit.params() - v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64)) - v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64)) + v.AddNonZeroFloat("latitude", config.Latitude) + v.AddNonZeroFloat("longitude", config.Longitude) - return v, nil + return v, err } // method returns Telegram API method name for edit message Live Location. @@ -717,8 +475,8 @@ type StopMessageLiveLocationConfig struct { } // values returns a url.Values representation of StopMessageLiveLocationConfig. -func (config StopMessageLiveLocationConfig) values() (url.Values, error) { - return config.BaseEdit.values() +func (config StopMessageLiveLocationConfig) params() (Params, error) { + return config.BaseEdit.params() } // method returns Telegram API method name for stop message Live Location. @@ -736,21 +494,16 @@ type VenueConfig struct { FoursquareID string } -func (config VenueConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } +func (config VenueConfig) params() (Params, error) { + v, err := config.BaseChat.params() - 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) - } + v.AddNonZeroFloat("latitude", config.Latitude) + v.AddNonZeroFloat("longitude", config.Longitude) + v["title"] = config.Title + v["address"] = config.Address + v.AddNonEmpty("foursquare_id", config.FoursquareID) - return v, nil + return v, err } func (config VenueConfig) method() string { @@ -765,17 +518,14 @@ type ContactConfig struct { LastName string } -func (config ContactConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } +func (config ContactConfig) params() (Params, error) { + v, err := config.BaseChat.params() - v.Add("phone_number", config.PhoneNumber) - v.Add("first_name", config.FirstName) - v.Add("last_name", config.LastName) + v["phone_number"] = config.PhoneNumber + v["first_name"] = config.FirstName + v["last_name"] = config.LastName - return v, nil + return v, err } func (config ContactConfig) method() string { @@ -788,15 +538,12 @@ type GameConfig struct { GameShortName string } -func (config GameConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() - if err != nil { - return v, err - } +func (config GameConfig) params() (Params, error) { + v, err := config.BaseChat.params() - v.Add("game_short_name", config.GameShortName) + v["game_short_name"] = config.GameShortName - return v, nil + return v, err } func (config GameConfig) method() string { @@ -815,22 +562,19 @@ type SetGameScoreConfig struct { InlineMessageID string } -func (config SetGameScoreConfig) values() (url.Values, error) { - v := url.Values{} +func (config SetGameScoreConfig) params() (Params, error) { + v := make(Params) + + v.AddNonZero("user_id", config.UserID) + v.AddNonZero("scrore", config.Score) + v.AddBool("disable_edit_message", config.DisableEditMessage) - 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)) + if config.InlineMessageID != "" { + v["inline_message_id"] = config.InlineMessageID } else { - v.Add("inline_message_id", config.InlineMessageID) + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + v.AddNonZero("message_id", config.MessageID) } - v.Add("disable_edit_message", strconv.FormatBool(config.DisableEditMessage)) return v, nil } @@ -848,19 +592,16 @@ type GetGameHighScoresConfig struct { InlineMessageID string } -func (config GetGameHighScoresConfig) values() (url.Values, error) { - v := url.Values{} +func (config GetGameHighScoresConfig) params() (Params, error) { + v := make(Params) - 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)) + v.AddNonZero("user_id", config.UserID) + + if config.InlineMessageID != "" { + v["inline_message_id"] = config.InlineMessageID } else { - v.Add("inline_message_id", config.InlineMessageID) + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + v.AddNonZero("message_id", config.MessageID) } return v, nil @@ -877,13 +618,12 @@ type ChatActionConfig struct { } // 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 +func (config ChatActionConfig) params() (Params, error) { + v, err := config.BaseChat.params() + + v["action"] = config.Action + + return v, err } // method returns Telegram API method name for sending ChatAction. @@ -899,17 +639,14 @@ type EditMessageTextConfig struct { DisableWebPagePreview bool } -func (config EditMessageTextConfig) values() (url.Values, error) { - v, err := config.BaseEdit.values() - if err != nil { - return v, err - } +func (config EditMessageTextConfig) params() (Params, error) { + v, err := config.BaseEdit.params() - v.Add("text", config.Text) - v.Add("parse_mode", config.ParseMode) - v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview)) + v["text"] = config.Text + v.AddNonEmpty("parse_mode", config.ParseMode) + v.AddBool("disable_web_page_preview", config.DisableWebPagePreview) - return v, nil + return v, err } func (config EditMessageTextConfig) method() string { @@ -923,15 +660,13 @@ type EditMessageCaptionConfig struct { ParseMode string } -func (config EditMessageCaptionConfig) values() (url.Values, error) { - v, _ := config.BaseEdit.values() +func (config EditMessageCaptionConfig) params() (Params, error) { + v, err := config.BaseEdit.params() - v.Add("caption", config.Caption) - if config.ParseMode != "" { - v.Add("parse_mode", config.ParseMode) - } + v["caption"] = config.Caption + v.AddNonEmpty("parse_mode", config.ParseMode) - return v, nil + return v, err } func (config EditMessageCaptionConfig) method() string { @@ -944,8 +679,8 @@ type EditMessageReplyMarkupConfig struct { BaseEdit } -func (config EditMessageReplyMarkupConfig) values() (url.Values, error) { - return config.BaseEdit.values() +func (config EditMessageReplyMarkupConfig) params() (Params, error) { + return config.BaseEdit.params() } func (config EditMessageReplyMarkupConfig) method() string { @@ -960,6 +695,20 @@ type UserProfilePhotosConfig struct { Limit int } +func (UserProfilePhotosConfig) method() string { + return "getUserProfilePhotos" +} + +func (config UserProfilePhotosConfig) params() (Params, error) { + params := make(Params) + + params.AddNonZero("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 @@ -972,6 +721,20 @@ type UpdateConfig struct { Timeout int } +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) + + return params, nil +} + // WebhookConfig contains information about a SetWebhook request. type WebhookConfig struct { URL *url.URL @@ -983,28 +746,14 @@ func (config WebhookConfig) method() string { return "setWebhook" } -func (config WebhookConfig) values() (url.Values, error) { - v := url.Values{} - - if config.URL != nil { - v.Add("url", config.URL.String()) - } - if config.MaxConnections != 0 { - v.Add("max_connections", strconv.Itoa(config.MaxConnections)) - } - - return v, nil -} - -func (config WebhookConfig) params() (map[string]string, error) { - params := make(map[string]string) +func (config WebhookConfig) params() (Params, error) { + params := make(Params) if config.URL != nil { params["url"] = config.URL.String() } - if config.MaxConnections != 0 { - params["max_connections"] = strconv.Itoa(config.MaxConnections) - } + + params.AddNonZero("max_connections", config.MaxConnections) return params, nil } @@ -1029,8 +778,8 @@ func (config RemoveWebhookConfig) method() string { return "setWebhook" } -func (config RemoveWebhookConfig) values() (url.Values, error) { - return url.Values{}, nil +func (config RemoveWebhookConfig) params() (Params, error) { + return nil, nil } // FileBytes contains information about a set of bytes to upload @@ -1064,20 +813,19 @@ func (config InlineConfig) method() string { return "answerInlineQuery" } -func (config InlineConfig) values() (url.Values, error) { - v := url.Values{} +func (config InlineConfig) params() (Params, error) { + v := make(Params) - 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 { + v["inline_query_id"] = config.InlineQueryID + v.AddNonZero("cache_time", config.CacheTime) + v.AddBool("is_personal", config.IsPersonal) + v.AddNonEmpty("next_offset", config.NextOffset) + v.AddNonEmpty("switch_pm_text", config.SwitchPMText) + v.AddNonEmpty("switch_pm_parameter", config.SwitchPMParameter) + + if err := v.AddInterface("results", config.Results); err != nil { return v, err } - v.Add("results", string(data)) - v.Add("switch_pm_text", config.SwitchPMText) - v.Add("switch_pm_parameter", config.SwitchPMParameter) return v, nil } @@ -1095,18 +843,14 @@ func (config CallbackConfig) method() string { return "answerCallbackQuery" } -func (config CallbackConfig) values() (url.Values, error) { - v := url.Values{} +func (config CallbackConfig) params() (Params, error) { + v := make(Params) - 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)) + v["callback_query_id"] = config.CallbackQueryID + v.AddNonEmpty("text", config.Text) + v.AddBool("show_alert", config.ShowAlert) + v.AddNonEmpty("url", config.URL) + v.AddNonZero("cache_time", config.CacheTime) return v, nil } @@ -1129,17 +873,11 @@ func (config UnbanChatMemberConfig) method() string { return "unbanChatMember" } -func (config UnbanChatMemberConfig) values() (url.Values, error) { - v := url.Values{} +func (config UnbanChatMemberConfig) params() (Params, error) { + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + v.AddNonZero("user_id", config.UserID) return v, nil } @@ -1154,19 +892,12 @@ func (config KickChatMemberConfig) method() string { return "kickChatMember" } -func (config KickChatMemberConfig) values() (url.Values, error) { - v := url.Values{} +func (config KickChatMemberConfig) params() (Params, error) { + v := make(Params) - 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)) - } + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + v.AddNonZero("user_id", config.UserID) + v.AddNonZero64("until_date", config.UntilDate) return v, nil } @@ -1185,33 +916,17 @@ func (config RestrictChatMemberConfig) method() string { return "restrictChatMember" } -func (config RestrictChatMemberConfig) values() (url.Values, error) { - v := url.Values{} +func (config RestrictChatMemberConfig) params() (Params, error) { + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + v.AddNonZero("user_id", 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)) - } + v.AddNonNilBool("can_send_messages", config.CanSendMessages) + v.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) + v.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) + v.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) + v.AddNonZero64("until_date", config.UntilDate) return v, nil } @@ -1233,42 +948,20 @@ func (config PromoteChatMemberConfig) method() string { return "promoteChatMember" } -func (config PromoteChatMemberConfig) values() (url.Values, error) { - v := url.Values{} +func (config PromoteChatMemberConfig) params() (Params, error) { + v := make(Params) - 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)) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + v.AddNonZero("user_id", 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)) - } + v.AddNonNilBool("can_change_info", config.CanChangeInfo) + v.AddNonNilBool("can_post_messages", config.CanPostMessages) + v.AddNonNilBool("can_edit_messages", config.CanEditMessages) + v.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) + v.AddNonNilBool("can_invite_users", config.CanInviteUsers) + v.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) + v.AddNonNilBool("can_pin_messages", config.CanPinMessages) + v.AddNonNilBool("can_promote_members", config.CanPromoteMembers) return v, nil } @@ -1289,14 +982,10 @@ func (config LeaveChatConfig) method() string { return "leaveChat" } -func (config LeaveChatConfig) values() (url.Values, error) { - v := url.Values{} +func (config LeaveChatConfig) params() (Params, error) { + v := make(Params) - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) return v, nil } @@ -1331,52 +1020,33 @@ type InvoiceConfig struct { IsFlexible bool } -func (config InvoiceConfig) values() (url.Values, error) { - v, err := config.BaseChat.values() +func (config InvoiceConfig) params() (Params, error) { + v, err := config.BaseChat.params() 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 { + + v["title"] = config.Title + v["description"] = config.Description + v["payload"] = config.Payload + v["provider_token"] = config.ProviderToken + v["start_parameter"] = config.StartParameter + v["currency"] = config.Currency + + if err = v.AddInterface("prices", config.Prices); err != nil { return v, err } - v.Add("prices", string(data)) - if config.ProviderData != "" { - v.Add("provider_data", config.ProviderData) - } - 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 { - v.Add("need_name", strconv.FormatBool(config.NeedName)) - } - if config.NeedPhoneNumber { - v.Add("need_phone_number", strconv.FormatBool(config.NeedPhoneNumber)) - } - if config.NeedEmail { - v.Add("need_email", strconv.FormatBool(config.NeedEmail)) - } - if config.NeedShippingAddress { - v.Add("need_shipping_address", strconv.FormatBool(config.NeedShippingAddress)) - } - if config.IsFlexible { - v.Add("is_flexible", strconv.FormatBool(config.IsFlexible)) - } + + v.AddNonEmpty("provider_data", config.ProviderData) + v.AddNonEmpty("photo_url", config.PhotoURL) + v.AddNonZero("photo_size", config.PhotoSize) + v.AddNonZero("photo_width", config.PhotoWidth) + v.AddNonZero("photo_height", config.PhotoHeight) + v.AddBool("need_name", config.NeedName) + v.AddBool("need_phone_number", config.NeedPhoneNumber) + v.AddBool("need_email", config.NeedEmail) + v.AddBool("need_shipping_address", config.NeedShippingAddress) + v.AddBool("is_flexible", config.IsFlexible) return v, nil } @@ -1410,11 +1080,11 @@ func (config DeleteMessageConfig) method() string { return "deleteMessage" } -func (config DeleteMessageConfig) values() (url.Values, error) { - v := url.Values{} +func (config DeleteMessageConfig) params() (Params, error) { + v := make(Params) - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - v.Add("message_id", strconv.Itoa(config.MessageID)) + v.AddNonZero64("chat_id", config.ChatID) + v.AddNonZero("message_id", config.MessageID) return v, nil } @@ -1431,16 +1101,12 @@ func (config PinChatMessageConfig) method() string { return "pinChatMessage" } -func (config PinChatMessageConfig) values() (url.Values, error) { - v := url.Values{} +func (config PinChatMessageConfig) params() (Params, error) { + v := make(Params) - 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)) - v.Add("disable_notification", strconv.FormatBool(config.DisableNotification)) + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + v.AddNonZero("message_id", config.MessageID) + v.AddBool("disable_notification", config.DisableNotification) return v, nil } @@ -1455,14 +1121,10 @@ func (config UnpinChatMessageConfig) method() string { return "unpinChatMessage" } -func (config UnpinChatMessageConfig) values() (url.Values, error) { - v := url.Values{} +func (config UnpinChatMessageConfig) params() (Params, error) { + v := make(Params) - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) return v, nil } @@ -1498,14 +1160,10 @@ func (config DeleteChatPhotoConfig) method() string { return "deleteChatPhoto" } -func (config DeleteChatPhotoConfig) values() (url.Values, error) { - v := url.Values{} +func (config DeleteChatPhotoConfig) params() (Params, error) { + v := make(Params) - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) return v, nil } @@ -1522,16 +1180,11 @@ func (config SetChatTitleConfig) method() string { return "setChatTitle" } -func (config SetChatTitleConfig) values() (url.Values, error) { - v := url.Values{} - - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } +func (config SetChatTitleConfig) params() (Params, error) { + v := make(Params) - v.Add("title", config.Title) + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + v["title"] = config.Title return v, nil } @@ -1548,16 +1201,11 @@ func (config SetChatDescriptionConfig) method() string { return "setChatDescription" } -func (config SetChatDescriptionConfig) values() (url.Values, error) { - v := url.Values{} +func (config SetChatDescriptionConfig) params() (Params, error) { + v := make(Params) - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } - - v.Add("description", config.Description) + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + v["description"] = config.Description return v, nil } @@ -1571,10 +1219,10 @@ func (config GetStickerSetConfig) method() string { return "getStickerSet" } -func (config GetStickerSetConfig) values() (url.Values, error) { - v := url.Values{} +func (config GetStickerSetConfig) params() (Params, error) { + v := make(Params) - v.Add("name", config.Name) + v["name"] = config.Name return v, nil } @@ -1589,22 +1237,14 @@ func (config UploadStickerConfig) method() string { return "uploadStickerFile" } -func (config UploadStickerConfig) values() (url.Values, error) { - v := url.Values{} +func (config UploadStickerConfig) params() (Params, error) { + v := make(Params) - v.Add("user_id", strconv.FormatInt(config.UserID, 10)) + v.AddNonZero64("user_id", config.UserID) return v, nil } -func (config UploadStickerConfig) params() (map[string]string, error) { - params := make(map[string]string) - - params["user_id"] = strconv.FormatInt(config.UserID, 10) - - return params, nil -} - func (config UploadStickerConfig) name() string { return "png_sticker" } @@ -1632,49 +1272,24 @@ func (config NewStickerSetConfig) method() string { return "createNewStickerSet" } -func (config NewStickerSetConfig) values() (url.Values, error) { - v := url.Values{} +func (config NewStickerSetConfig) params() (Params, error) { + v := make(Params) + + v.AddNonZero64("user_id", config.UserID) + v["name"] = config.Name + v["title"] = config.Title - v.Add("user_id", strconv.FormatInt(config.UserID, 10)) - v.Add("name", config.Name) - v.Add("title", config.Title) if sticker, ok := config.PNGSticker.(string); ok { - v.Add("png_sticker", sticker) + v[config.name()] = sticker } - v.Add("emojis", config.Emojis) - if config.ContainsMasks { - v.Add("contains_masks", strconv.FormatBool(config.ContainsMasks)) - data, err := json.Marshal(config.MaskPosition) - if err != nil { - return v, err - } + v["emojis"] = config.Emojis - v.Add("mask_position", string(data)) - } - - return v, nil -} + v.AddBool("contains_masks", config.ContainsMasks) -func (config NewStickerSetConfig) params() (map[string]string, error) { - params := make(map[string]string) + err := v.AddInterface("mask_position", config.MaskPosition) - params["user_id"] = strconv.FormatInt(config.UserID, 10) - params["name"] = config.Name - params["title"] = config.Title - params["emojis"] = config.Emojis - if config.ContainsMasks { - params["contains_masks"] = strconv.FormatBool(config.ContainsMasks) - - data, err := json.Marshal(config.MaskPosition) - if err != nil { - return params, err - } - - params["mask_position"] = string(data) - } - - return params, nil + return v, err } func (config NewStickerSetConfig) getFile() interface{} { @@ -1704,43 +1319,20 @@ func (config AddStickerConfig) method() string { return "addStickerToSet" } -func (config AddStickerConfig) values() (url.Values, error) { - v := url.Values{} +func (config AddStickerConfig) params() (Params, error) { + v := make(Params) + + v.AddNonZero64("user_id", config.UserID) + v["name"] = config.Name + v["emojis"] = config.Emojis - v.Add("user_id", strconv.FormatInt(config.UserID, 10)) - v.Add("name", config.Name) if sticker, ok := config.PNGSticker.(string); ok { - v.Add("png_sticker", sticker) + v[config.name()] = sticker } - v.Add("emojis", config.Emojis) - if config.MaskPosition != nil { - data, err := json.Marshal(config.MaskPosition) - if err != nil { - return v, err - } - - v.Add("mask_position", string(data)) - } - - return v, nil -} - -func (config AddStickerConfig) params() (map[string]string, error) { - params := make(map[string]string) - params["user_id"] = strconv.FormatInt(config.UserID, 10) - params["name"] = config.Name - params["emojis"] = config.Emojis - if config.MaskPosition != nil { - data, err := json.Marshal(config.MaskPosition) - if err != nil { - return params, err - } - - params["mask_position"] = string(data) - } + err := v.AddInterface("mask_position", config.MaskPosition) - return params, nil + return v, err } func (config AddStickerConfig) name() string { @@ -1765,11 +1357,11 @@ func (config SetStickerPositionConfig) method() string { return "setStickerPositionInSet" } -func (config SetStickerPositionConfig) values() (url.Values, error) { - v := url.Values{} +func (config SetStickerPositionConfig) params() (Params, error) { + v := make(Params) - v.Add("sticker", config.Sticker) - v.Add("position", strconv.Itoa(config.Position)) + v["sticker"] = config.Sticker + v.AddNonZero("position", config.Position) return v, nil } @@ -1783,10 +1375,10 @@ func (config DeleteStickerConfig) method() string { return "deleteStickerFromSet" } -func (config DeleteStickerConfig) values() (url.Values, error) { - v := url.Values{} +func (config DeleteStickerConfig) params() (Params, error) { + v := make(Params) - v.Add("sticker", config.Sticker) + v["sticker"] = config.Sticker return v, nil } @@ -1803,16 +1395,11 @@ func (config SetChatStickerSetConfig) method() string { return "setChatStickerSet" } -func (config SetChatStickerSetConfig) values() (url.Values, error) { - v := url.Values{} - - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } +func (config SetChatStickerSetConfig) params() (Params, error) { + v := make(Params) - v.Add("sticker_set_name", config.StickerSetName) + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + v["sticker_set_name"] = config.StickerSetName return v, nil } @@ -1827,14 +1414,10 @@ func (config DeleteChatStickerSetConfig) method() string { return "deleteChatStickerSet" } -func (config DeleteChatStickerSetConfig) values() (url.Values, error) { - v := url.Values{} +func (config DeleteChatStickerSetConfig) params() (Params, error) { + v := make(Params) - if config.SuperGroupUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.SuperGroupUsername) - } + v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) return v, nil } @@ -1855,25 +1438,15 @@ func (config MediaGroupConfig) method() string { return "sendMediaGroup" } -func (config MediaGroupConfig) values() (url.Values, error) { - v := url.Values{} +func (config MediaGroupConfig) params() (Params, error) { + v := make(Params) - if config.ChannelUsername == "" { - v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) - } else { - v.Add("chat_id", config.ChannelUsername) - } - bytes, err := json.Marshal(config.Media) - if err != nil { - return v, err - } - v.Add("media", string(bytes)) - if config.DisableNotification { - v.Add("disable_notification", strconv.FormatBool(config.DisableNotification)) - } - if config.ReplyToMessageID != 0 { - v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID)) + v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + if err := v.AddInterface("media", config.Media); err != nil { + return v, nil } + v.AddBool("disable_notification", config.DisableNotification) + v.AddNonZero("reply_to_message_id", config.ReplyToMessageID) return v, nil } diff --git a/params.go b/params.go new file mode 100644 index 0000000..599c8eb --- /dev/null +++ b/params.go @@ -0,0 +1,100 @@ +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) + } +} + +// AddNonNilBool adds the value of a bool pointer if not nil. +func (p Params) AddNonNilBool(key string, value *bool) { + if value != nil { + 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 nill 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) + } + case int64: + if v != 0 { + p[key] = strconv.FormatInt(v, 10) + } + case string: + if v != "" { + p[key] = v + } + case nil: + default: + b, err := json.Marshal(arg) + if err != nil { + return err + } + + p[key] = string(b) + } + } + + return nil +} diff --git a/passport.go b/passport.go index f949b88..5f55006 100644 --- a/passport.go +++ b/passport.go @@ -307,7 +307,7 @@ type ( MiddleNameNative string `json:"middle_name_native"` } - // IdDocumentData https://core.telegram.org/passport#iddocumentdata + // IDDocumentData https://core.telegram.org/passport#iddocumentdata IDDocumentData struct { DocumentNumber string `json:"document_no"` ExpiryDate string `json:"expiry_date"` diff --git a/types_test.go b/types_test.go index 8a67c8e..928ebae 100644 --- a/types_test.go +++ b/types_test.go @@ -198,3 +198,44 @@ func TestFileLink(t *testing.T) { t.Fail() } } + +// Ensure all configs are sendable +var ( + _ tgbotapi.Chattable = tgbotapi.AnimationConfig{} + _ tgbotapi.Chattable = tgbotapi.AudioConfig{} + _ tgbotapi.Chattable = tgbotapi.CallbackConfig{} + _ tgbotapi.Chattable = tgbotapi.ChatActionConfig{} + _ tgbotapi.Chattable = tgbotapi.ContactConfig{} + _ tgbotapi.Chattable = tgbotapi.DeleteChatPhotoConfig{} + _ tgbotapi.Chattable = tgbotapi.DeleteChatStickerSetConfig{} + _ tgbotapi.Chattable = tgbotapi.DeleteMessageConfig{} + _ tgbotapi.Chattable = tgbotapi.DocumentConfig{} + _ tgbotapi.Chattable = tgbotapi.EditMessageCaptionConfig{} + _ tgbotapi.Chattable = tgbotapi.EditMessageLiveLocationConfig{} + _ tgbotapi.Chattable = tgbotapi.EditMessageReplyMarkupConfig{} + _ tgbotapi.Chattable = tgbotapi.EditMessageTextConfig{} + _ tgbotapi.Chattable = tgbotapi.ForwardConfig{} + _ tgbotapi.Chattable = tgbotapi.GameConfig{} + _ tgbotapi.Chattable = tgbotapi.GetGameHighScoresConfig{} + _ tgbotapi.Chattable = tgbotapi.InlineConfig{} + _ tgbotapi.Chattable = tgbotapi.InvoiceConfig{} + _ tgbotapi.Chattable = tgbotapi.KickChatMemberConfig{} + _ tgbotapi.Chattable = tgbotapi.LocationConfig{} + _ tgbotapi.Chattable = tgbotapi.MediaGroupConfig{} + _ tgbotapi.Chattable = tgbotapi.MessageConfig{} + _ tgbotapi.Chattable = tgbotapi.PhotoConfig{} + _ tgbotapi.Chattable = tgbotapi.PinChatMessageConfig{} + _ tgbotapi.Chattable = tgbotapi.SetChatDescriptionConfig{} + _ tgbotapi.Chattable = tgbotapi.SetChatPhotoConfig{} + _ tgbotapi.Chattable = tgbotapi.SetChatTitleConfig{} + _ tgbotapi.Chattable = tgbotapi.SetGameScoreConfig{} + _ tgbotapi.Chattable = tgbotapi.StickerConfig{} + _ tgbotapi.Chattable = tgbotapi.UnpinChatMessageConfig{} + _ tgbotapi.Chattable = tgbotapi.UpdateConfig{} + _ tgbotapi.Chattable = tgbotapi.UserProfilePhotosConfig{} + _ tgbotapi.Chattable = tgbotapi.VenueConfig{} + _ tgbotapi.Chattable = tgbotapi.VideoConfig{} + _ tgbotapi.Chattable = tgbotapi.VideoNoteConfig{} + _ tgbotapi.Chattable = tgbotapi.VoiceConfig{} + _ tgbotapi.Chattable = tgbotapi.WebhookConfig{} +) From 655c3a4137072ec3d30bf69378c601b1fc44770f Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 8 Oct 2018 18:26:50 -0500 Subject: [PATCH 20/29] Remove outdated and old comments. --- configs.go | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/configs.go b/configs.go index 62c90cd..41df83f 100644 --- a/configs.go +++ b/configs.go @@ -68,7 +68,6 @@ type BaseChat struct { DisableNotification bool } -// params returns Params representation of BaseChat func (chat *BaseChat) params() (Params, error) { v := make(Params) @@ -91,7 +90,6 @@ type BaseFile struct { FileSize int } -// params returns a Params representation of BaseFile. func (file BaseFile) params() (Params, error) { params, err := file.BaseChat.params() @@ -101,12 +99,10 @@ func (file BaseFile) params() (Params, error) { return params, err } -// 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 } @@ -143,7 +139,6 @@ type MessageConfig struct { DisableWebPagePreview bool } -// values returns a url.Values representation of MessageConfig. func (config MessageConfig) params() (Params, error) { v, err := config.BaseChat.params() if err != nil { @@ -157,7 +152,6 @@ func (config MessageConfig) params() (Params, error) { return v, nil } -// method returns Telegram API method name for sending Message. func (config MessageConfig) method() string { return "sendMessage" } @@ -170,7 +164,6 @@ type ForwardConfig struct { MessageID int // required } -// values returns a url.Values representation of ForwardConfig. func (config ForwardConfig) params() (Params, error) { v, err := config.BaseChat.params() if err != nil { @@ -183,7 +176,6 @@ func (config ForwardConfig) params() (Params, error) { return v, nil } -// method returns Telegram API method name for sending Forward. func (config ForwardConfig) method() string { return "forwardMessage" } @@ -195,7 +187,6 @@ type PhotoConfig struct { ParseMode string } -// Params returns a map[string]string representation of PhotoConfig. func (config PhotoConfig) params() (Params, error) { params, err := config.BaseFile.params() @@ -206,12 +197,10 @@ func (config PhotoConfig) params() (Params, error) { return params, err } -// 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" } @@ -226,7 +215,6 @@ type AudioConfig struct { Title string } -// values returns a url.Values representation of AudioConfig. func (config AudioConfig) params() (Params, error) { v, err := config.BaseChat.params() if err != nil { @@ -243,12 +231,10 @@ func (config AudioConfig) params() (Params, error) { return v, 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" } @@ -260,7 +246,6 @@ type DocumentConfig struct { ParseMode string } -// params returns a map[string]string representation of DocumentConfig. func (config DocumentConfig) params() (Params, error) { params, err := config.BaseFile.params() @@ -271,12 +256,10 @@ func (config DocumentConfig) params() (Params, error) { return params, err } -// 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" } @@ -286,7 +269,6 @@ type StickerConfig struct { BaseFile } -// values returns a url.Values representation of StickerConfig. func (config StickerConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -295,12 +277,10 @@ func (config StickerConfig) params() (Params, error) { return v, err } -// 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" } @@ -313,7 +293,6 @@ type VideoConfig struct { ParseMode string } -// values returns a url.Values representation of VideoConfig. func (config VideoConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -325,12 +304,10 @@ func (config VideoConfig) params() (Params, error) { return v, err } -// 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" } @@ -343,7 +320,6 @@ type AnimationConfig struct { ParseMode string } -// values returns a Params representation of AnimationConfig. func (config AnimationConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -355,12 +331,10 @@ func (config AnimationConfig) params() (Params, error) { return v, err } -// 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" } @@ -372,7 +346,6 @@ type VideoNoteConfig struct { Length int } -// values returns a url.Values representation of VideoNoteConfig. func (config VideoNoteConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -383,12 +356,10 @@ func (config VideoNoteConfig) params() (Params, error) { return v, err } -// 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" } @@ -401,7 +372,6 @@ type VoiceConfig struct { Duration int } -// values returns a url.Values representation of VoiceConfig. func (config VoiceConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -413,12 +383,10 @@ func (config VoiceConfig) params() (Params, error) { return v, err } -// 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" } @@ -431,7 +399,6 @@ type LocationConfig struct { LivePeriod int // optional } -// values returns a url.Values representation of LocationConfig. func (config LocationConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -442,7 +409,6 @@ func (config LocationConfig) params() (Params, error) { return v, err } -// method returns Telegram API method name for sending Location. func (config LocationConfig) method() string { return "sendLocation" } @@ -454,7 +420,6 @@ type EditMessageLiveLocationConfig struct { Longitude float64 // required } -// values returns a url.Values representation of EditMessageLiveLocationConfig. func (config EditMessageLiveLocationConfig) params() (Params, error) { v, err := config.BaseEdit.params() @@ -464,7 +429,6 @@ func (config EditMessageLiveLocationConfig) params() (Params, error) { return v, err } -// method returns Telegram API method name for edit message Live Location. func (config EditMessageLiveLocationConfig) method() string { return "editMessageLiveLocation" } @@ -474,12 +438,10 @@ type StopMessageLiveLocationConfig struct { BaseEdit } -// values returns a url.Values representation of StopMessageLiveLocationConfig. func (config StopMessageLiveLocationConfig) params() (Params, error) { return config.BaseEdit.params() } -// method returns Telegram API method name for stop message Live Location. func (config StopMessageLiveLocationConfig) method() string { return "stopMessageLiveLocation" } @@ -617,7 +579,6 @@ type ChatActionConfig struct { Action string // required } -// values returns a url.Values representation of ChatActionConfig. func (config ChatActionConfig) params() (Params, error) { v, err := config.BaseChat.params() @@ -626,7 +587,6 @@ func (config ChatActionConfig) params() (Params, error) { return v, err } -// method returns Telegram API method name for sending ChatAction. func (config ChatActionConfig) method() string { return "sendChatAction" } From 1f859674f7d9f1ab67d3bef4b6ce2c66897f6203 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 8 Oct 2018 18:37:18 -0500 Subject: [PATCH 21/29] Consistency of variable names. --- bot.go | 88 ++++++++--------- configs.go | 284 ++++++++++++++++++++++++++--------------------------- 2 files changed, 186 insertions(+), 186 deletions(-) diff --git a/bot.go b/bot.go index c01bb82..b0a5efa 100644 --- a/bot.go +++ b/bot.go @@ -329,11 +329,11 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro // // Requires FileID. func (bot *BotAPI) GetFile(config FileConfig) (File, error) { - v := make(Params) + params := make(Params) - v["file_id"] = config.FileID + params["file_id"] = config.FileID - resp, err := bot.MakeRequest("getFile", v) + resp, err := bot.MakeRequest("getFile", params) if err != nil { return File{}, err } @@ -438,11 +438,11 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { // GetChat gets information about a chat. func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - resp, err := bot.MakeRequest("getChat", v) + resp, err := bot.MakeRequest("getChat", params) if err != nil { return Chat{}, err } @@ -458,11 +458,11 @@ func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { // 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 := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - resp, err := bot.MakeRequest("getChatAdministrators", v) + resp, err := bot.MakeRequest("getChatAdministrators", params) if err != nil { return []ChatMember{}, err } @@ -475,11 +475,11 @@ func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error // GetChatMembersCount gets the number of users in a chat. func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - resp, err := bot.MakeRequest("getChatMembersCount", v) + resp, err := bot.MakeRequest("getChatMembersCount", params) if err != nil { return -1, err } @@ -492,12 +492,12 @@ func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { // GetChatMember gets a specific chat member. func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("user_id", config.UserID) - resp, err := bot.MakeRequest("getChatMember", v) + resp, err := bot.MakeRequest("getChatMember", params) if err != nil { return ChatMember{}, err } @@ -511,12 +511,12 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) // 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 := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero("user_id", config.UserID) - return bot.MakeRequest("unbanChatMember", v) + return bot.MakeRequest("unbanChatMember", params) } // RestrictChatMember to restrict a user in a supergroup. The bot must be an @@ -524,37 +524,37 @@ func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) //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 := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero("user_id", config.UserID) - v.AddNonNilBool("can_send_messages", config.CanSendMessages) - v.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) - v.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) - v.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) - v.AddNonZero64("until_date", config.UntilDate) + params.AddNonNilBool("can_send_messages", config.CanSendMessages) + params.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) + params.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) + params.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) + params.AddNonZero64("until_date", config.UntilDate) - return bot.MakeRequest("restrictChatMember", v) + return bot.MakeRequest("restrictChatMember", params) } // PromoteChatMember add admin rights to user func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero("user_id", config.UserID) - v.AddNonNilBool("can_change_info", config.CanChangeInfo) - v.AddNonNilBool("can_post_messages", config.CanPostMessages) - v.AddNonNilBool("can_edit_messages", config.CanEditMessages) - v.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) - v.AddNonNilBool("can_invite_members", config.CanInviteUsers) - v.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) - v.AddNonNilBool("can_pin_messages", config.CanPinMessages) - v.AddNonNilBool("can_promote_members", config.CanPromoteMembers) + params.AddNonNilBool("can_change_info", config.CanChangeInfo) + params.AddNonNilBool("can_post_messages", config.CanPostMessages) + params.AddNonNilBool("can_edit_messages", config.CanEditMessages) + params.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) + params.AddNonNilBool("can_invite_members", config.CanInviteUsers) + params.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) + params.AddNonNilBool("can_pin_messages", config.CanPinMessages) + params.AddNonNilBool("can_promote_members", config.CanPromoteMembers) - return bot.MakeRequest("promoteChatMember", v) + return bot.MakeRequest("promoteChatMember", params) } // GetGameHighScores allows you to get the high scores for a game. @@ -577,11 +577,11 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh // GetInviteLink get InviteLink for a chat func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - resp, err := bot.MakeRequest("exportChatInviteLink", v) + resp, err := bot.MakeRequest("exportChatInviteLink", params) if err != nil { return "", err } diff --git a/configs.go b/configs.go index 41df83f..a5256b7 100644 --- a/configs.go +++ b/configs.go @@ -69,15 +69,15 @@ type BaseChat struct { } func (chat *BaseChat) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) - v.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) - v.AddBool("disable_notification", chat.DisableNotification) + params.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) + params.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) + params.AddBool("disable_notification", chat.DisableNotification) - err := v.AddInterface("reply_markup", chat.ReplyMarkup) + err := params.AddInterface("reply_markup", chat.ReplyMarkup) - return v, err + return params, err } // BaseFile is a base type for all file config types. @@ -117,18 +117,18 @@ type BaseEdit struct { } func (edit BaseEdit) params() (Params, error) { - v := make(Params) + params := make(Params) if edit.InlineMessageID != "" { - v["inline_message_id"] = edit.InlineMessageID + params["inline_message_id"] = edit.InlineMessageID } else { - v.AddFirstValid("chat_id", edit.ChatID, edit.ChannelUsername) - v.AddNonZero("message_id", edit.MessageID) + params.AddFirstValid("chat_id", edit.ChatID, edit.ChannelUsername) + params.AddNonZero("message_id", edit.MessageID) } - err := v.AddInterface("reply_markup", edit.ReplyMarkup) + err := params.AddInterface("reply_markup", edit.ReplyMarkup) - return v, err + return params, err } // MessageConfig contains information about a SendMessage request. @@ -525,20 +525,20 @@ type SetGameScoreConfig struct { } func (config SetGameScoreConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddNonZero("user_id", config.UserID) - v.AddNonZero("scrore", config.Score) - v.AddBool("disable_edit_message", config.DisableEditMessage) + params.AddNonZero("user_id", config.UserID) + params.AddNonZero("scrore", config.Score) + params.AddBool("disable_edit_message", config.DisableEditMessage) if config.InlineMessageID != "" { - v["inline_message_id"] = config.InlineMessageID + params["inline_message_id"] = config.InlineMessageID } else { - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - v.AddNonZero("message_id", config.MessageID) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) } - return v, nil + return params, nil } func (config SetGameScoreConfig) method() string { @@ -555,18 +555,18 @@ type GetGameHighScoresConfig struct { } func (config GetGameHighScoresConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddNonZero("user_id", config.UserID) + params.AddNonZero("user_id", config.UserID) if config.InlineMessageID != "" { - v["inline_message_id"] = config.InlineMessageID + params["inline_message_id"] = config.InlineMessageID } else { - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - v.AddNonZero("message_id", config.MessageID) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) } - return v, nil + return params, nil } func (config GetGameHighScoresConfig) method() string { @@ -774,20 +774,20 @@ func (config InlineConfig) method() string { } func (config InlineConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v["inline_query_id"] = config.InlineQueryID - v.AddNonZero("cache_time", config.CacheTime) - v.AddBool("is_personal", config.IsPersonal) - v.AddNonEmpty("next_offset", config.NextOffset) - v.AddNonEmpty("switch_pm_text", config.SwitchPMText) - v.AddNonEmpty("switch_pm_parameter", config.SwitchPMParameter) + 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) - if err := v.AddInterface("results", config.Results); err != nil { - return v, err + if err := params.AddInterface("results", config.Results); err != nil { + return params, err } - return v, nil + return params, nil } // CallbackConfig contains information on making a CallbackQuery response. @@ -804,15 +804,15 @@ func (config CallbackConfig) method() string { } func (config CallbackConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v["callback_query_id"] = config.CallbackQueryID - v.AddNonEmpty("text", config.Text) - v.AddBool("show_alert", config.ShowAlert) - v.AddNonEmpty("url", config.URL) - v.AddNonZero("cache_time", config.CacheTime) + 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 v, nil + return params, nil } // ChatMemberConfig contains information about a user in a chat for use @@ -834,12 +834,12 @@ func (config UnbanChatMemberConfig) method() string { } func (config UnbanChatMemberConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero("user_id", config.UserID) - return v, nil + return params, nil } // KickChatMemberConfig contains extra fields to kick user @@ -853,13 +853,13 @@ func (config KickChatMemberConfig) method() string { } func (config KickChatMemberConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - v.AddNonZero("user_id", config.UserID) - v.AddNonZero64("until_date", config.UntilDate) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("user_id", config.UserID) + params.AddNonZero64("until_date", config.UntilDate) - return v, nil + return params, nil } // RestrictChatMemberConfig contains fields to restrict members of chat @@ -877,18 +877,18 @@ func (config RestrictChatMemberConfig) method() string { } func (config RestrictChatMemberConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero("user_id", config.UserID) - v.AddNonNilBool("can_send_messages", config.CanSendMessages) - v.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) - v.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) - v.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) - v.AddNonZero64("until_date", config.UntilDate) + params.AddNonNilBool("can_send_messages", config.CanSendMessages) + params.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) + params.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) + params.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) + params.AddNonZero64("until_date", config.UntilDate) - return v, nil + return params, nil } // PromoteChatMemberConfig contains fields to promote members of chat @@ -909,21 +909,21 @@ func (config PromoteChatMemberConfig) method() string { } func (config PromoteChatMemberConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - v.AddNonZero("user_id", config.UserID) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) + params.AddNonZero("user_id", config.UserID) - v.AddNonNilBool("can_change_info", config.CanChangeInfo) - v.AddNonNilBool("can_post_messages", config.CanPostMessages) - v.AddNonNilBool("can_edit_messages", config.CanEditMessages) - v.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) - v.AddNonNilBool("can_invite_users", config.CanInviteUsers) - v.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) - v.AddNonNilBool("can_pin_messages", config.CanPinMessages) - v.AddNonNilBool("can_promote_members", config.CanPromoteMembers) + params.AddNonNilBool("can_change_info", config.CanChangeInfo) + params.AddNonNilBool("can_post_messages", config.CanPostMessages) + params.AddNonNilBool("can_edit_messages", config.CanEditMessages) + params.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) + params.AddNonNilBool("can_invite_users", config.CanInviteUsers) + params.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) + params.AddNonNilBool("can_pin_messages", config.CanPinMessages) + params.AddNonNilBool("can_promote_members", config.CanPromoteMembers) - return v, nil + return params, nil } // ChatConfig contains information about getting information on a chat. @@ -943,11 +943,11 @@ func (config LeaveChatConfig) method() string { } func (config LeaveChatConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - return v, nil + return params, nil } // ChatConfigWithUser contains information about getting information on @@ -1041,12 +1041,12 @@ func (config DeleteMessageConfig) method() string { } func (config DeleteMessageConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddNonZero64("chat_id", config.ChatID) - v.AddNonZero("message_id", config.MessageID) + params.AddNonZero64("chat_id", config.ChatID) + params.AddNonZero("message_id", config.MessageID) - return v, nil + return params, nil } // PinChatMessageConfig contains information of a message in a chat to pin. @@ -1062,13 +1062,13 @@ func (config PinChatMessageConfig) method() string { } func (config PinChatMessageConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - v.AddNonZero("message_id", config.MessageID) - v.AddBool("disable_notification", config.DisableNotification) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + params.AddBool("disable_notification", config.DisableNotification) - return v, nil + return params, nil } // UnpinChatMessageConfig contains information of chat to unpin. @@ -1082,11 +1082,11 @@ func (config UnpinChatMessageConfig) method() string { } func (config UnpinChatMessageConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - return v, nil + return params, nil } // SetChatPhotoConfig allows you to set a group, supergroup, or channel's photo. @@ -1121,11 +1121,11 @@ func (config DeleteChatPhotoConfig) method() string { } func (config DeleteChatPhotoConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - return v, nil + return params, nil } // SetChatTitleConfig allows you to set the title of something other than a private chat. @@ -1141,12 +1141,12 @@ func (config SetChatTitleConfig) method() string { } func (config SetChatTitleConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - v["title"] = config.Title + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params["title"] = config.Title - return v, nil + return params, nil } // SetChatDescriptionConfig allows you to set the description of a supergroup or channel. @@ -1162,12 +1162,12 @@ func (config SetChatDescriptionConfig) method() string { } func (config SetChatDescriptionConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - v["description"] = config.Description + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params["description"] = config.Description - return v, nil + return params, nil } // GetStickerSetConfig allows you to get the stickers in a set. @@ -1180,11 +1180,11 @@ func (config GetStickerSetConfig) method() string { } func (config GetStickerSetConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v["name"] = config.Name + params["name"] = config.Name - return v, nil + return params, nil } // UploadStickerConfig allows you to upload a sticker for use in a set later. @@ -1198,11 +1198,11 @@ func (config UploadStickerConfig) method() string { } func (config UploadStickerConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddNonZero64("user_id", config.UserID) + params.AddNonZero64("user_id", config.UserID) - return v, nil + return params, nil } func (config UploadStickerConfig) name() string { @@ -1233,23 +1233,23 @@ func (config NewStickerSetConfig) method() string { } func (config NewStickerSetConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddNonZero64("user_id", config.UserID) - v["name"] = config.Name - v["title"] = config.Title + params.AddNonZero64("user_id", config.UserID) + params["name"] = config.Name + params["title"] = config.Title if sticker, ok := config.PNGSticker.(string); ok { - v[config.name()] = sticker + params[config.name()] = sticker } - v["emojis"] = config.Emojis + params["emojis"] = config.Emojis - v.AddBool("contains_masks", config.ContainsMasks) + params.AddBool("contains_masks", config.ContainsMasks) - err := v.AddInterface("mask_position", config.MaskPosition) + err := params.AddInterface("mask_position", config.MaskPosition) - return v, err + return params, err } func (config NewStickerSetConfig) getFile() interface{} { @@ -1280,19 +1280,19 @@ func (config AddStickerConfig) method() string { } func (config AddStickerConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddNonZero64("user_id", config.UserID) - v["name"] = config.Name - v["emojis"] = config.Emojis + params.AddNonZero64("user_id", config.UserID) + params["name"] = config.Name + params["emojis"] = config.Emojis if sticker, ok := config.PNGSticker.(string); ok { - v[config.name()] = sticker + params[config.name()] = sticker } - err := v.AddInterface("mask_position", config.MaskPosition) + err := params.AddInterface("mask_position", config.MaskPosition) - return v, err + return params, err } func (config AddStickerConfig) name() string { @@ -1318,12 +1318,12 @@ func (config SetStickerPositionConfig) method() string { } func (config SetStickerPositionConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v["sticker"] = config.Sticker - v.AddNonZero("position", config.Position) + params["sticker"] = config.Sticker + params.AddNonZero("position", config.Position) - return v, nil + return params, nil } // DeleteStickerConfig allows you to delete a sticker from a set. @@ -1336,11 +1336,11 @@ func (config DeleteStickerConfig) method() string { } func (config DeleteStickerConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v["sticker"] = config.Sticker + params["sticker"] = config.Sticker - return v, nil + return params, nil } // SetChatStickerSetConfig allows you to set the sticker set for a supergroup. @@ -1356,12 +1356,12 @@ func (config SetChatStickerSetConfig) method() string { } func (config SetChatStickerSetConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - v["sticker_set_name"] = config.StickerSetName + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params["sticker_set_name"] = config.StickerSetName - return v, nil + return params, nil } // DeleteChatStickerSetConfig allows you to remove a supergroup's sticker set. @@ -1375,11 +1375,11 @@ func (config DeleteChatStickerSetConfig) method() string { } func (config DeleteChatStickerSetConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - return v, nil + return params, nil } // MediaGroupConfig allows you to send a group of media. @@ -1399,14 +1399,14 @@ func (config MediaGroupConfig) method() string { } func (config MediaGroupConfig) params() (Params, error) { - v := make(Params) + params := make(Params) - v.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) - if err := v.AddInterface("media", config.Media); err != nil { - return v, nil + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + if err := params.AddInterface("media", config.Media); err != nil { + return params, nil } - v.AddBool("disable_notification", config.DisableNotification) - v.AddNonZero("reply_to_message_id", config.ReplyToMessageID) + params.AddBool("disable_notification", config.DisableNotification) + params.AddNonZero("reply_to_message_id", config.ReplyToMessageID) - return v, nil + return params, nil } From 4d758f17d47eba8951d7810190e907db56e51801 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 8 Oct 2018 23:05:24 -0500 Subject: [PATCH 22/29] Add some missing fields, generalize configs, remove unneeded methods. --- README.md | 2 +- bot.go | 110 +++------------- configs.go | 365 +++++++++++++++++++++++++++++++++++------------------ helpers.go | 42 +++++- types.go | 66 +++++++--- 5 files changed, 350 insertions(+), 235 deletions(-) diff --git a/README.md b/README.md index 0bf5a16..883b8fc 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ func main() { ``` 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 differen kinds of things. +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. diff --git a/bot.go b/bot.go index b0a5efa..5f9b079 100644 --- a/bot.go +++ b/bot.go @@ -329,11 +329,9 @@ func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserPro // // Requires FileID. func (bot *BotAPI) GetFile(config FileConfig) (File, error) { - params := make(Params) - - params["file_id"] = config.FileID + params, _ := config.params() - resp, err := bot.MakeRequest("getFile", params) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return File{}, err } @@ -437,12 +435,10 @@ func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel { } // GetChat gets information about a chat. -func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) +func (bot *BotAPI) GetChat(config ChatInfoConfig) (Chat, error) { + params, _ := config.params() - resp, err := bot.MakeRequest("getChat", params) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return Chat{}, err } @@ -457,12 +453,10 @@ func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) { // // 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) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) +func (bot *BotAPI) GetChatAdministrators(config ChatAdministratorsConfig) ([]ChatMember, error) { + params, _ := config.params() - resp, err := bot.MakeRequest("getChatAdministrators", params) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return []ChatMember{}, err } @@ -474,12 +468,10 @@ func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error } // GetChatMembersCount gets the number of users in a chat. -func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) +func (bot *BotAPI) GetChatMembersCount(config ChatMemberCountConfig) (int, error) { + params, _ := config.params() - resp, err := bot.MakeRequest("getChatMembersCount", params) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return -1, err } @@ -491,13 +483,10 @@ func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) { } // GetChatMember gets a specific chat member. -func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) - params.AddNonZero("user_id", config.UserID) +func (bot *BotAPI) GetChatMember(config GetChatMemberConfig) (ChatMember, error) { + params, _ := config.params() - resp, err := bot.MakeRequest("getChatMember", params) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return ChatMember{}, err } @@ -508,63 +497,11 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) 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) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - params.AddNonZero("user_id", config.UserID) - - return bot.MakeRequest("unbanChatMember", params) -} - -// 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) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - params.AddNonZero("user_id", config.UserID) - - params.AddNonNilBool("can_send_messages", config.CanSendMessages) - params.AddNonNilBool("can_send_media_messages", config.CanSendMediaMessages) - params.AddNonNilBool("can_send_other_messages", config.CanSendOtherMessages) - params.AddNonNilBool("can_add_web_page_previews", config.CanAddWebPagePreviews) - params.AddNonZero64("until_date", config.UntilDate) - - return bot.MakeRequest("restrictChatMember", params) -} - -// PromoteChatMember add admin rights to user -func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) - params.AddNonZero("user_id", config.UserID) - - params.AddNonNilBool("can_change_info", config.CanChangeInfo) - params.AddNonNilBool("can_post_messages", config.CanPostMessages) - params.AddNonNilBool("can_edit_messages", config.CanEditMessages) - params.AddNonNilBool("can_delete_messages", config.CanDeleteMessages) - params.AddNonNilBool("can_invite_members", config.CanInviteUsers) - params.AddNonNilBool("can_restrict_members", config.CanRestrictMembers) - params.AddNonNilBool("can_pin_messages", config.CanPinMessages) - params.AddNonNilBool("can_promote_members", config.CanPromoteMembers) - - return bot.MakeRequest("promoteChatMember", params) -} - // GetGameHighScores allows you to get the high scores for a game. func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) { - v, err := config.params() - if err != nil { - return nil, err - } + params, _ := config.params() - resp, err := bot.MakeRequest(config.method(), v) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return []GameHighScore{}, err } @@ -576,12 +513,10 @@ func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHigh } // GetInviteLink get InviteLink for a chat -func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { - params := make(Params) - - params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) +func (bot *BotAPI) GetInviteLink(config ChatInviteLinkConfig) (string, error) { + params, _ := config.params() - resp, err := bot.MakeRequest("exportChatInviteLink", params) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return "", err } @@ -594,12 +529,9 @@ func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) { // GetStickerSet returns a StickerSet. func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) { - v, err := config.params() - if err != nil { - return StickerSet{}, nil - } + params, _ := config.params() - resp, err := bot.MakeRequest(config.method(), v) + resp, err := bot.MakeRequest(config.method(), params) if err != nil { return StickerSet{}, nil } diff --git a/configs.go b/configs.go index a5256b7..250b557 100644 --- a/configs.go +++ b/configs.go @@ -140,16 +140,16 @@ type MessageConfig struct { } func (config MessageConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() if err != nil { - return v, err + return params, err } - v.AddNonEmpty("text", config.Text) - v.AddBool("disable_web_page_preview", config.DisableWebPagePreview) - v.AddNonEmpty("parse_mode", config.ParseMode) + params.AddNonEmpty("text", config.Text) + params.AddBool("disable_web_page_preview", config.DisableWebPagePreview) + params.AddNonEmpty("parse_mode", config.ParseMode) - return v, nil + return params, nil } func (config MessageConfig) method() string { @@ -165,15 +165,15 @@ type ForwardConfig struct { } func (config ForwardConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() if err != nil { - return v, err + return params, err } - v.AddNonZero64("from_chat_id", config.FromChatID) - v.AddNonZero("message_id", config.MessageID) + params.AddNonZero64("from_chat_id", config.FromChatID) + params.AddNonZero("message_id", config.MessageID) - return v, nil + return params, nil } func (config ForwardConfig) method() string { @@ -216,19 +216,19 @@ type AudioConfig struct { } func (config AudioConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() if err != nil { - return v, err + return params, err } - v.AddNonEmpty(config.name(), config.FileID) - v.AddNonZero("duration", config.Duration) - v.AddNonEmpty("performer", config.Performer) - v.AddNonEmpty("title", config.Title) - v.AddNonEmpty("caption", config.Caption) - v.AddNonEmpty("parse_mode", config.ParseMode) + params.AddNonEmpty(config.name(), config.FileID) + 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) - return v, nil + return params, nil } func (config AudioConfig) name() string { @@ -270,11 +270,11 @@ type StickerConfig struct { } func (config StickerConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonEmpty(config.name(), config.FileID) + params.AddNonEmpty(config.name(), config.FileID) - return v, err + return params, err } func (config StickerConfig) name() string { @@ -288,20 +288,22 @@ func (config StickerConfig) method() string { // VideoConfig contains information about a SendVideo request. type VideoConfig struct { BaseFile - Duration int - Caption string - ParseMode string + Duration int + Caption string + ParseMode string + SupportsStreaming bool } func (config VideoConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonEmpty(config.name(), config.FileID) - v.AddNonZero("duration", config.Duration) - v.AddNonEmpty("caption", config.Caption) - v.AddNonEmpty("parse_mode", config.ParseMode) + params.AddNonEmpty(config.name(), config.FileID) + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + params.AddBool("supports_streaming", config.SupportsStreaming) - return v, err + return params, err } func (config VideoConfig) name() string { @@ -321,14 +323,14 @@ type AnimationConfig struct { } func (config AnimationConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonEmpty(config.name(), config.FileID) - v.AddNonZero("duration", config.Duration) - v.AddNonEmpty("caption", config.Caption) - v.AddNonEmpty("parse_mode", config.ParseMode) + params.AddNonEmpty(config.name(), config.FileID) + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) - return v, err + return params, err } func (config AnimationConfig) name() string { @@ -347,13 +349,13 @@ type VideoNoteConfig struct { } func (config VideoNoteConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonEmpty(config.name(), config.FileID) - v.AddNonZero("duration", config.Duration) - v.AddNonZero("length", config.Length) + params.AddNonEmpty(config.name(), config.FileID) + params.AddNonZero("duration", config.Duration) + params.AddNonZero("length", config.Length) - return v, err + return params, err } func (config VideoNoteConfig) name() string { @@ -373,14 +375,14 @@ type VoiceConfig struct { } func (config VoiceConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonEmpty(config.name(), config.FileID) - v.AddNonZero("duration", config.Duration) - v.AddNonEmpty("caption", config.Caption) - v.AddNonEmpty("parse_mode", config.ParseMode) + params.AddNonEmpty(config.name(), config.FileID) + params.AddNonZero("duration", config.Duration) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) - return v, err + return params, err } func (config VoiceConfig) name() string { @@ -400,13 +402,13 @@ type LocationConfig struct { } func (config LocationConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonZeroFloat("latitude", config.Latitude) - v.AddNonZeroFloat("longitude", config.Longitude) - v.AddNonZero("live_period", config.LivePeriod) + params.AddNonZeroFloat("latitude", config.Latitude) + params.AddNonZeroFloat("longitude", config.Longitude) + params.AddNonZero("live_period", config.LivePeriod) - return v, err + return params, err } func (config LocationConfig) method() string { @@ -421,12 +423,12 @@ type EditMessageLiveLocationConfig struct { } func (config EditMessageLiveLocationConfig) params() (Params, error) { - v, err := config.BaseEdit.params() + params, err := config.BaseEdit.params() - v.AddNonZeroFloat("latitude", config.Latitude) - v.AddNonZeroFloat("longitude", config.Longitude) + params.AddNonZeroFloat("latitude", config.Latitude) + params.AddNonZeroFloat("longitude", config.Longitude) - return v, err + return params, err } func (config EditMessageLiveLocationConfig) method() string { @@ -457,15 +459,15 @@ type VenueConfig struct { } func (config VenueConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v.AddNonZeroFloat("latitude", config.Latitude) - v.AddNonZeroFloat("longitude", config.Longitude) - v["title"] = config.Title - v["address"] = config.Address - v.AddNonEmpty("foursquare_id", config.FoursquareID) + params.AddNonZeroFloat("latitude", config.Latitude) + params.AddNonZeroFloat("longitude", config.Longitude) + params["title"] = config.Title + params["address"] = config.Address + params.AddNonEmpty("foursquare_id", config.FoursquareID) - return v, err + return params, err } func (config VenueConfig) method() string { @@ -478,16 +480,19 @@ type ContactConfig struct { PhoneNumber string FirstName string LastName string + VCard string } func (config ContactConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v["phone_number"] = config.PhoneNumber - v["first_name"] = config.FirstName - v["last_name"] = config.LastName + params["phone_number"] = config.PhoneNumber + params["first_name"] = config.FirstName - return v, err + params.AddNonEmpty("last_name", config.LastName) + params.AddNonEmpty("vcard", config.VCard) + + return params, err } func (config ContactConfig) method() string { @@ -501,11 +506,11 @@ type GameConfig struct { } func (config GameConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v["game_short_name"] = config.GameShortName + params["game_short_name"] = config.GameShortName - return v, err + return params, err } func (config GameConfig) method() string { @@ -580,11 +585,11 @@ type ChatActionConfig struct { } func (config ChatActionConfig) params() (Params, error) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() - v["action"] = config.Action + params["action"] = config.Action - return v, err + return params, err } func (config ChatActionConfig) method() string { @@ -600,13 +605,13 @@ type EditMessageTextConfig struct { } func (config EditMessageTextConfig) params() (Params, error) { - v, err := config.BaseEdit.params() + params, err := config.BaseEdit.params() - v["text"] = config.Text - v.AddNonEmpty("parse_mode", config.ParseMode) - v.AddBool("disable_web_page_preview", config.DisableWebPagePreview) + params["text"] = config.Text + params.AddNonEmpty("parse_mode", config.ParseMode) + params.AddBool("disable_web_page_preview", config.DisableWebPagePreview) - return v, err + return params, err } func (config EditMessageTextConfig) method() string { @@ -621,18 +626,37 @@ type EditMessageCaptionConfig struct { } func (config EditMessageCaptionConfig) params() (Params, error) { - v, err := config.BaseEdit.params() + params, err := config.BaseEdit.params() - v["caption"] = config.Caption - v.AddNonEmpty("parse_mode", config.ParseMode) + params["caption"] = config.Caption + params.AddNonEmpty("parse_mode", config.ParseMode) - return v, err + return params, err } func (config EditMessageCaptionConfig) method() string { return "editMessageCaption" } +// EditMessageMediaConfig contains information about editing a message's media. +type EditMessageMediaConfig struct { + BaseEdit + + Media interface{} +} + +func (EditMessageMediaConfig) method() string { + return "editMessageMedia" +} + +func (config EditMessageMediaConfig) params() (Params, error) { + params, err := config.BaseEdit.params() + + params.AddInterface("media", config.Media) + + return params, err +} + // EditMessageReplyMarkupConfig allows you to modify the reply markup // of a message. type EditMessageReplyMarkupConfig struct { @@ -674,6 +698,18 @@ 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 @@ -700,6 +736,7 @@ type WebhookConfig struct { URL *url.URL Certificate interface{} MaxConnections int + AllowedUpdates []string } func (config WebhookConfig) method() string { @@ -714,6 +751,7 @@ func (config WebhookConfig) params() (Params, error) { } params.AddNonZero("max_connections", config.MaxConnections) + params.AddInterface("allowed_updates", config.AllowedUpdates) return params, nil } @@ -932,6 +970,60 @@ type ChatConfig struct { 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" +} + +// 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 +} + // LeaveChatConfig allows you to leave a chat. type LeaveChatConfig struct { ChatID int64 @@ -950,65 +1042,86 @@ func (config LeaveChatConfig) params() (Params, error) { return params, nil } -// ChatConfigWithUser contains information about getting information on -// a specific user within a chat. +// ChatConfigWithUser contains information about a chat and a user. type ChatConfigWithUser struct { ChatID int64 SuperGroupUsername string UserID int } +func (config ChatConfigWithUser) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("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 - StartParameter string // required - Currency string // required - Prices *[]LabeledPrice // required - ProviderData string - PhotoURL string - PhotoSize int - PhotoWidth int - PhotoHeight int - NeedName bool - NeedPhoneNumber bool - NeedEmail bool - NeedShippingAddress bool - IsFlexible bool + Title string // required + Description string // required + Payload string // required + ProviderToken string // required + StartParameter string // required + Currency string // required + Prices *[]LabeledPrice // required + 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) { - v, err := config.BaseChat.params() + params, err := config.BaseChat.params() if err != nil { - return v, err + return params, err } - v["title"] = config.Title - v["description"] = config.Description - v["payload"] = config.Payload - v["provider_token"] = config.ProviderToken - v["start_parameter"] = config.StartParameter - v["currency"] = config.Currency + params["title"] = config.Title + params["description"] = config.Description + params["payload"] = config.Payload + params["provider_token"] = config.ProviderToken + params["start_parameter"] = config.StartParameter + params["currency"] = config.Currency - if err = v.AddInterface("prices", config.Prices); err != nil { - return v, err + if err = params.AddInterface("prices", config.Prices); err != nil { + return params, err } - v.AddNonEmpty("provider_data", config.ProviderData) - v.AddNonEmpty("photo_url", config.PhotoURL) - v.AddNonZero("photo_size", config.PhotoSize) - v.AddNonZero("photo_width", config.PhotoWidth) - v.AddNonZero("photo_height", config.PhotoHeight) - v.AddBool("need_name", config.NeedName) - v.AddBool("need_phone_number", config.NeedPhoneNumber) - v.AddBool("need_email", config.NeedEmail) - v.AddBool("need_shipping_address", config.NeedShippingAddress) - v.AddBool("is_flexible", config.IsFlexible) - - return v, nil + 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, nil } func (config InvoiceConfig) method() string { diff --git a/helpers.go b/helpers.go index b4e01eb..9d16bdb 100644 --- a/helpers.go +++ b/helpers.go @@ -302,16 +302,50 @@ func NewMediaGroup(chatID int64, files []interface{}) MediaGroupConfig { // NewInputMediaPhoto creates a new InputMediaPhoto. func NewInputMediaPhoto(media string) InputMediaPhoto { return InputMediaPhoto{ - Type: "photo", - Media: media, + BaseInputMedia{ + Type: "photo", + Media: media, + }, } } // NewInputMediaVideo creates a new InputMediaVideo. func NewInputMediaVideo(media string) InputMediaVideo { return InputMediaVideo{ - Type: "video", - Media: media, + BaseInputMedia: BaseInputMedia{ + Type: "video", + Media: media, + }, + } +} + +// NewInputMediaAnimation creates a new InputMediaAnimation. +func NewInputMediaAnimation(media string) InputMediaAnimation { + return InputMediaAnimation{ + BaseInputMedia: BaseInputMedia{ + Type: "animation", + Media: media, + }, + } +} + +// NewInputMediaAudio creates a new InputMediaAudio. +func NewInputMediaAudio(media string) InputMediaAudio { + return InputMediaAudio{ + BaseInputMedia: BaseInputMedia{ + Type: "audio", + Media: media, + }, + } +} + +// NewInputMediaDocument creates a new InputMediaDocument. +func NewInputMediaDocument(media string) InputMediaDocument { + return InputMediaDocument{ + BaseInputMedia: BaseInputMedia{ + Type: "document", + Media: media, + }, } } diff --git a/types.go b/types.go index 101cfdf..73a3faf 100644 --- a/types.go +++ b/types.go @@ -175,6 +175,7 @@ type Message struct { PinnedMessage *Message `json:"pinned_message"` // optional Invoice *Invoice `json:"invoice"` // optional SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional + ConnectedWebsite string `json:"connected_website"` // optional PassportData *PassportData `json:"passport_data,omitempty"` // optional } @@ -367,6 +368,7 @@ type Contact struct { FirstName string `json:"first_name"` LastName string `json:"last_name"` // optional UserID int `json:"user_id"` // optional + VCard string `json:"vcard"` // optional } // Location contains information about a place. @@ -692,6 +694,21 @@ type InlineQueryResultLocation struct { ThumbHeight int `json:"thumb_height"` } +// 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 string `json:"type"` @@ -740,6 +757,7 @@ type InputContactMessageContent struct { PhoneNumber string `json:"phone_number"` FirstName string `json:"first_name"` LastName string `json:"last_name"` + VCard string `json:"vcard"` } // Invoice contains basic information about an invoice. @@ -820,29 +838,47 @@ type StickerSet struct { Stickers []Sticker `json:"stickers"` } -// InputMediaPhoto is a photo to send as part of a media group. -// -// Telegram recommends to use a file_id instead of uploading. -type InputMediaPhoto struct { +// BaseInputMedia is a base type for the InputMedia types. +type BaseInputMedia struct { Type string `json:"type"` Media string `json:"media"` Caption string `json:"caption"` ParseMode string `json:"parse_mode"` } +// 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. -// -// Telegram recommends to use a file_id instead of uploading. 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"` + BaseInputMedia + Width int `json:"width"` + Height int `json:"height"` + Duration int `json:"duration"` + SupportsStreaming bool `json:"supports_streaming"` +} + +// InputMediaAnimation is an animation to send as part of a media group. +type InputMediaAnimation struct { + BaseInputMedia + Width int `json:"width"` + Height int `json:"height"` + Duration int `json:"duration"` +} + +// InputMediaAudio is a audio to send as part of a media group. +type InputMediaAudio struct { + BaseInputMedia + Duration int `json:"duration"` + Performer string `json:"performer"` + Title string `json:"title"` +} + +// InputMediaDocument is a audio to send as part of a media group. +type InputMediaDocument struct { + BaseInputMedia } // Error is an error containing extra information returned by the Telegram API. From cdcb93df5fc27adebfa8f3bc7d86ff0a33b5c7fe Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 8 Oct 2018 23:32:34 -0500 Subject: [PATCH 23/29] No reason to use pointers to arrays. --- configs.go | 16 ++++++++-------- helpers.go | 2 +- types.go | 24 ++++++++++++------------ types_test.go | 12 ++++++------ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/configs.go b/configs.go index 250b557..08a2609 100644 --- a/configs.go +++ b/configs.go @@ -1070,13 +1070,13 @@ func (GetChatMemberConfig) method() string { // 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 + Title string // required + Description string // required + Payload string // required + ProviderToken string // required + StartParameter string // required + Currency string // required + Prices []LabeledPrice // required ProviderData string PhotoURL string PhotoSize int @@ -1132,7 +1132,7 @@ func (config InvoiceConfig) method() string { type ShippingConfig struct { ShippingQueryID string // required OK bool // required - ShippingOptions *[]ShippingOption + ShippingOptions []ShippingOption ErrorMessage string } diff --git a/helpers.go b/helpers.go index 9d16bdb..fea373d 100644 --- a/helpers.go +++ b/helpers.go @@ -737,7 +737,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, diff --git a/types.go b/types.go index 73a3faf..8a5326a 100644 --- a/types.go +++ b/types.go @@ -147,13 +147,13 @@ type Message struct { MediaGroupID string `json:"media_group_id"` // optional AuthorSignature string `json:"author_signature"` // optional Text string `json:"text"` // optional - Entities *[]MessageEntity `json:"entities"` // optional - CaptionEntities *[]MessageEntity `json:"caption_entities"` // 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 + Photo []PhotoSize `json:"photo"` // optional Sticker *Sticker `json:"sticker"` // optional Video *Video `json:"video"` // optional VideoNote *VideoNote `json:"video_note"` // optional @@ -162,10 +162,10 @@ type Message struct { Contact *Contact `json:"contact"` // optional Location *Location `json:"location"` // optional Venue *Venue `json:"venue"` // optional - NewChatMembers *[]User `json:"new_chat_members"` // 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 + 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 @@ -186,11 +186,11 @@ func (m *Message) Time() time.Time { // IsCommand returns true if message starts with a "bot_command" entity. func (m *Message) IsCommand() bool { - if m.Entities == nil || len(*m.Entities) == 0 { + if m.Entities == nil || len(m.Entities) == 0 { return false } - entity := (*m.Entities)[0] + entity := m.Entities[0] return entity.Offset == 0 && entity.Type == "bot_command" } @@ -220,7 +220,7 @@ func (m *Message) CommandWithAt() string { } // IsCommand() checks that the message begins with a bot_command entity - entity := (*m.Entities)[0] + entity := m.Entities[0] return m.Text[1:entity.Length] } @@ -239,7 +239,7 @@ func (m *Message) CommandArguments() string { } // IsCommand() checks that the message begins with a bot_command entity - entity := (*m.Entities)[0] + entity := m.Entities[0] if len(m.Text) == entity.Length { return "" // The command makes up the whole message @@ -795,9 +795,9 @@ type OrderInfo struct { // ShippingOption represents one shipping option. type ShippingOption struct { - ID string `json:"id"` - Title string `json:"title"` - Prices *[]LabeledPrice `json:"prices"` + ID string `json:"id"` + Title string `json:"title"` + Prices []LabeledPrice `json:"prices"` } // SuccessfulPayment contains basic information about a successful payment. diff --git a/types_test.go b/types_test.go index 928ebae..2659a2f 100644 --- a/types_test.go +++ b/types_test.go @@ -47,7 +47,7 @@ func TestMessageTime(t *testing.T) { func TestMessageIsCommandWithCommand(t *testing.T) { message := tgbotapi.Message{Text: "/command"} - message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if !message.IsCommand() { t.Fail() @@ -72,7 +72,7 @@ func TestIsCommandWithEmptyText(t *testing.T) { func TestCommandWithCommand(t *testing.T) { message := tgbotapi.Message{Text: "/command"} - message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.Command() != "command" { t.Fail() @@ -97,7 +97,7 @@ func TestCommandWithNonCommand(t *testing.T) { func TestCommandWithBotName(t *testing.T) { message := tgbotapi.Message{Text: "/command@testbot"} - message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} + message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} if message.Command() != "command" { t.Fail() @@ -106,7 +106,7 @@ func TestCommandWithBotName(t *testing.T) { func TestCommandWithAtWithBotName(t *testing.T) { message := tgbotapi.Message{Text: "/command@testbot"} - message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} + message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} if message.CommandWithAt() != "command@testbot" { t.Fail() @@ -115,7 +115,7 @@ func TestCommandWithAtWithBotName(t *testing.T) { func TestMessageCommandArgumentsWithArguments(t *testing.T) { message := tgbotapi.Message{Text: "/command with arguments"} - message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.CommandArguments() != "with arguments" { t.Fail() } @@ -123,7 +123,7 @@ func TestMessageCommandArgumentsWithArguments(t *testing.T) { func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) { message := tgbotapi.Message{Text: "/command-without argument space"} - message.Entities = &[]tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.CommandArguments() != "without argument space" { t.Fail() } From a746f39d224dc1070eb527fac4fdb70fd0ac0964 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Mon, 8 Oct 2018 23:34:34 -0500 Subject: [PATCH 24/29] Move tests into tgbotapi package. --- bot_test.go | 147 ++++++++++++++++++++++++------------------------ helpers_test.go | 52 ++++++++--------- types_test.go | 134 ++++++++++++++++++++++--------------------- 3 files changed, 163 insertions(+), 170 deletions(-) diff --git a/bot_test.go b/bot_test.go index 89a14c0..a84647d 100644 --- a/bot_test.go +++ b/bot_test.go @@ -1,14 +1,11 @@ -package tgbotapi_test +package tgbotapi import ( "io/ioutil" - "log" "net/http" "os" "testing" "time" - - "github.com/go-telegram-bot-api/telegram-bot-api" ) const ( @@ -25,8 +22,8 @@ const ( ExistingStickerFileID = "BQADAgADcwADjMcoCbdl-6eB--YPAg" ) -func getBot(t *testing.T) (*tgbotapi.BotAPI, error) { - bot, err := tgbotapi.NewBotAPI(TestToken) +func getBot(t *testing.T) (*BotAPI, error) { + bot, err := NewBotAPI(TestToken) bot.Debug = true if err != nil { @@ -38,7 +35,7 @@ func getBot(t *testing.T) (*tgbotapi.BotAPI, error) { } func TestNewBotAPI_notoken(t *testing.T) { - _, err := tgbotapi.NewBotAPI("") + _, err := NewBotAPI("") if err == nil { t.Error(err) @@ -49,7 +46,7 @@ func TestNewBotAPI_notoken(t *testing.T) { func TestGetUpdates(t *testing.T) { bot, _ := getBot(t) - u := tgbotapi.NewUpdate(0) + u := NewUpdate(0) _, err := bot.GetUpdates(u) @@ -62,7 +59,7 @@ func TestGetUpdates(t *testing.T) { func TestSendWithMessage(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api") + msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") msg.ParseMode = "markdown" _, err := bot.Send(msg) @@ -75,7 +72,7 @@ func TestSendWithMessage(t *testing.T) { func TestSendWithMessageReply(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api") + msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") msg.ReplyToMessageID = ReplyToMessageID _, err := bot.Send(msg) @@ -88,7 +85,7 @@ func TestSendWithMessageReply(t *testing.T) { func TestSendWithMessageForward(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewForward(ChatID, ChatID, ReplyToMessageID) + msg := NewForward(ChatID, ChatID, ReplyToMessageID) _, err := bot.Send(msg) if err != nil { @@ -100,7 +97,7 @@ func TestSendWithMessageForward(t *testing.T) { func TestSendWithNewPhoto(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg") + msg := NewPhotoUpload(ChatID, "tests/image.jpg") msg.Caption = "Test" _, err := bot.Send(msg) @@ -114,9 +111,9 @@ func TestSendWithNewPhotoWithFileBytes(t *testing.T) { bot, _ := getBot(t) data, _ := ioutil.ReadFile("tests/image.jpg") - b := tgbotapi.FileBytes{Name: "image.jpg", Bytes: data} + b := FileBytes{Name: "image.jpg", Bytes: data} - msg := tgbotapi.NewPhotoUpload(ChatID, b) + msg := NewPhotoUpload(ChatID, b) msg.Caption = "Test" _, err := bot.Send(msg) @@ -130,9 +127,9 @@ func TestSendWithNewPhotoWithFileReader(t *testing.T) { bot, _ := getBot(t) f, _ := os.Open("tests/image.jpg") - reader := tgbotapi.FileReader{Name: "image.jpg", Reader: f, Size: -1} + reader := FileReader{Name: "image.jpg", Reader: f, Size: -1} - msg := tgbotapi.NewPhotoUpload(ChatID, reader) + msg := NewPhotoUpload(ChatID, reader) msg.Caption = "Test" _, err := bot.Send(msg) @@ -145,7 +142,7 @@ func TestSendWithNewPhotoWithFileReader(t *testing.T) { func TestSendWithNewPhotoReply(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewPhotoUpload(ChatID, "tests/image.jpg") + msg := NewPhotoUpload(ChatID, "tests/image.jpg") msg.ReplyToMessageID = ReplyToMessageID _, err := bot.Send(msg) @@ -159,7 +156,7 @@ func TestSendWithNewPhotoReply(t *testing.T) { func TestSendWithExistingPhoto(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewPhotoShare(ChatID, ExistingPhotoFileID) + msg := NewPhotoShare(ChatID, ExistingPhotoFileID) msg.Caption = "Test" _, err := bot.Send(msg) @@ -172,7 +169,7 @@ func TestSendWithExistingPhoto(t *testing.T) { func TestSendWithNewDocument(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewDocumentUpload(ChatID, "tests/image.jpg") + msg := NewDocumentUpload(ChatID, "tests/image.jpg") _, err := bot.Send(msg) if err != nil { @@ -184,7 +181,7 @@ func TestSendWithNewDocument(t *testing.T) { func TestSendWithExistingDocument(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewDocumentShare(ChatID, ExistingDocumentFileID) + msg := NewDocumentShare(ChatID, ExistingDocumentFileID) _, err := bot.Send(msg) if err != nil { @@ -196,7 +193,7 @@ func TestSendWithExistingDocument(t *testing.T) { func TestSendWithNewAudio(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewAudioUpload(ChatID, "tests/audio.mp3") + msg := NewAudioUpload(ChatID, "tests/audio.mp3") msg.Title = "TEST" msg.Duration = 10 msg.Performer = "TEST" @@ -213,7 +210,7 @@ func TestSendWithNewAudio(t *testing.T) { func TestSendWithExistingAudio(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewAudioShare(ChatID, ExistingAudioFileID) + msg := NewAudioShare(ChatID, ExistingAudioFileID) msg.Title = "TEST" msg.Duration = 10 msg.Performer = "TEST" @@ -229,7 +226,7 @@ func TestSendWithExistingAudio(t *testing.T) { func TestSendWithNewVoice(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewVoiceUpload(ChatID, "tests/voice.ogg") + msg := NewVoiceUpload(ChatID, "tests/voice.ogg") msg.Duration = 10 _, err := bot.Send(msg) @@ -242,7 +239,7 @@ func TestSendWithNewVoice(t *testing.T) { func TestSendWithExistingVoice(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewVoiceShare(ChatID, ExistingVoiceFileID) + msg := NewVoiceShare(ChatID, ExistingVoiceFileID) msg.Duration = 10 _, err := bot.Send(msg) @@ -255,7 +252,7 @@ func TestSendWithExistingVoice(t *testing.T) { func TestSendWithContact(t *testing.T) { bot, _ := getBot(t) - contact := tgbotapi.NewContact(ChatID, "5551234567", "Test") + contact := NewContact(ChatID, "5551234567", "Test") if _, err := bot.Send(contact); err != nil { t.Error(err) @@ -266,7 +263,7 @@ func TestSendWithContact(t *testing.T) { func TestSendWithLocation(t *testing.T) { bot, _ := getBot(t) - _, err := bot.Send(tgbotapi.NewLocation(ChatID, 40, 40)) + _, err := bot.Send(NewLocation(ChatID, 40, 40)) if err != nil { t.Error(err) @@ -277,7 +274,7 @@ func TestSendWithLocation(t *testing.T) { func TestSendWithVenue(t *testing.T) { bot, _ := getBot(t) - venue := tgbotapi.NewVenue(ChatID, "A Test Location", "123 Test Street", 40, 40) + venue := NewVenue(ChatID, "A Test Location", "123 Test Street", 40, 40) if _, err := bot.Send(venue); err != nil { t.Error(err) @@ -288,7 +285,7 @@ func TestSendWithVenue(t *testing.T) { func TestSendWithNewVideo(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewVideoUpload(ChatID, "tests/video.mp4") + msg := NewVideoUpload(ChatID, "tests/video.mp4") msg.Duration = 10 msg.Caption = "TEST" @@ -303,7 +300,7 @@ func TestSendWithNewVideo(t *testing.T) { func TestSendWithExistingVideo(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewVideoShare(ChatID, ExistingVideoFileID) + msg := NewVideoShare(ChatID, ExistingVideoFileID) msg.Duration = 10 msg.Caption = "TEST" @@ -318,7 +315,7 @@ func TestSendWithExistingVideo(t *testing.T) { func TestSendWithNewVideoNote(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewVideoNoteUpload(ChatID, 240, "tests/videonote.mp4") + msg := NewVideoNoteUpload(ChatID, 240, "tests/videonote.mp4") msg.Duration = 10 _, err := bot.Send(msg) @@ -332,7 +329,7 @@ func TestSendWithNewVideoNote(t *testing.T) { func TestSendWithExistingVideoNote(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewVideoNoteShare(ChatID, 240, ExistingVideoNoteFileID) + msg := NewVideoNoteShare(ChatID, 240, ExistingVideoNoteFileID) msg.Duration = 10 _, err := bot.Send(msg) @@ -346,7 +343,7 @@ func TestSendWithExistingVideoNote(t *testing.T) { func TestSendWithNewSticker(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg") + msg := NewStickerUpload(ChatID, "tests/image.jpg") _, err := bot.Send(msg) @@ -359,7 +356,7 @@ func TestSendWithNewSticker(t *testing.T) { func TestSendWithExistingSticker(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID) + msg := NewStickerShare(ChatID, ExistingStickerFileID) _, err := bot.Send(msg) @@ -372,8 +369,8 @@ func TestSendWithExistingSticker(t *testing.T) { func TestSendWithNewStickerAndKeyboardHide(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewStickerUpload(ChatID, "tests/image.jpg") - msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{ + msg := NewStickerUpload(ChatID, "tests/image.jpg") + msg.ReplyMarkup = ReplyKeyboardRemove{ RemoveKeyboard: true, Selective: false, } @@ -388,8 +385,8 @@ func TestSendWithNewStickerAndKeyboardHide(t *testing.T) { func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewStickerShare(ChatID, ExistingStickerFileID) - msg.ReplyMarkup = tgbotapi.ReplyKeyboardRemove{ + msg := NewStickerShare(ChatID, ExistingStickerFileID) + msg.ReplyMarkup = ReplyKeyboardRemove{ RemoveKeyboard: true, Selective: false, } @@ -405,7 +402,7 @@ func TestSendWithExistingStickerAndKeyboardHide(t *testing.T) { func TestGetFile(t *testing.T) { bot, _ := getBot(t) - file := tgbotapi.FileConfig{ + file := FileConfig{ FileID: ExistingPhotoFileID, } @@ -420,7 +417,7 @@ func TestGetFile(t *testing.T) { func TestSendChatConfig(t *testing.T) { bot, _ := getBot(t) - _, err := bot.Request(tgbotapi.NewChatAction(ChatID, tgbotapi.ChatTyping)) + _, err := bot.Request(NewChatAction(ChatID, ChatTyping)) if err != nil { t.Error(err) @@ -431,14 +428,14 @@ func TestSendChatConfig(t *testing.T) { func TestSendEditMessage(t *testing.T) { bot, _ := getBot(t) - msg, err := bot.Send(tgbotapi.NewMessage(ChatID, "Testing editing.")) + msg, err := bot.Send(NewMessage(ChatID, "Testing editing.")) if err != nil { t.Error(err) t.Fail() } - edit := tgbotapi.EditMessageTextConfig{ - BaseEdit: tgbotapi.BaseEdit{ + edit := EditMessageTextConfig{ + BaseEdit: BaseEdit{ ChatID: ChatID, MessageID: msg.MessageID, }, @@ -455,7 +452,7 @@ func TestSendEditMessage(t *testing.T) { func TestGetUserProfilePhotos(t *testing.T) { bot, _ := getBot(t) - _, err := bot.GetUserProfilePhotos(tgbotapi.NewUserProfilePhotos(ChatID)) + _, err := bot.GetUserProfilePhotos(NewUserProfilePhotos(ChatID)) if err != nil { t.Error(err) t.Fail() @@ -467,9 +464,9 @@ func TestSetWebhookWithCert(t *testing.T) { time.Sleep(time.Second * 2) - bot.Request(tgbotapi.RemoveWebhookConfig{}) + bot.Request(RemoveWebhookConfig{}) - wh := tgbotapi.NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") + wh := NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") _, err := bot.Request(wh) if err != nil { t.Error(err) @@ -482,7 +479,7 @@ func TestSetWebhookWithCert(t *testing.T) { t.Error(err) } - bot.Request(tgbotapi.RemoveWebhookConfig{}) + bot.Request(RemoveWebhookConfig{}) } func TestSetWebhookWithoutCert(t *testing.T) { @@ -490,9 +487,9 @@ func TestSetWebhookWithoutCert(t *testing.T) { time.Sleep(time.Second * 2) - bot.Request(tgbotapi.RemoveWebhookConfig{}) + bot.Request(RemoveWebhookConfig{}) - wh := tgbotapi.NewWebhook("https://example.com/tgbotapi-test/" + bot.Token) + wh := NewWebhook("https://example.com/tgbotapi-test/" + bot.Token) _, err := bot.Request(wh) if err != nil { t.Error(err) @@ -509,13 +506,13 @@ func TestSetWebhookWithoutCert(t *testing.T) { t.Errorf("failed to set webhook: %s", info.LastErrorMessage) } - bot.Request(tgbotapi.RemoveWebhookConfig{}) + bot.Request(RemoveWebhookConfig{}) } func TestUpdatesChan(t *testing.T) { bot, _ := getBot(t) - var ucfg tgbotapi.UpdateConfig = tgbotapi.NewUpdate(0) + var ucfg = NewUpdate(0) ucfg.Timeout = 60 _, err := bot.GetUpdatesChan(ucfg) @@ -528,10 +525,10 @@ func TestUpdatesChan(t *testing.T) { func TestSendWithMediaGroup(t *testing.T) { bot, _ := getBot(t) - cfg := tgbotapi.NewMediaGroup(ChatID, []interface{}{ - tgbotapi.NewInputMediaPhoto("https://i.imgur.com/unQLJIb.jpg"), - tgbotapi.NewInputMediaPhoto("https://i.imgur.com/J5qweNZ.jpg"), - tgbotapi.NewInputMediaVideo("https://i.imgur.com/F6RmI24.mp4"), + cfg := NewMediaGroup(ChatID, []interface{}{ + NewInputMediaPhoto("https://i.imgur.com/unQLJIb.jpg"), + NewInputMediaPhoto("https://i.imgur.com/J5qweNZ.jpg"), + NewInputMediaVideo("https://i.imgur.com/F6RmI24.mp4"), }) _, err := bot.Request(cfg) if err != nil { @@ -540,16 +537,16 @@ func TestSendWithMediaGroup(t *testing.T) { } func ExampleNewBotAPI() { - bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") + bot, err := NewBotAPI("MyAwesomeBotToken") if err != nil { - log.Panic(err) + panic(err) } bot.Debug = true log.Printf("Authorized on account %s", bot.Self.UserName) - u := tgbotapi.NewUpdate(0) + u := NewUpdate(0) u.Timeout = 60 updates, err := bot.GetUpdatesChan(u) @@ -566,7 +563,7 @@ func ExampleNewBotAPI() { log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text) - msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text) + msg := NewMessage(update.Message.Chat.ID, update.Message.Text) msg.ReplyToMessageID = update.Message.MessageID bot.Send(msg) @@ -574,24 +571,24 @@ func ExampleNewBotAPI() { } func ExampleNewWebhook() { - bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") + bot, err := NewBotAPI("MyAwesomeBotToken") if err != nil { - log.Fatal(err) + panic(err) } bot.Debug = true log.Printf("Authorized on account %s", bot.Self.UserName) - _, err = bot.Request(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) + _, err = bot.Request(NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) if err != nil { - log.Fatal(err) + panic(err) } info, err := bot.GetWebhookInfo() if err != nil { - log.Fatal(err) + panic(err) } if info.LastErrorDate != 0 { @@ -607,14 +604,14 @@ func ExampleNewWebhook() { } func ExampleInlineConfig() { - bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") // create new bot + bot, err := NewBotAPI("MyAwesomeBotToken") // create new bot if err != nil { - log.Panic(err) + panic(err) } log.Printf("Authorized on account %s", bot.Self.UserName) - u := tgbotapi.NewUpdate(0) + u := NewUpdate(0) u.Timeout = 60 updates, err := bot.GetUpdatesChan(u) @@ -624,10 +621,10 @@ func ExampleInlineConfig() { continue } - article := tgbotapi.NewInlineQueryResultArticle(update.InlineQuery.ID, "Echo", update.InlineQuery.Query) + article := NewInlineQueryResultArticle(update.InlineQuery.ID, "Echo", update.InlineQuery.Query) article.Description = update.InlineQuery.Query - inlineConf := tgbotapi.InlineConfig{ + inlineConf := InlineConfig{ InlineQueryID: update.InlineQuery.ID, IsPersonal: true, CacheTime: 0, @@ -643,11 +640,11 @@ func ExampleInlineConfig() { func TestDeleteMessage(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api") + msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") msg.ParseMode = "markdown" message, _ := bot.Send(msg) - deleteMessageConfig := tgbotapi.DeleteMessageConfig{ + deleteMessageConfig := DeleteMessageConfig{ ChatID: message.Chat.ID, MessageID: message.MessageID, } @@ -662,11 +659,11 @@ func TestDeleteMessage(t *testing.T) { func TestPinChatMessage(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") + msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") msg.ParseMode = "markdown" message, _ := bot.Send(msg) - pinChatMessageConfig := tgbotapi.PinChatMessageConfig{ + pinChatMessageConfig := PinChatMessageConfig{ ChatID: message.Chat.ID, MessageID: message.MessageID, DisableNotification: false, @@ -682,12 +679,12 @@ func TestPinChatMessage(t *testing.T) { func TestUnpinChatMessage(t *testing.T) { bot, _ := getBot(t) - msg := tgbotapi.NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") + msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") msg.ParseMode = "markdown" message, _ := bot.Send(msg) // We need pin message to unpin something - pinChatMessageConfig := tgbotapi.PinChatMessageConfig{ + pinChatMessageConfig := PinChatMessageConfig{ ChatID: message.Chat.ID, MessageID: message.MessageID, DisableNotification: false, @@ -698,7 +695,7 @@ func TestUnpinChatMessage(t *testing.T) { t.Fail() } - unpinChatMessageConfig := tgbotapi.UnpinChatMessageConfig{ + unpinChatMessageConfig := UnpinChatMessageConfig{ ChatID: message.Chat.ID, } diff --git a/helpers_test.go b/helpers_test.go index 7cb5c0b..8e4508b 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,48 +1,46 @@ -package tgbotapi_test +package tgbotapi import ( "testing" - - "github.com/go-telegram-bot-api/telegram-bot-api" ) func TestNewInlineQueryResultArticle(t *testing.T) { - result := tgbotapi.NewInlineQueryResultArticle("id", "title", "message") + result := NewInlineQueryResultArticle("id", "title", "message") if result.Type != "article" || result.ID != "id" || result.Title != "title" || - result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "message" { + result.InputMessageContent.(InputTextMessageContent).Text != "message" { t.Fail() } } func TestNewInlineQueryResultArticleMarkdown(t *testing.T) { - result := tgbotapi.NewInlineQueryResultArticleMarkdown("id", "title", "*message*") + result := NewInlineQueryResultArticleMarkdown("id", "title", "*message*") if result.Type != "article" || result.ID != "id" || result.Title != "title" || - result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "*message*" || - result.InputMessageContent.(tgbotapi.InputTextMessageContent).ParseMode != "Markdown" { + result.InputMessageContent.(InputTextMessageContent).Text != "*message*" || + result.InputMessageContent.(InputTextMessageContent).ParseMode != "Markdown" { t.Fail() } } func TestNewInlineQueryResultArticleHTML(t *testing.T) { - result := tgbotapi.NewInlineQueryResultArticleHTML("id", "title", "message") + result := NewInlineQueryResultArticleHTML("id", "title", "message") if result.Type != "article" || result.ID != "id" || result.Title != "title" || - result.InputMessageContent.(tgbotapi.InputTextMessageContent).Text != "message" || - result.InputMessageContent.(tgbotapi.InputTextMessageContent).ParseMode != "HTML" { + result.InputMessageContent.(InputTextMessageContent).Text != "message" || + result.InputMessageContent.(InputTextMessageContent).ParseMode != "HTML" { t.Fail() } } func TestNewInlineQueryResultGIF(t *testing.T) { - result := tgbotapi.NewInlineQueryResultGIF("id", "google.com") + result := NewInlineQueryResultGIF("id", "google.com") if result.Type != "gif" || result.ID != "id" || @@ -52,7 +50,7 @@ func TestNewInlineQueryResultGIF(t *testing.T) { } func TestNewInlineQueryResultMPEG4GIF(t *testing.T) { - result := tgbotapi.NewInlineQueryResultMPEG4GIF("id", "google.com") + result := NewInlineQueryResultMPEG4GIF("id", "google.com") if result.Type != "mpeg4_gif" || result.ID != "id" || @@ -62,7 +60,7 @@ func TestNewInlineQueryResultMPEG4GIF(t *testing.T) { } func TestNewInlineQueryResultPhoto(t *testing.T) { - result := tgbotapi.NewInlineQueryResultPhoto("id", "google.com") + result := NewInlineQueryResultPhoto("id", "google.com") if result.Type != "photo" || result.ID != "id" || @@ -72,7 +70,7 @@ func TestNewInlineQueryResultPhoto(t *testing.T) { } func TestNewInlineQueryResultPhotoWithThumb(t *testing.T) { - result := tgbotapi.NewInlineQueryResultPhotoWithThumb("id", "google.com", "thumb.com") + result := NewInlineQueryResultPhotoWithThumb("id", "google.com", "thumb.com") if result.Type != "photo" || result.ID != "id" || @@ -83,7 +81,7 @@ func TestNewInlineQueryResultPhotoWithThumb(t *testing.T) { } func TestNewInlineQueryResultVideo(t *testing.T) { - result := tgbotapi.NewInlineQueryResultVideo("id", "google.com") + result := NewInlineQueryResultVideo("id", "google.com") if result.Type != "video" || result.ID != "id" || @@ -93,7 +91,7 @@ func TestNewInlineQueryResultVideo(t *testing.T) { } func TestNewInlineQueryResultAudio(t *testing.T) { - result := tgbotapi.NewInlineQueryResultAudio("id", "google.com", "title") + result := NewInlineQueryResultAudio("id", "google.com", "title") if result.Type != "audio" || result.ID != "id" || @@ -104,7 +102,7 @@ func TestNewInlineQueryResultAudio(t *testing.T) { } func TestNewInlineQueryResultVoice(t *testing.T) { - result := tgbotapi.NewInlineQueryResultVoice("id", "google.com", "title") + result := NewInlineQueryResultVoice("id", "google.com", "title") if result.Type != "voice" || result.ID != "id" || @@ -115,7 +113,7 @@ func TestNewInlineQueryResultVoice(t *testing.T) { } func TestNewInlineQueryResultDocument(t *testing.T) { - result := tgbotapi.NewInlineQueryResultDocument("id", "google.com", "title", "mime/type") + result := NewInlineQueryResultDocument("id", "google.com", "title", "mime/type") if result.Type != "document" || result.ID != "id" || @@ -127,7 +125,7 @@ func TestNewInlineQueryResultDocument(t *testing.T) { } func TestNewInlineQueryResultLocation(t *testing.T) { - result := tgbotapi.NewInlineQueryResultLocation("id", "name", 40, 50) + result := NewInlineQueryResultLocation("id", "name", 40, 50) if result.Type != "location" || result.ID != "id" || @@ -139,7 +137,7 @@ func TestNewInlineQueryResultLocation(t *testing.T) { } func TestNewEditMessageText(t *testing.T) { - edit := tgbotapi.NewEditMessageText(ChatID, ReplyToMessageID, "new text") + edit := NewEditMessageText(ChatID, ReplyToMessageID, "new text") if edit.Text != "new text" || edit.BaseEdit.ChatID != ChatID || @@ -149,7 +147,7 @@ func TestNewEditMessageText(t *testing.T) { } func TestNewEditMessageCaption(t *testing.T) { - edit := tgbotapi.NewEditMessageCaption(ChatID, ReplyToMessageID, "new caption") + edit := NewEditMessageCaption(ChatID, ReplyToMessageID, "new caption") if edit.Caption != "new caption" || edit.BaseEdit.ChatID != ChatID || @@ -159,15 +157,15 @@ func TestNewEditMessageCaption(t *testing.T) { } func TestNewEditMessageReplyMarkup(t *testing.T) { - markup := tgbotapi.InlineKeyboardMarkup{ - InlineKeyboard: [][]tgbotapi.InlineKeyboardButton{ - []tgbotapi.InlineKeyboardButton{ - tgbotapi.InlineKeyboardButton{Text: "test"}, + markup := InlineKeyboardMarkup{ + InlineKeyboard: [][]InlineKeyboardButton{ + []InlineKeyboardButton{ + InlineKeyboardButton{Text: "test"}, }, }, } - edit := tgbotapi.NewEditMessageReplyMarkup(ChatID, ReplyToMessageID, markup) + edit := NewEditMessageReplyMarkup(ChatID, ReplyToMessageID, markup) if edit.ReplyMarkup.InlineKeyboard[0][0].Text != "test" || edit.BaseEdit.ChatID != ChatID || diff --git a/types_test.go b/types_test.go index 2659a2f..b418874 100644 --- a/types_test.go +++ b/types_test.go @@ -1,14 +1,12 @@ -package tgbotapi_test +package tgbotapi import ( "testing" "time" - - "github.com/go-telegram-bot-api/telegram-bot-api" ) func TestUserStringWith(t *testing.T) { - user := tgbotapi.User{ + user := User{ ID: 0, FirstName: "Test", LastName: "Test", @@ -23,7 +21,7 @@ func TestUserStringWith(t *testing.T) { } func TestUserStringWithUserName(t *testing.T) { - user := tgbotapi.User{ + user := User{ ID: 0, FirstName: "Test", LastName: "Test", @@ -37,7 +35,7 @@ func TestUserStringWithUserName(t *testing.T) { } func TestMessageTime(t *testing.T) { - message := tgbotapi.Message{Date: 0} + message := Message{Date: 0} date := time.Unix(0, 0) if message.Time() != date { @@ -46,8 +44,8 @@ func TestMessageTime(t *testing.T) { } func TestMessageIsCommandWithCommand(t *testing.T) { - message := tgbotapi.Message{Text: "/command"} - message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message := Message{Text: "/command"} + message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if !message.IsCommand() { t.Fail() @@ -55,7 +53,7 @@ func TestMessageIsCommandWithCommand(t *testing.T) { } func TestIsCommandWithText(t *testing.T) { - message := tgbotapi.Message{Text: "some text"} + message := Message{Text: "some text"} if message.IsCommand() { t.Fail() @@ -63,7 +61,7 @@ func TestIsCommandWithText(t *testing.T) { } func TestIsCommandWithEmptyText(t *testing.T) { - message := tgbotapi.Message{Text: ""} + message := Message{Text: ""} if message.IsCommand() { t.Fail() @@ -71,8 +69,8 @@ func TestIsCommandWithEmptyText(t *testing.T) { } func TestCommandWithCommand(t *testing.T) { - message := tgbotapi.Message{Text: "/command"} - message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message := Message{Text: "/command"} + message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.Command() != "command" { t.Fail() @@ -80,7 +78,7 @@ func TestCommandWithCommand(t *testing.T) { } func TestCommandWithEmptyText(t *testing.T) { - message := tgbotapi.Message{Text: ""} + message := Message{Text: ""} if message.Command() != "" { t.Fail() @@ -88,7 +86,7 @@ func TestCommandWithEmptyText(t *testing.T) { } func TestCommandWithNonCommand(t *testing.T) { - message := tgbotapi.Message{Text: "test text"} + message := Message{Text: "test text"} if message.Command() != "" { t.Fail() @@ -96,8 +94,8 @@ func TestCommandWithNonCommand(t *testing.T) { } func TestCommandWithBotName(t *testing.T) { - message := tgbotapi.Message{Text: "/command@testbot"} - message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} + message := Message{Text: "/command@testbot"} + message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} if message.Command() != "command" { t.Fail() @@ -105,8 +103,8 @@ func TestCommandWithBotName(t *testing.T) { } func TestCommandWithAtWithBotName(t *testing.T) { - message := tgbotapi.Message{Text: "/command@testbot"} - message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} + message := Message{Text: "/command@testbot"} + message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 16}} if message.CommandWithAt() != "command@testbot" { t.Fail() @@ -114,37 +112,37 @@ func TestCommandWithAtWithBotName(t *testing.T) { } func TestMessageCommandArgumentsWithArguments(t *testing.T) { - message := tgbotapi.Message{Text: "/command with arguments"} - message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message := Message{Text: "/command with arguments"} + message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.CommandArguments() != "with arguments" { t.Fail() } } func TestMessageCommandArgumentsWithMalformedArguments(t *testing.T) { - message := tgbotapi.Message{Text: "/command-without argument space"} - message.Entities = []tgbotapi.MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} + message := Message{Text: "/command-without argument space"} + message.Entities = []MessageEntity{{Type: "bot_command", Offset: 0, Length: 8}} if message.CommandArguments() != "without argument space" { t.Fail() } } func TestMessageCommandArgumentsWithoutArguments(t *testing.T) { - message := tgbotapi.Message{Text: "/command"} + message := Message{Text: "/command"} if message.CommandArguments() != "" { t.Fail() } } func TestMessageCommandArgumentsForNonCommand(t *testing.T) { - message := tgbotapi.Message{Text: "test text"} + message := Message{Text: "test text"} if message.CommandArguments() != "" { t.Fail() } } func TestMessageEntityParseURLGood(t *testing.T) { - entity := tgbotapi.MessageEntity{URL: "https://www.google.com"} + entity := MessageEntity{URL: "https://www.google.com"} if _, err := entity.ParseURL(); err != nil { t.Fail() @@ -152,7 +150,7 @@ func TestMessageEntityParseURLGood(t *testing.T) { } func TestMessageEntityParseURLBad(t *testing.T) { - entity := tgbotapi.MessageEntity{URL: ""} + entity := MessageEntity{URL: ""} if _, err := entity.ParseURL(); err == nil { t.Fail() @@ -160,7 +158,7 @@ func TestMessageEntityParseURLBad(t *testing.T) { } func TestChatIsPrivate(t *testing.T) { - chat := tgbotapi.Chat{ID: 10, Type: "private"} + chat := Chat{ID: 10, Type: "private"} if !chat.IsPrivate() { t.Fail() @@ -168,7 +166,7 @@ func TestChatIsPrivate(t *testing.T) { } func TestChatIsGroup(t *testing.T) { - chat := tgbotapi.Chat{ID: 10, Type: "group"} + chat := Chat{ID: 10, Type: "group"} if !chat.IsGroup() { t.Fail() @@ -176,7 +174,7 @@ func TestChatIsGroup(t *testing.T) { } func TestChatIsChannel(t *testing.T) { - chat := tgbotapi.Chat{ID: 10, Type: "channel"} + chat := Chat{ID: 10, Type: "channel"} if !chat.IsChannel() { t.Fail() @@ -184,7 +182,7 @@ func TestChatIsChannel(t *testing.T) { } func TestChatIsSuperGroup(t *testing.T) { - chat := tgbotapi.Chat{ID: 10, Type: "supergroup"} + chat := Chat{ID: 10, Type: "supergroup"} if !chat.IsSuperGroup() { t.Fail() @@ -192,7 +190,7 @@ func TestChatIsSuperGroup(t *testing.T) { } func TestFileLink(t *testing.T) { - file := tgbotapi.File{FilePath: "test/test.txt"} + file := File{FilePath: "test/test.txt"} if file.Link("token") != "https://api.telegram.org/file/bottoken/test/test.txt" { t.Fail() @@ -201,41 +199,41 @@ func TestFileLink(t *testing.T) { // Ensure all configs are sendable var ( - _ tgbotapi.Chattable = tgbotapi.AnimationConfig{} - _ tgbotapi.Chattable = tgbotapi.AudioConfig{} - _ tgbotapi.Chattable = tgbotapi.CallbackConfig{} - _ tgbotapi.Chattable = tgbotapi.ChatActionConfig{} - _ tgbotapi.Chattable = tgbotapi.ContactConfig{} - _ tgbotapi.Chattable = tgbotapi.DeleteChatPhotoConfig{} - _ tgbotapi.Chattable = tgbotapi.DeleteChatStickerSetConfig{} - _ tgbotapi.Chattable = tgbotapi.DeleteMessageConfig{} - _ tgbotapi.Chattable = tgbotapi.DocumentConfig{} - _ tgbotapi.Chattable = tgbotapi.EditMessageCaptionConfig{} - _ tgbotapi.Chattable = tgbotapi.EditMessageLiveLocationConfig{} - _ tgbotapi.Chattable = tgbotapi.EditMessageReplyMarkupConfig{} - _ tgbotapi.Chattable = tgbotapi.EditMessageTextConfig{} - _ tgbotapi.Chattable = tgbotapi.ForwardConfig{} - _ tgbotapi.Chattable = tgbotapi.GameConfig{} - _ tgbotapi.Chattable = tgbotapi.GetGameHighScoresConfig{} - _ tgbotapi.Chattable = tgbotapi.InlineConfig{} - _ tgbotapi.Chattable = tgbotapi.InvoiceConfig{} - _ tgbotapi.Chattable = tgbotapi.KickChatMemberConfig{} - _ tgbotapi.Chattable = tgbotapi.LocationConfig{} - _ tgbotapi.Chattable = tgbotapi.MediaGroupConfig{} - _ tgbotapi.Chattable = tgbotapi.MessageConfig{} - _ tgbotapi.Chattable = tgbotapi.PhotoConfig{} - _ tgbotapi.Chattable = tgbotapi.PinChatMessageConfig{} - _ tgbotapi.Chattable = tgbotapi.SetChatDescriptionConfig{} - _ tgbotapi.Chattable = tgbotapi.SetChatPhotoConfig{} - _ tgbotapi.Chattable = tgbotapi.SetChatTitleConfig{} - _ tgbotapi.Chattable = tgbotapi.SetGameScoreConfig{} - _ tgbotapi.Chattable = tgbotapi.StickerConfig{} - _ tgbotapi.Chattable = tgbotapi.UnpinChatMessageConfig{} - _ tgbotapi.Chattable = tgbotapi.UpdateConfig{} - _ tgbotapi.Chattable = tgbotapi.UserProfilePhotosConfig{} - _ tgbotapi.Chattable = tgbotapi.VenueConfig{} - _ tgbotapi.Chattable = tgbotapi.VideoConfig{} - _ tgbotapi.Chattable = tgbotapi.VideoNoteConfig{} - _ tgbotapi.Chattable = tgbotapi.VoiceConfig{} - _ tgbotapi.Chattable = tgbotapi.WebhookConfig{} + _ Chattable = AnimationConfig{} + _ Chattable = AudioConfig{} + _ Chattable = CallbackConfig{} + _ Chattable = ChatActionConfig{} + _ Chattable = ContactConfig{} + _ Chattable = DeleteChatPhotoConfig{} + _ Chattable = DeleteChatStickerSetConfig{} + _ Chattable = DeleteMessageConfig{} + _ Chattable = DocumentConfig{} + _ Chattable = EditMessageCaptionConfig{} + _ Chattable = EditMessageLiveLocationConfig{} + _ Chattable = EditMessageReplyMarkupConfig{} + _ Chattable = EditMessageTextConfig{} + _ Chattable = ForwardConfig{} + _ Chattable = GameConfig{} + _ Chattable = GetGameHighScoresConfig{} + _ Chattable = InlineConfig{} + _ Chattable = InvoiceConfig{} + _ Chattable = KickChatMemberConfig{} + _ Chattable = LocationConfig{} + _ Chattable = MediaGroupConfig{} + _ Chattable = MessageConfig{} + _ Chattable = PhotoConfig{} + _ Chattable = PinChatMessageConfig{} + _ Chattable = SetChatDescriptionConfig{} + _ Chattable = SetChatPhotoConfig{} + _ Chattable = SetChatTitleConfig{} + _ Chattable = SetGameScoreConfig{} + _ Chattable = StickerConfig{} + _ Chattable = UnpinChatMessageConfig{} + _ Chattable = UpdateConfig{} + _ Chattable = UserProfilePhotosConfig{} + _ Chattable = VenueConfig{} + _ Chattable = VideoConfig{} + _ Chattable = VideoNoteConfig{} + _ Chattable = VoiceConfig{} + _ Chattable = WebhookConfig{} ) From afda722fc3cf4eb4cb59c1652c28b7a57210f4a7 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Tue, 9 Oct 2018 00:47:19 -0500 Subject: [PATCH 25/29] Remove unused error returned by GetUpdatesChan. --- bot.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot.go b/bot.go index 5f9b079..7766346 100644 --- a/bot.go +++ b/bot.go @@ -378,7 +378,7 @@ func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) { } // GetUpdatesChan starts and returns a channel for getting updates. -func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) { +func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) UpdatesChannel { ch := make(chan Update, bot.Buffer) go func() { @@ -407,7 +407,7 @@ func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) { } }() - return ch, nil + return ch } // StopReceivingUpdates stops the go routine which receives updates From 290b9363d44f83c188b2d15e00c509ffdfdada82 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Tue, 9 Oct 2018 01:45:18 -0500 Subject: [PATCH 26/29] Fix bot_test.go, update README. --- README.md | 59 +---------------------------------------------------- bot_test.go | 17 ++------------- 2 files changed, 3 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 883b8fc..02cab2d 100644 --- a/README.md +++ b/README.md @@ -62,64 +62,7 @@ func main() { } ``` -There are more examples on the [wiki](https://github.com/go-telegram-bot-api/telegram-bot-api/wiki) +There are more examples on the [site](https://go-telegram-bot-api.github.io/) 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. - -```go -package main - -import ( - "log" - "net/http" - - "github.com/go-telegram-bot-api/telegram-bot-api" -) - -func main() { - bot, err := tgbotapi.NewBotAPI("MyAwesomeBotToken") - if err != nil { - log.Fatal(err) - } - - bot.Debug = true - - log.Printf("Authorized on account %s", bot.Self.UserName) - - _, err = bot.Request(tgbotapi.NewWebhookWithCert("https://www.google.com:8443/"+bot.Token, "cert.pem")) - 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) - - for update := range updates { - log.Printf("%+v\n", update) - } -} -``` - -If you need, you may generate a self signed certficate, as this requires -HTTPS / TLS. The above example tells Telegram that this is your -certificate and that it should be trusted, even though it is not -properly signed. - - openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 3560 -subj "//O=Org\CN=Test" -nodes - -Now that [Let's Encrypt](https://letsencrypt.org) is available, you may -wish to generate your free TLS certificate there. diff --git a/bot_test.go b/bot_test.go index a84647d..b315792 100644 --- a/bot_test.go +++ b/bot_test.go @@ -509,19 +509,6 @@ func TestSetWebhookWithoutCert(t *testing.T) { bot.Request(RemoveWebhookConfig{}) } -func TestUpdatesChan(t *testing.T) { - bot, _ := getBot(t) - - var ucfg = NewUpdate(0) - ucfg.Timeout = 60 - _, err := bot.GetUpdatesChan(ucfg) - - if err != nil { - t.Error(err) - t.Fail() - } -} - func TestSendWithMediaGroup(t *testing.T) { bot, _ := getBot(t) @@ -549,7 +536,7 @@ func ExampleNewBotAPI() { u := NewUpdate(0) u.Timeout = 60 - updates, err := bot.GetUpdatesChan(u) + updates := bot.GetUpdatesChan(u) // Optional: wait for updates and clear them if you don't want to handle // a large backlog of old messages @@ -614,7 +601,7 @@ func ExampleInlineConfig() { u := NewUpdate(0) u.Timeout = 60 - updates, err := bot.GetUpdatesChan(u) + updates := bot.GetUpdatesChan(u) for update := range updates { if update.InlineQuery == nil { // if no inline query, ignore it From 5781187bc20cbb9910ba867eafe318479be119b6 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Tue, 9 Oct 2018 02:08:29 -0500 Subject: [PATCH 27/29] Add go mod files. --- go.mod | 3 +++ go.sum | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b63227e --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/go-telegram-bot-api/telegram-bot-api/v5 + +require github.com/technoweenie/multipartstreamer v1.0.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8660600 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= +github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= From fa4070825746d9c5c9d73bb37774a6ccc60fb80d Mon Sep 17 00:00:00 2001 From: Syfaro Date: Tue, 25 Dec 2018 15:44:01 -0600 Subject: [PATCH 28/29] Add SendMediaGroup method. --- bot.go | 15 +++++++++++++++ bot_test.go | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/bot.go b/bot.go index 7766346..3247f01 100644 --- a/bot.go +++ b/bot.go @@ -307,6 +307,21 @@ func (bot *BotAPI) Send(c Chattable) (Message, error) { return message, err } +// SendMediaGroup sends a media group and returns the resulting messages. +func (bot *BotAPI) SendMediaGroup(config MediaGroupConfig) ([]Message, error) { + params, _ := config.params() + + resp, err := bot.MakeRequest(config.method(), params) + 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. diff --git a/bot_test.go b/bot_test.go index b315792..137f9b9 100644 --- a/bot_test.go +++ b/bot_test.go @@ -517,10 +517,19 @@ func TestSendWithMediaGroup(t *testing.T) { NewInputMediaPhoto("https://i.imgur.com/J5qweNZ.jpg"), NewInputMediaVideo("https://i.imgur.com/F6RmI24.mp4"), }) - _, err := bot.Request(cfg) + + messages, err := bot.SendMediaGroup(cfg) if err != nil { t.Error(err) } + + if messages == nil { + t.Error() + } + + if len(messages) != 3 { + t.Error() + } } func ExampleNewBotAPI() { From db88019230b2befb84cb3cbd97fe8802049df821 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Sun, 14 Apr 2019 14:46:45 -0500 Subject: [PATCH 29/29] Add support for Polls and other API 4.2 updates. --- bot.go | 20 +++++++++++++++++++- bot_test.go | 33 +++++++++++++++++++++++++++++++++ configs.go | 36 ++++++++++++++++++++++++++++++++++++ helpers.go | 21 +++++++++++++++++++++ types.go | 18 ++++++++++++++++++ 5 files changed, 127 insertions(+), 1 deletion(-) diff --git a/bot.go b/bot.go index 3247f01..c6ca084 100644 --- a/bot.go +++ b/bot.go @@ -548,7 +548,7 @@ func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) resp, err := bot.MakeRequest(config.method(), params) if err != nil { - return StickerSet{}, nil + return StickerSet{}, err } var stickers StickerSet @@ -556,3 +556,21 @@ func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) return stickers, err } + +// StopPoll stops a poll and returns the result. +func (bot *BotAPI) StopPoll(config StopPollConfig) (Poll, error) { + params, err := config.params() + if err != nil { + return Poll{}, err + } + + resp, err := bot.MakeRequest(config.method(), params) + if err != nil { + return Poll{}, err + } + + var poll Poll + err = json.Unmarshal(resp.Result, &poll) + + return poll, err +} diff --git a/bot_test.go b/bot_test.go index 137f9b9..fe1fd55 100644 --- a/bot_test.go +++ b/bot_test.go @@ -700,3 +700,36 @@ func TestUnpinChatMessage(t *testing.T) { t.Fail() } } + +func TestPolls(t *testing.T) { + bot, _ := getBot(t) + + poll := NewPoll(SupergroupChatID, "Are polls working?", "Yes", "No") + + msg, err := bot.Send(poll) + if err != nil { + t.Error(err) + t.Fail() + } + + result, err := bot.StopPoll(NewStopPoll(SupergroupChatID, msg.MessageID)) + if err != nil { + t.Error(err) + t.Fail() + } + + if result.Question != "Are polls working?" { + t.Error("Poll question did not match") + t.Fail() + } + + if !result.IsClosed { + t.Error("Poll did not end") + t.Fail() + } + + if result.Options[0].Text != "Yes" || result.Options[0].VoterCount != 0 || result.Options[1].Text != "No" || result.Options[1].VoterCount != 0 { + t.Error("Poll options were incorrect") + t.Fail() + } +} diff --git a/configs.go b/configs.go index 08a2609..0258d74 100644 --- a/configs.go +++ b/configs.go @@ -499,6 +499,29 @@ func (config ContactConfig) method() string { return "sendContact" } +// SendPollConfig allows you to send a poll. +type SendPollConfig struct { + BaseChat + Question string + Options []string +} + +func (config SendPollConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params["question"] = config.Question + err = params.AddInterface("options", config.Options) + + return params, err +} + +func (SendPollConfig) method() string { + return "sendPoll" +} + // GameConfig allows you to send a game. type GameConfig struct { BaseChat @@ -671,6 +694,19 @@ 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 { diff --git a/helpers.go b/helpers.go index fea373d..b97aa2a 100644 --- a/helpers.go +++ b/helpers.go @@ -814,3 +814,24 @@ func NewDeleteChatPhoto(chatID int64, photo interface{}) 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, + } +} + +// NewStopPoll allows you to stop a poll. +func NewStopPoll(chatID int64, messageID int) StopPollConfig { + return StopPollConfig{ + BaseEdit{ + ChatID: chatID, + MessageID: messageID, + }, + } +} diff --git a/types.go b/types.go index 8a5326a..a301c3b 100644 --- a/types.go +++ b/types.go @@ -37,6 +37,7 @@ type Update struct { CallbackQuery *CallbackQuery `json:"callback_query"` ShippingQuery *ShippingQuery `json:"shipping_query"` PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"` + Poll *Poll `json:"poll"` } // UpdatesChannel is the channel for getting updates. @@ -141,6 +142,7 @@ type Message struct { ForwardFromChat *Chat `json:"forward_from_chat"` // optional ForwardFromMessageID int `json:"forward_from_message_id"` // optional ForwardSignature string `json:"forward_signature"` // optional + ForwardSenderName string `json:"forward_sender_name"` // optional ForwardDate int `json:"forward_date"` // optional ReplyToMessage *Message `json:"reply_to_message"` // optional EditDate int `json:"edit_date"` // optional @@ -162,6 +164,7 @@ type Message struct { Contact *Contact `json:"contact"` // optional Location *Location `json:"location"` // optional Venue *Venue `json:"venue"` // optional + Poll *Poll `json:"poll"` // optional NewChatMembers []User `json:"new_chat_members"` // optional LeftChatMember *User `json:"left_chat_member"` // optional NewChatTitle string `json:"new_chat_title"` // optional @@ -385,6 +388,20 @@ type Venue struct { FoursquareID string `json:"foursquare_id"` // optional } +// PollOption contains information about one answer option in a poll. +type PollOption struct { + Text string `json:"text"` + VoterCount int `json:"voter_count"` +} + +// Poll contains information about a poll. +type Poll struct { + ID string `json:"id"` + Question string `json:"question"` + Options []PollOption `json:"options"` + IsClosed bool `json:"is_closed"` +} + // UserProfilePhotos contains a set of user profile photos. type UserProfilePhotos struct { TotalCount int `json:"total_count"` @@ -487,6 +504,7 @@ type ChatMember struct { CanRestrictMembers bool `json:"can_restrict_members,omitempty"` // optional CanPinMessages bool `json:"can_pin_messages,omitempty"` // optional CanPromoteMembers bool `json:"can_promote_members,omitempty"` // optional + IsChatMember bool `json:"is_member"` // 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