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,11 +247,44 @@ func (bot *BotAPI) IsMessageToMe(message Message) bool { // // It requires the Chattable to send. func (bot *BotAPI) Send(c Chattable) (Message, error) { - switch c.(type) { + 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. +func (bot *BotAPI) Request(c Chattable) (APIResponse, error) { + switch t := c.(type) { case Fileable: - return bot.sendFile(c.(Fileable)) + if t.useExistingFile() { + v, err := t.values() + if err != nil { + return APIResponse{}, err + } + + return bot.MakeRequest(t.method(), v) + } + + p, err := t.params() + if err != nil { + return APIResponse{}, err + } + + return bot.UploadFile(t.method(), p, t.name(), t.getFile()) default: - return bot.sendChattable(c) + v, err := c.values() + if err != nil { + return APIResponse{}, err + } + + return bot.MakeRequest(c.method(), v) } } @@ -280,70 +298,6 @@ func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) { } } -// sendExisting will send a Message with an existing file to Telegram. -func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) { - v, err := config.values() - - if err != nil { - return Message{}, err - } - - message, err := bot.makeMessageRequest(method, v) - if err != nil { - return Message{}, err - } - - return message, nil -} - -// uploadAndSend will send a Message with a new file to Telegram. -func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) { - params, err := config.params() - if err != nil { - return Message{}, err - } - - file := config.getFile() - - resp, err := bot.UploadFile(method, params, config.name(), file) - if err != nil { - return Message{}, err - } - - var message Message - json.Unmarshal(resp.Result, &message) - - bot.debugLog(method, nil, message) - - return message, nil -} - -// sendFile determines if the file is using an existing file or uploading -// a new file, then sends it as needed. -func (bot *BotAPI) sendFile(config Fileable) (Message, error) { - if config.useExistingFile() { - return bot.sendExisting(config.method(), config) - } - - return bot.uploadAndSend(config.method(), config) -} - -// sendChattable sends a Chattable. -func (bot *BotAPI) sendChattable(config Chattable) (Message, error) { - v, err := config.values() - if err != nil { - return Message{}, err - } - - message, err := bot.makeMessageRequest(config.method(), v) - - if err != nil { - return Message{}, err - } - - return message, nil -} - // GetUserProfilePhotos gets a user's profile photos. // // It requires UserID. @@ -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"` +}