diff --git a/.gitignore b/.gitignore index aa7ac80..c875660 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ coverage.out +debug.test \ No newline at end of file diff --git a/bot.go b/bot.go index e4f0452..01cc0a6 100644 --- a/bot.go +++ b/bot.go @@ -674,14 +674,16 @@ func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) } // UnbanChatMember unbans a user from a chat. Note that this only will work -// in supergroups, and requires the bot to be an admin. +// 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", strconv.FormatInt(config.ChatID, 10)) - } else { + 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)) @@ -704,3 +706,51 @@ 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) +} diff --git a/bot_test.go b/bot_test.go index b026978..e4c31e2 100644 --- a/bot_test.go +++ b/bot_test.go @@ -616,3 +616,22 @@ func ExampleAnswerInlineQuery() { } } } + +func TestDeleteMessage(t *testing.T) { + bot, _ := getBot(t) + + msg := tgbotapi.NewMessage(ChatID, "A test message from the test library in telegram-bot-api") + msg.ParseMode = "markdown" + message, _ := bot.Send(msg) + + deleteMessageConfig := tgbotapi.DeleteMessageConfig{ + ChatID: message.Chat.ID, + MessageID: message.MessageID, + } + _, err := bot.DeleteMessage(deleteMessageConfig) + + if err != nil { + t.Error(err) + t.Fail() + } +} diff --git a/configs.go b/configs.go index 7fd8fd8..9d10655 100644 --- a/configs.go +++ b/configs.go @@ -902,6 +902,7 @@ type CallbackConfig struct { type ChatMemberConfig struct { ChatID int64 SuperGroupUsername string + ChannelUsername string UserID int } @@ -918,3 +919,109 @@ type ChatConfigWithUser struct { SuperGroupUsername string UserID int } + +// InvoiceConfig contains information for sendInvoice request. +type InvoiceConfig struct { + BaseChat + Title string // required + Description string // required + Payload string // required + ProviderToken string // required + StartParameter string // required + Currency string // required + Prices *[]LabeledPrice // required + PhotoURL string + PhotoSize int + PhotoWidth int + PhotoHeight int + NeedName bool + NeedPhoneNumber bool + NeedEmail bool + NeedShippingAddress bool + IsFlexible bool +} + +func (config InvoiceConfig) values() (url.Values, error) { + v, err := config.BaseChat.values() + if err != nil { + return v, err + } + v.Add("title", config.Title) + v.Add("description", config.Description) + v.Add("payload", config.Payload) + v.Add("provider_token", config.ProviderToken) + v.Add("start_parameter", config.StartParameter) + v.Add("currency", config.Currency) + data, err := json.Marshal(config.Prices) + if err != nil { + return v, err + } + v.Add("prices", string(data)) + if config.PhotoURL != "" { + v.Add("photo_url", config.PhotoURL) + } + if config.PhotoSize != 0 { + v.Add("photo_size", strconv.Itoa(config.PhotoSize)) + } + if config.PhotoWidth != 0 { + v.Add("photo_width", strconv.Itoa(config.PhotoWidth)) + } + if config.PhotoHeight != 0 { + v.Add("photo_height", strconv.Itoa(config.PhotoHeight)) + } + if config.NeedName != false { + v.Add("need_name", strconv.FormatBool(config.NeedName)) + } + if config.NeedPhoneNumber != false { + v.Add("need_phone_number", strconv.FormatBool(config.NeedPhoneNumber)) + } + if config.NeedEmail != false { + v.Add("need_email", strconv.FormatBool(config.NeedEmail)) + } + if config.NeedShippingAddress != false { + v.Add("need_shipping_address", strconv.FormatBool(config.NeedShippingAddress)) + } + if config.IsFlexible != false { + v.Add("is_flexible", strconv.FormatBool(config.IsFlexible)) + } + + return v, nil +} + +func (config InvoiceConfig) method() string { + return "sendInvoice" +} + +// ShippingConfig contains information for answerShippingQuery request. +type ShippingConfig struct { + ShippingQueryID string // required + OK bool // required + ShippingOptions *[]ShippingOption + ErrorMessage string +} + +// PreCheckoutConfig conatins information for answerPreCheckoutQuery request. +type PreCheckoutConfig struct { + PreCheckoutQueryID string // required + OK bool // required + ErrorMessage string +} + +// DeleteMessageConfig contains information of a message in a chat to delete. +type DeleteMessageConfig struct { + ChatID int64 + MessageID int +} + +func (config DeleteMessageConfig) method() string { + return "deleteMessage" +} + +func (config DeleteMessageConfig) values() (url.Values, error) { + v := url.Values{} + + v.Add("chat_id", strconv.FormatInt(config.ChatID, 10)) + v.Add("message_id", strconv.Itoa(config.MessageID)) + + return v, nil +} diff --git a/debug.test b/debug.test deleted file mode 100755 index 5c11e8e..0000000 Binary files a/debug.test and /dev/null differ diff --git a/helpers.go b/helpers.go index aacc4d9..c2e5fb7 100644 --- a/helpers.go +++ b/helpers.go @@ -648,3 +648,16 @@ func NewCallbackWithAlert(id, text string) CallbackConfig { ShowAlert: true, } } + +// NewInvoice created a new Invoice request to the user. +func NewInvoice(chatID int64, title, description, payload, providerToken, startParameter, currency string, prices *[]LabeledPrice) InvoiceConfig { + return InvoiceConfig{ + BaseChat: BaseChat{ChatID: chatID}, + Title: title, + Description: description, + Payload: payload, + ProviderToken: providerToken, + StartParameter: startParameter, + Currency: currency, + Prices: prices} +} diff --git a/types.go b/types.go index 31f70e0..10aa312 100644 --- a/types.go +++ b/types.go @@ -35,6 +35,8 @@ type Update struct { InlineQuery *InlineQuery `json:"inline_query"` ChosenInlineResult *ChosenInlineResult `json:"chosen_inline_result"` CallbackQuery *CallbackQuery `json:"callback_query"` + ShippingQuery *ShippingQuery `json:"shipping_query"` + PreCheckoutQuery *PreCheckoutQuery `json:"pre_checkout_query"` } // UpdatesChannel is the channel for getting updates. @@ -49,10 +51,11 @@ func (ch UpdatesChannel) Clear() { // User is a user on Telegram. type User struct { - ID int `json:"id"` - FirstName string `json:"first_name"` - LastName string `json:"last_name"` // optional - UserName string `json:"username"` // optional + ID int `json:"id"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` // optional + UserName string `json:"username"` // optional + LanguageCode string `json:"language_code"` // optional } // String displays a simple text version of a user. @@ -117,41 +120,43 @@ func (c Chat) ChatConfig() ChatConfig { // Message is returned by almost every request, and contains data about // almost anything. type Message struct { - MessageID int `json:"message_id"` - From *User `json:"from"` // optional - Date int `json:"date"` - Chat *Chat `json:"chat"` - ForwardFrom *User `json:"forward_from"` // optional - ForwardFromChat *Chat `json:"forward_from_chat"` // optional - ForwardFromMessageID int `json:"forward_from_message_id"` // optional - ForwardDate int `json:"forward_date"` // optional - ReplyToMessage *Message `json:"reply_to_message"` // optional - EditDate int `json:"edit_date"` // optional - Text string `json:"text"` // optional - Entities *[]MessageEntity `json:"entities"` // optional - Audio *Audio `json:"audio"` // optional - Document *Document `json:"document"` // optional - Game *Game `json:"game"` // optional - Photo *[]PhotoSize `json:"photo"` // optional - Sticker *Sticker `json:"sticker"` // optional - Video *Video `json:"video"` // optional - VideoNote *VideoNote `json:"video_note"` // optional - Voice *Voice `json:"voice"` // optional - Caption string `json:"caption"` // optional - Contact *Contact `json:"contact"` // optional - Location *Location `json:"location"` // optional - Venue *Venue `json:"venue"` // optional - NewChatMember *User `json:"new_chat_member"` // optional - LeftChatMember *User `json:"left_chat_member"` // optional - NewChatTitle string `json:"new_chat_title"` // optional - NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // optional - DeleteChatPhoto bool `json:"delete_chat_photo"` // optional - GroupChatCreated bool `json:"group_chat_created"` // optional - SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional - ChannelChatCreated bool `json:"channel_chat_created"` // optional - MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional - MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional - PinnedMessage *Message `json:"pinned_message"` // optional + MessageID int `json:"message_id"` + From *User `json:"from"` // optional + Date int `json:"date"` + Chat *Chat `json:"chat"` + ForwardFrom *User `json:"forward_from"` // optional + ForwardFromChat *Chat `json:"forward_from_chat"` // optional + ForwardFromMessageID int `json:"forward_from_message_id"` // optional + ForwardDate int `json:"forward_date"` // optional + ReplyToMessage *Message `json:"reply_to_message"` // optional + EditDate int `json:"edit_date"` // optional + Text string `json:"text"` // optional + Entities *[]MessageEntity `json:"entities"` // optional + Audio *Audio `json:"audio"` // optional + Document *Document `json:"document"` // optional + Game *Game `json:"game"` // optional + Photo *[]PhotoSize `json:"photo"` // optional + Sticker *Sticker `json:"sticker"` // optional + Video *Video `json:"video"` // optional + VideoNote *VideoNote `json:"video_note"` // optional + Voice *Voice `json:"voice"` // optional + Caption string `json:"caption"` // optional + Contact *Contact `json:"contact"` // optional + Location *Location `json:"location"` // optional + Venue *Venue `json:"venue"` // optional + NewChatMembers *[]User `json:"new_chat_members"` // optional + LeftChatMember *User `json:"left_chat_member"` // optional + NewChatTitle string `json:"new_chat_title"` // optional + NewChatPhoto *[]PhotoSize `json:"new_chat_photo"` // optional + DeleteChatPhoto bool `json:"delete_chat_photo"` // optional + GroupChatCreated bool `json:"group_chat_created"` // optional + SuperGroupChatCreated bool `json:"supergroup_chat_created"` // optional + ChannelChatCreated bool `json:"channel_chat_created"` // optional + MigrateToChatID int64 `json:"migrate_to_chat_id"` // optional + MigrateFromChatID int64 `json:"migrate_from_chat_id"` // optional + PinnedMessage *Message `json:"pinned_message"` // optional + Invoice *Invoice `json:"invoice"` // optional + SuccessfulPayment *SuccessfulPayment `json:"successful_payment"` // optional } // Time converts the message timestamp into a Time. @@ -371,6 +376,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"` } // CallbackQuery is data sent when a keyboard button with callback data @@ -503,6 +509,7 @@ type InlineQueryResultGIF struct { URL string `json:"gif_url"` // required Width int `json:"gif_width"` Height int `json:"gif_height"` + Duration int `json:"gif_duration"` ThumbURL string `json:"thumb_url"` Title string `json:"title"` Caption string `json:"caption"` @@ -517,6 +524,7 @@ type InlineQueryResultMPEG4GIF struct { URL string `json:"mpeg4_url"` // required Width int `json:"mpeg4_width"` Height int `json:"mpeg4_height"` + Duration int `json:"mpeg4_duration"` ThumbURL string `json:"thumb_url"` Title string `json:"title"` Caption string `json:"caption"` @@ -645,3 +653,73 @@ type InputContactMessageContent struct { FirstName string `json:"first_name"` LastName string `json:"last_name"` } + +// Invoice contains basic information about an invoice. +type Invoice struct { + Title string `json:"title"` + Description string `json:"description"` + StartParameter string `json:"start_parameter"` + Currency string `json:"currency"` + TotalAmount int `json:"total_amount"` +} + +// LabeledPrice represents a portion of the price for goods or services. +type LabeledPrice struct { + Label string `json:"label"` + Amount int `json:"amount"` +} + +// ShippingAddress represents a shipping address. +type ShippingAddress struct { + CountryCode string `json:"country_code"` + State string `json:"state"` + City string `json:"city"` + StreetLine1 string `json:"street_line1"` + StreetLine2 string `json:"street_line2"` + PostCode string `json:"post_code"` +} + +// OrderInfo represents information about an order. +type OrderInfo struct { + Name string `json:"name,omitempty"` + PhoneNumber string `json:"phone_number,omitempty"` + Email string `json:"email,omitempty"` + ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"` +} + +// ShippingOption represents one shipping option. +type ShippingOption struct { + ID string `json:"id"` + Title string `json:"title"` + Prices *[]LabeledPrice `json:"prices"` +} + +// SuccessfulPayment contains basic information about a successful payment. +type SuccessfulPayment struct { + Currency string `json:"currency"` + TotalAmount int `json:"total_amount"` + InvoicePayload string `json:"invoice_payload"` + ShippingOptionID string `json:"shipping_option_id,omitempty"` + OrderInfo *OrderInfo `json:"order_info,omitempty"` + TelegramPaymentChargeID string `json:"telegram_payment_charge_id"` + ProviderPaymentChargeID string `json:"provider_payment_charge_id"` +} + +// ShippingQuery contains information about an incoming shipping query. +type ShippingQuery struct { + ID string `json:"id"` + From *User `json:"from"` + InvoicePayload string `json:"invoice_payload"` + ShippingAddress *ShippingAddress `json:"shipping_address"` +} + +// PreCheckoutQuery contains information about an incoming pre-checkout query. +type PreCheckoutQuery struct { + ID string `json:"id"` + From *User `json:"from"` + Currency string `json:"currency"` + TotalAmount int `json:"total_amount"` + InvoicePayload string `json:"invoice_payload"` + ShippingOptionID string `json:"shipping_option_id,omitempty"` + OrderInfo *OrderInfo `json:"order_info,omitempty"` +} diff --git a/types_test.go b/types_test.go index 5ef21a9..d2c24d4 100644 --- a/types_test.go +++ b/types_test.go @@ -1,13 +1,14 @@ package tgbotapi_test import ( - "github.com/go-telegram-bot-api/telegram-bot-api" "testing" "time" + + "github.com/go-telegram-bot-api/telegram-bot-api" ) func TestUserStringWith(t *testing.T) { - user := tgbotapi.User{0, "Test", "Test", ""} + user := tgbotapi.User{0, "Test", "Test", "", "en"} if user.String() != "Test Test" { t.Fail() @@ -15,7 +16,7 @@ func TestUserStringWith(t *testing.T) { } func TestUserStringWithUserName(t *testing.T) { - user := tgbotapi.User{0, "Test", "Test", "@test"} + user := tgbotapi.User{0, "Test", "Test", "@test", "en"} if user.String() != "@test" { t.Fail()