From ac5306ce0c6d68946d7d5a029c0338e758b5fc96 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Thu, 5 Nov 2020 16:53:37 -0500 Subject: [PATCH 1/4] Updates for Bot API 5.0. --- bot_test.go | 8 +- configs.go | 245 ++++++++++++++++++++++++++++++++++++++++---------- types.go | 244 +++++++++++++++++++++++++++++++------------------ types_test.go | 2 +- 4 files changed, 358 insertions(+), 141 deletions(-) diff --git a/bot_test.go b/bot_test.go index b490d8d..db0c335 100644 --- a/bot_test.go +++ b/bot_test.go @@ -477,7 +477,7 @@ func TestSetWebhookWithCert(t *testing.T) { time.Sleep(time.Second * 2) - bot.Request(RemoveWebhookConfig{}) + bot.Request(DeleteWebhookConfig{}) wh := NewWebhookWithCert("https://example.com/tgbotapi-test/"+bot.Token, "tests/cert.pem") _, err := bot.Request(wh) @@ -491,7 +491,7 @@ func TestSetWebhookWithCert(t *testing.T) { t.Error(err) } - bot.Request(RemoveWebhookConfig{}) + bot.Request(DeleteWebhookConfig{}) } func TestSetWebhookWithoutCert(t *testing.T) { @@ -499,7 +499,7 @@ func TestSetWebhookWithoutCert(t *testing.T) { time.Sleep(time.Second * 2) - bot.Request(RemoveWebhookConfig{}) + bot.Request(DeleteWebhookConfig{}) wh := NewWebhook("https://example.com/tgbotapi-test/" + bot.Token) _, err := bot.Request(wh) @@ -519,7 +519,7 @@ func TestSetWebhookWithoutCert(t *testing.T) { t.Errorf("failed to set webhook: %s", info.LastErrorMessage) } - bot.Request(RemoveWebhookConfig{}) + bot.Request(DeleteWebhookConfig{}) } func TestSendWithMediaGroup(t *testing.T) { diff --git a/configs.go b/configs.go index 939522b..bd07203 100644 --- a/configs.go +++ b/configs.go @@ -63,13 +63,41 @@ type Fileable interface { useExistingFile() bool } +// LogOutConfig is a request to log out of the cloud Bot API server. +// +// Note that you may not log back in for at least 10 minutes. +type LogOutConfig struct{} + +func (LogOutConfig) method() string { + return "logOut" +} + +func (LogOutConfig) params() (Params, error) { + return nil, nil +} + +// CloseConfig is a request to close the bot instance on a local server. +// +// Note that you may not close an instance for the first 10 minutes after the +// bot has started. +type CloseConfig struct{} + +func (CloseConfig) method() string { + return "close" +} + +func (CloseConfig) params() (Params, error) { + return nil, nil +} + // BaseChat is base type for all chat config types. type BaseChat struct { - ChatID int64 // required - ChannelUsername string - ReplyToMessageID int - ReplyMarkup interface{} - DisableNotification bool + ChatID int64 // required + ChannelUsername string + ReplyToMessageID int + ReplyMarkup interface{} + DisableNotification bool + AllowSendingWithoutReply bool } func (chat *BaseChat) params() (Params, error) { @@ -78,6 +106,7 @@ func (chat *BaseChat) params() (Params, error) { params.AddFirstValid("chat_id", chat.ChatID, chat.ChannelUsername) params.AddNonZero("reply_to_message_id", chat.ReplyToMessageID) params.AddBool("disable_notification", chat.DisableNotification) + params.AddBool("allow_sending_without_reply", chat.AllowSendingWithoutReply) err := params.AddInterface("reply_markup", chat.ReplyMarkup) @@ -140,6 +169,7 @@ type MessageConfig struct { BaseChat Text string ParseMode string + Entities []MessageEntity DisableWebPagePreview bool } @@ -152,8 +182,9 @@ func (config MessageConfig) params() (Params, error) { params.AddNonEmpty("text", config.Text) params.AddBool("disable_web_page_preview", config.DisableWebPagePreview) params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("entities", config.Entities) - return params, nil + return params, err } func (config MessageConfig) method() string { @@ -184,19 +215,50 @@ func (config ForwardConfig) method() string { return "forwardMessage" } +// CopyMessageConfig contains information about a copyMessage request. +type CopyMessageConfig struct { + BaseChat + FromChatID int64 + FromChannelUsername string + MessageID int + Caption string + ParseMode string + CaptionEntities []MessageEntity +} + +func (config CopyMessageConfig) params() (Params, error) { + params, err := config.BaseChat.params() + if err != nil { + return params, err + } + + params.AddFirstValid("from_chat_id", config.FromChatID, config.FromChannelUsername) + params.AddNonZero("message_id", config.MessageID) + params.AddNonEmpty("caption", config.Caption) + params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) + + return params, err +} + // PhotoConfig contains information about a SendPhoto request. type PhotoConfig struct { BaseFile - Caption string - ParseMode string + Caption string + ParseMode string + CaptionEntities []MessageEntity } func (config PhotoConfig) params() (Params, error) { params, err := config.BaseFile.params() + if err != nil { + return params, err + } params.AddNonEmpty(config.name(), config.FileID) params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) return params, err } @@ -212,11 +274,12 @@ func (config PhotoConfig) method() string { // AudioConfig contains information about a SendAudio request. type AudioConfig struct { BaseFile - Caption string - ParseMode string - Duration int - Performer string - Title string + Caption string + ParseMode string + CaptionEntities []MessageEntity + Duration int + Performer string + Title string } func (config AudioConfig) params() (Params, error) { @@ -231,8 +294,9 @@ func (config AudioConfig) params() (Params, error) { params.AddNonEmpty("title", config.Title) params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) - return params, nil + return params, err } func (config AudioConfig) name() string { @@ -246,8 +310,10 @@ func (config AudioConfig) method() string { // DocumentConfig contains information about a SendDocument request. type DocumentConfig struct { BaseFile - Caption string - ParseMode string + Caption string + ParseMode string + CaptionEntities []MessageEntity + DisableContentTypeDetection bool } func (config DocumentConfig) params() (Params, error) { @@ -256,6 +322,7 @@ func (config DocumentConfig) params() (Params, error) { params.AddNonEmpty(config.name(), config.FileID) params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) + params.AddBool("disable_content_type_detection", config.DisableContentTypeDetection) return params, err } @@ -295,17 +362,22 @@ type VideoConfig struct { Duration int Caption string ParseMode string + CaptionEntities []MessageEntity SupportsStreaming bool } func (config VideoConfig) params() (Params, error) { params, err := config.BaseChat.params() + if err != nil { + return params, err + } params.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) + err = params.AddInterface("caption_entities", config.CaptionEntities) return params, err } @@ -321,18 +393,23 @@ func (config VideoConfig) method() string { // AnimationConfig contains information about a SendAnimation request. type AnimationConfig struct { BaseFile - Duration int - Caption string - ParseMode string + Duration int + Caption string + ParseMode string + CaptionEntities []MessageEntity } func (config AnimationConfig) params() (Params, error) { params, err := config.BaseChat.params() + if err != nil { + return params, err + } params.AddNonEmpty(config.name(), config.FileID) params.AddNonZero("duration", config.Duration) params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) return params, err } @@ -373,18 +450,23 @@ func (config VideoNoteConfig) method() string { // VoiceConfig contains information about a SendVoice request. type VoiceConfig struct { BaseFile - Caption string - ParseMode string - Duration int + Caption string + ParseMode string + CaptionEntities []MessageEntity + Duration int } func (config VoiceConfig) params() (Params, error) { params, err := config.BaseChat.params() + if err != nil { + return params, err + } params.AddNonEmpty(config.name(), config.FileID) params.AddNonZero("duration", config.Duration) params.AddNonEmpty("caption", config.Caption) params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) return params, err } @@ -400,9 +482,12 @@ func (config VoiceConfig) method() string { // LocationConfig contains information about a SendLocation request. type LocationConfig struct { BaseChat - Latitude float64 // required - Longitude float64 // required - LivePeriod int // optional + Latitude float64 // required + Longitude float64 // required + HorizontalAccuracy float64 // optional + LivePeriod int // optional + Heading int // optional + ProximityAlertRadius int // optional } func (config LocationConfig) params() (Params, error) { @@ -410,7 +495,10 @@ func (config LocationConfig) params() (Params, error) { params.AddNonZeroFloat("latitude", config.Latitude) params.AddNonZeroFloat("longitude", config.Longitude) + params.AddNonZeroFloat("horizontal_accuracy", config.HorizontalAccuracy) params.AddNonZero("live_period", config.LivePeriod) + params.AddNonZero("heading", config.Heading) + params.AddNonZero("proximity_alert_radius", config.ProximityAlertRadius) return params, err } @@ -422,8 +510,11 @@ func (config LocationConfig) method() string { // EditMessageLiveLocationConfig allows you to update a live location. type EditMessageLiveLocationConfig struct { BaseEdit - Latitude float64 // required - Longitude float64 // required + Latitude float64 // required + Longitude float64 // required + HorizontalAccuracy float64 // optional + Heading int // optional + ProximityAlertRadius int // optional } func (config EditMessageLiveLocationConfig) params() (Params, error) { @@ -431,6 +522,9 @@ func (config EditMessageLiveLocationConfig) params() (Params, error) { params.AddNonZeroFloat("latitude", config.Latitude) params.AddNonZeroFloat("longitude", config.Longitude) + params.AddNonZeroFloat("horizontal_accuracy", config.HorizontalAccuracy) + params.AddNonZero("heading", config.Heading) + params.AddNonZero("proximity_alert_radius", config.ProximityAlertRadius) return params, err } @@ -455,11 +549,14 @@ func (config StopMessageLiveLocationConfig) method() string { // VenueConfig contains information about a SendVenue request. type VenueConfig struct { BaseChat - Latitude float64 // required - Longitude float64 // required - Title string // required - Address string // required - FoursquareID string + Latitude float64 // required + Longitude float64 // required + Title string // required + Address string // required + FoursquareID string + FoursquareType string + GooglePlaceID string + GooglePlaceType string } func (config VenueConfig) params() (Params, error) { @@ -470,6 +567,9 @@ func (config VenueConfig) params() (Params, error) { params["title"] = config.Title params["address"] = config.Address params.AddNonEmpty("foursquare_id", config.FoursquareID) + params.AddNonEmpty("foursquare_type", config.FoursquareType) + params.AddNonEmpty("google_place_id", config.GooglePlaceID) + params.AddNonEmpty("google_place_type", config.GooglePlaceType) return params, err } @@ -514,6 +614,7 @@ type SendPollConfig struct { CorrectOptionID int64 Explanation string ExplanationParseMode string + ExplanationEntities []MessageEntity OpenPeriod int CloseDate int IsClosed bool @@ -526,7 +627,9 @@ func (config SendPollConfig) params() (Params, error) { } params["question"] = config.Question - err = params.AddInterface("options", config.Options) + if err = params.AddInterface("options", config.Options); err != nil { + return params, err + } params["is_anonymous"] = strconv.FormatBool(config.IsAnonymous) params.AddNonEmpty("type", config.Type) params["allows_multiple_answers"] = strconv.FormatBool(config.AllowsMultipleAnswers) @@ -536,6 +639,7 @@ func (config SendPollConfig) params() (Params, error) { params.AddNonEmpty("explanation_parse_mode", config.ExplanationParseMode) params.AddNonZero("open_period", config.OpenPeriod) params.AddNonZero("close_date", config.CloseDate) + err = params.AddInterface("explanation_entities", config.ExplanationEntities) return params, err } @@ -646,15 +750,20 @@ type EditMessageTextConfig struct { BaseEdit Text string ParseMode string + Entities []MessageEntity DisableWebPagePreview bool } func (config EditMessageTextConfig) params() (Params, error) { params, err := config.BaseEdit.params() + if err != nil { + return params, err + } params["text"] = config.Text params.AddNonEmpty("parse_mode", config.ParseMode) params.AddBool("disable_web_page_preview", config.DisableWebPagePreview) + err = params.AddInterface("entities", config.Entities) return params, err } @@ -666,15 +775,20 @@ func (config EditMessageTextConfig) method() string { // EditMessageCaptionConfig allows you to modify the caption of a message. type EditMessageCaptionConfig struct { BaseEdit - Caption string - ParseMode string + Caption string + ParseMode string + CaptionEntities []MessageEntity } func (config EditMessageCaptionConfig) params() (Params, error) { params, err := config.BaseEdit.params() + if err != nil { + return params, err + } params["caption"] = config.Caption params.AddNonEmpty("parse_mode", config.ParseMode) + err = params.AddInterface("caption_entities", config.CaptionEntities) return params, err } @@ -791,10 +905,12 @@ func (config UpdateConfig) params() (Params, error) { // WebhookConfig contains information about a SetWebhook request. type WebhookConfig struct { - URL *url.URL - Certificate interface{} - MaxConnections int - AllowedUpdates []string + URL *url.URL + Certificate interface{} + IPAddress string + MaxConnections int + AllowedUpdates []string + DropPendingUpdates bool } func (config WebhookConfig) method() string { @@ -808,8 +924,10 @@ func (config WebhookConfig) params() (Params, error) { params["url"] = config.URL.String() } + params.AddNonEmpty("ip_address", config.IPAddress) params.AddNonZero("max_connections", config.MaxConnections) params.AddInterface("allowed_updates", config.AllowedUpdates) + params.AddBool("drop_pending_updates", config.DropPendingUpdates) return params, nil } @@ -826,16 +944,21 @@ func (config WebhookConfig) useExistingFile() bool { return config.URL != nil } -// RemoveWebhookConfig is a helper to remove a webhook. -type RemoveWebhookConfig struct { +// DeleteWebhookConfig is a helper to delete a webhook. +type DeleteWebhookConfig struct { + DropPendingUpdates bool } -func (config RemoveWebhookConfig) method() string { - return "setWebhook" +func (config DeleteWebhookConfig) method() string { + return "deleteWebhook" } -func (config RemoveWebhookConfig) params() (Params, error) { - return nil, nil +func (config DeleteWebhookConfig) params() (Params, error) { + params := make(Params) + + params.AddBool("drop_pending_updates", config.DropPendingUpdates) + + return params, nil } // FileBytes contains information about a set of bytes to upload @@ -923,6 +1046,7 @@ type ChatMemberConfig struct { // UnbanChatMemberConfig allows you to unban a user. type UnbanChatMemberConfig struct { ChatMemberConfig + OnlyIfBanned bool } func (config UnbanChatMemberConfig) method() string { @@ -934,6 +1058,7 @@ func (config UnbanChatMemberConfig) params() (Params, error) { params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) params.AddNonZero("user_id", config.UserID) + params.AddBool("only_if_banned", config.OnlyIfBanned) return params, nil } @@ -986,6 +1111,7 @@ func (config RestrictChatMemberConfig) params() (Params, error) { // PromoteChatMemberConfig contains fields to promote members of chat type PromoteChatMemberConfig struct { ChatMemberConfig + IsAnonymous bool CanChangeInfo bool CanPostMessages bool CanEditMessages bool @@ -1006,6 +1132,7 @@ func (config PromoteChatMemberConfig) params() (Params, error) { params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername, config.ChannelUsername) params.AddNonZero("user_id", config.UserID) + params.AddBool("is_anonymous", config.IsAnonymous) params.AddBool("can_change_info", config.CanChangeInfo) params.AddBool("can_post_messages", config.CanPostMessages) params.AddBool("can_edit_messages", config.CanEditMessages) @@ -1281,10 +1408,13 @@ func (config PinChatMessageConfig) params() (Params, error) { return params, nil } -// UnpinChatMessageConfig contains information of chat to unpin. +// UnpinChatMessageConfig contains information of a chat message to unpin. +// +// If MessageID is not specified, it will unpin the most recent pin. type UnpinChatMessageConfig struct { ChatID int64 ChannelUsername string + MessageID int } func (config UnpinChatMessageConfig) method() string { @@ -1294,6 +1424,26 @@ func (config UnpinChatMessageConfig) method() string { func (config UnpinChatMessageConfig) params() (Params, error) { params := make(Params) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) + params.AddNonZero("message_id", config.MessageID) + + return params, nil +} + +// UnpinAllChatMessagesConfig contains information of all messages to unpin in +// a chat. +type UnpinAllChatMessagesConfig struct { + ChatID int64 + ChannelUsername string +} + +func (config UnpinAllChatMessagesConfig) method() string { + return "unpinAllChatMessages" +} + +func (config UnpinAllChatMessagesConfig) params() (Params, error) { + params := make(Params) + params.AddFirstValid("chat_id", config.ChatID, config.ChannelUsername) return params, nil @@ -1680,7 +1830,8 @@ func (config MediaGroupConfig) params() (Params, error) { // DiceConfig allows you to send a random dice roll to Telegram. // -// Emoji may be one of the following: 🎲 (1-6), 🎯 (1-6), 🏀 (1-5). +// Emoji may be one of the following: 🎲 (1-6), 🎯 (1-6), 🏀 (1-5), ⚽ (1-5), +// 🎰 (1-64). type DiceConfig struct { BaseChat diff --git a/types.go b/types.go index 95a871d..6e74d1a 100644 --- a/types.go +++ b/types.go @@ -121,6 +121,7 @@ type Chat struct { LastName string `json:"last_name,omitempty"` // optional AllMembersAreAdmins bool `json:"all_members_are_administrators,omitempty"` // deprecated, optional Photo *ChatPhoto `json:"photo,omitempty"` // optional + Bio string `json:"bio,omitempty"` // optional Description string `json:"description,omitempty"` // optional InviteLink string `json:"invite_link,omitempty"` // optional PinnedMessage *Message `json:"pinned_message,omitempty"` // optional @@ -128,6 +129,8 @@ type Chat struct { SlowModeDelay int `json:"slow_mode_delay,omitempty"` // optional StickerSetName string `json:"sticker_set_name,omitempty"` // optional CanSetStickerSet bool `json:"can_set_sticker_set,omitempty"` // optional + LinkedChatID int `json:"linked_chat_id,omitempty"` // optional + Location *ChatLocation `json:"location"` // optional } // IsPrivate returns if the Chat is a private conversation. @@ -158,55 +161,57 @@ 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,omitempty"` // optional - Date int `json:"date"` - Chat *Chat `json:"chat"` - ForwardFrom *User `json:"forward_from,omitempty"` // optional - ForwardFromChat *Chat `json:"forward_from_chat,omitempty"` // optional - ForwardFromMessageID int `json:"forward_from_message_id,omitempty"` // optional - ForwardSignature string `json:"forward_signature,omitempty"` // optional - ForwardSenderName string `json:"forward_sender_name,omitempty"` // optional - ForwardDate int `json:"forward_date,omitempty"` // optional - ReplyToMessage *Message `json:"reply_to_message,omitempty"` // optional - ViaBot *User `json:"via_bot"` // optional - EditDate int `json:"edit_date,omitempty"` // optional - MediaGroupID string `json:"media_group_id,omitempty"` // optional - AuthorSignature string `json:"author_signature,omitempty"` // optional - Text string `json:"text,omitempty"` // optional - Entities []MessageEntity `json:"entities,omitempty"` // optional - CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` // optional - Audio *Audio `json:"audio,omitempty"` // optional - Document *Document `json:"document,omitempty"` // optional - Animation *ChatAnimation `json:"animation,omitempty"` // optional - Game *Game `json:"game,omitempty"` // optional - Photo []PhotoSize `json:"photo,omitempty"` // optional - Sticker *Sticker `json:"sticker,omitempty"` // optional - Video *Video `json:"video,omitempty"` // optional - VideoNote *VideoNote `json:"video_note,omitempty"` // optional - Voice *Voice `json:"voice,omitempty"` // optional - Caption string `json:"caption,omitempty"` // optional - Contact *Contact `json:"contact,omitempty"` // optional - Location *Location `json:"location,omitempty"` // optional - Venue *Venue `json:"venue,omitempty"` // optional - Poll *Poll `json:"poll,omitempty"` // optional - Dice *Dice `json:"dice,omitempty"` // optional - NewChatMembers []User `json:"new_chat_members,omitempty"` // optional - LeftChatMember *User `json:"left_chat_member,omitempty"` // optional - NewChatTitle string `json:"new_chat_title,omitempty"` // optional - NewChatPhoto []PhotoSize `json:"new_chat_photo,omitempty"` // optional - DeleteChatPhoto bool `json:"delete_chat_photo,omitempty"` // optional - GroupChatCreated bool `json:"group_chat_created,omitempty"` // optional - SuperGroupChatCreated bool `json:"supergroup_chat_created,omitempty"` // optional - ChannelChatCreated bool `json:"channel_chat_created,omitempty"` // optional - MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` // optional - MigrateFromChatID int64 `json:"migrate_from_chat_id,omitempty"` // optional - PinnedMessage *Message `json:"pinned_message,omitempty"` // optional - Invoice *Invoice `json:"invoice,omitempty"` // optional - SuccessfulPayment *SuccessfulPayment `json:"successful_payment,omitempty"` // optional - ConnectedWebsite string `json:"connected_website,omitempty"` // optional - PassportData *PassportData `json:"passport_data,omitempty"` // optional - ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` // optional + MessageID int `json:"message_id"` + From *User `json:"from,omitempty"` // optional + SenderChat *Chat `json:"sender_chat,omitempty"` // optional + Date int `json:"date"` + Chat *Chat `json:"chat"` + ForwardFrom *User `json:"forward_from,omitempty"` // optional + ForwardFromChat *Chat `json:"forward_from_chat,omitempty"` // optional + ForwardFromMessageID int `json:"forward_from_message_id,omitempty"` // optional + ForwardSignature string `json:"forward_signature,omitempty"` // optional + ForwardSenderName string `json:"forward_sender_name,omitempty"` // optional + ForwardDate int `json:"forward_date,omitempty"` // optional + ReplyToMessage *Message `json:"reply_to_message,omitempty"` // optional + ViaBot *User `json:"via_bot"` // optional + EditDate int `json:"edit_date,omitempty"` // optional + MediaGroupID string `json:"media_group_id,omitempty"` // optional + AuthorSignature string `json:"author_signature,omitempty"` // optional + Text string `json:"text,omitempty"` // optional + Entities []MessageEntity `json:"entities,omitempty"` // optional + Audio *Audio `json:"audio,omitempty"` // optional + Document *Document `json:"document,omitempty"` // optional + Animation *ChatAnimation `json:"animation,omitempty"` // optional + Photo []PhotoSize `json:"photo,omitempty"` // optional + Sticker *Sticker `json:"sticker,omitempty"` // optional + Video *Video `json:"video,omitempty"` // optional + VideoNote *VideoNote `json:"video_note,omitempty"` // optional + Voice *Voice `json:"voice,omitempty"` // optional + Caption string `json:"caption,omitempty"` // optional + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` // optional + Contact *Contact `json:"contact,omitempty"` // optional + Dice *Dice `json:"dice,omitempty"` // optional + Game *Game `json:"game,omitempty"` // optional + Poll *Poll `json:"poll,omitempty"` // optional + Venue *Venue `json:"venue,omitempty"` // optional + Location *Location `json:"location,omitempty"` // optional + NewChatMembers []User `json:"new_chat_members,omitempty"` // optional + LeftChatMember *User `json:"left_chat_member,omitempty"` // optional + NewChatTitle string `json:"new_chat_title,omitempty"` // optional + NewChatPhoto []PhotoSize `json:"new_chat_photo,omitempty"` // optional + DeleteChatPhoto bool `json:"delete_chat_photo,omitempty"` // optional + GroupChatCreated bool `json:"group_chat_created,omitempty"` // optional + SuperGroupChatCreated bool `json:"supergroup_chat_created,omitempty"` // optional + ChannelChatCreated bool `json:"channel_chat_created,omitempty"` // optional + MigrateToChatID int64 `json:"migrate_to_chat_id,omitempty"` // optional + MigrateFromChatID int64 `json:"migrate_from_chat_id,omitempty"` // optional + PinnedMessage *Message `json:"pinned_message,omitempty"` // optional + Invoice *Invoice `json:"invoice,omitempty"` // optional + SuccessfulPayment *SuccessfulPayment `json:"successful_payment,omitempty"` // optional + ConnectedWebsite string `json:"connected_website,omitempty"` // optional + PassportData *PassportData `json:"passport_data,omitempty"` // optional + ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered"` // optional + ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` // optional } // Time converts the message timestamp into a Time. @@ -363,6 +368,7 @@ type Audio struct { Duration int `json:"duration"` Performer string `json:"performer,omitempty"` // optional Title string `json:"title,omitempty"` // optional + FileName string `json:"file_name,omitempty"` // optional MimeType string `json:"mime_type,omitempty"` // optional FileSize int `json:"file_size,omitempty"` // optional } @@ -427,6 +433,7 @@ type Video struct { Height int `json:"height"` Duration int `json:"duration"` Thumbnail *PhotoSize `json:"thumb,omitempty"` // optional + FileName string `json:"file_name,omitempty"` // optional MimeType string `json:"mime_type,omitempty"` // optional FileSize int `json:"file_size,omitempty"` // optional } @@ -463,16 +470,23 @@ 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"` + HorizontalAccuracy float64 `json:"horizontal_accuracy"` // optional + LivePeriod int `json:"live_period"` // optional + Heading int `json:"heading"` // optional + ProximityAlertRadius int `json:"proximity_alert_radius"` // optional } // Venue contains information about a venue, including its Location. type Venue struct { - Location Location `json:"location"` - Title string `json:"title"` - Address string `json:"address"` - FoursquareID string `json:"foursquare_id,omitempty"` // optional + Location Location `json:"location"` + Title string `json:"title"` + Address string `json:"address"` + FoursquareID string `json:"foursquare_id,omitempty"` // optional + FoursquareType string `json:"foursquare_type,omitempty"` // optional + GooglePlaceID string `json:"google_place_id,omitempty"` // optional + GooglePlaceType string `json:"google_place_type,omitempty"` // optional } // PollOption contains information about one answer option in a poll. @@ -510,6 +524,14 @@ type Dice struct { Value int `json:"value"` } +// ProximityAlertTriggered represents a service message sent when a user in the +// chat triggers a proximity alert sent by another user. +type ProximityAlertTriggered struct { + Traveler User `json:"traveler"` + Watcher User `json:"watcher"` + Distance int `json:"distance"` +} + // UserProfilePhotos contains a set of user profile photos. type UserProfilePhotos struct { TotalCount int `json:"total_count"` @@ -620,6 +642,7 @@ type ChatMember struct { User *User `json:"user"` Status string `json:"status"` CustomTitle string `json:"custom_title,omitempty"` // optional + IsAnonymous bool `json:"is_anonymous"` // optional UntilDate int64 `json:"until_date,omitempty"` // optional CanBeEdited bool `json:"can_be_edited,omitempty"` // optional CanPostMessages bool `json:"can_post_messages,omitempty"` // optional @@ -685,12 +708,14 @@ type CallbackGame struct{} // WebhookInfo is information about a currently set webhook. type WebhookInfo struct { - URL string `json:"url"` - HasCustomCertificate bool `json:"has_custom_certificate"` - PendingUpdateCount int `json:"pending_update_count"` - LastErrorDate int `json:"last_error_date"` // optional - LastErrorMessage string `json:"last_error_message"` // optional - MaxConnections int `json:"max_connections"` // optional + URL string `json:"url"` + HasCustomCertificate bool `json:"has_custom_certificate"` + PendingUpdateCount int `json:"pending_update_count"` + IPAddress string `json:"ip_address"` // optional + LastErrorDate int `json:"last_error_date"` // optional + LastErrorMessage string `json:"last_error_message"` // optional + MaxConnections int `json:"max_connections"` // optional + AllowedUpdates []string `json:"allowed_updates"` // optional } // IsSet returns true if a webhook is currently set. @@ -734,6 +759,8 @@ type InlineQueryResultPhoto struct { Title string `json:"title"` Description string `json:"description"` Caption string `json:"caption"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -747,6 +774,7 @@ type InlineQueryResultCachedPhoto struct { Description string `json:"description"` Caption string `json:"caption"` ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -763,6 +791,8 @@ type InlineQueryResultGIF struct { Duration int `json:"gif_duration,omitempty"` Title string `json:"title,omitempty"` Caption string `json:"caption,omitempty"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -776,6 +806,7 @@ type InlineQueryResultCachedGIF struct { Caption string `json:"caption"` ParseMode string `json:"parse_mode"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -791,6 +822,8 @@ type InlineQueryResultMPEG4GIF struct { ThumbMimeType string `json:"thumb_mime_type"` Title string `json:"title"` Caption string `json:"caption"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -804,6 +837,7 @@ type InlineQueryResultCachedMpeg4Gif struct { Title string `json:"title"` Caption string `json:"caption"` ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -817,6 +851,8 @@ type InlineQueryResultVideo struct { ThumbURL string `json:"thumb_url"` Title string `json:"title"` Caption string `json:"caption"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` Width int `json:"video_width"` Height int `json:"video_height"` Duration int `json:"video_duration"` @@ -834,6 +870,7 @@ type InlineQueryResultCachedVideo struct { Description string `json:"description"` Caption string `json:"caption"` ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -844,7 +881,6 @@ type InlineQueryResultCachedSticker struct { ID string `json:"id"` // required StickerID string `json:"sticker_file_id"` // required Title string `json:"title"` // required - ParseMode string `json:"parse_mode"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -856,6 +892,8 @@ type InlineQueryResultAudio struct { URL string `json:"audio_url"` // required Title string `json:"title"` // required Caption string `json:"caption"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` Performer string `json:"performer"` Duration int `json:"audio_duration"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` @@ -869,6 +907,7 @@ type InlineQueryResultCachedAudio struct { AudioID string `json:"audio_file_id"` // required Caption string `json:"caption"` ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -880,6 +919,8 @@ type InlineQueryResultVoice struct { URL string `json:"voice_url"` // required Title string `json:"title"` // required Caption string `json:"caption"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` Duration int `json:"voice_duration"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` @@ -893,6 +934,7 @@ type InlineQueryResultCachedVoice struct { Title string `json:"title"` // required Caption string `json:"caption"` ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } @@ -903,6 +945,8 @@ type InlineQueryResultDocument struct { ID string `json:"id"` // required Title string `json:"title"` // required Caption string `json:"caption"` + ParseMode string `json:"parse_mode,omitempty"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` URL string `json:"document_url"` // required MimeType string `json:"mime_type"` // required Description string `json:"description"` @@ -922,23 +966,27 @@ type InlineQueryResultCachedDocument struct { Caption string `json:"caption"` Description string `json:"description"` ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` } // InlineQueryResultLocation is an inline query response location. type InlineQueryResultLocation struct { - Type string `json:"type"` // required - ID string `json:"id"` // required - Latitude float64 `json:"latitude"` // required - Longitude float64 `json:"longitude"` // required - 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"` - ThumbWidth int `json:"thumb_width"` - ThumbHeight int `json:"thumb_height"` + 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 + HorizontalAccuracy float64 `json:"horizontal_accuracy,omitempty"` + Heading int `json:"heading"` + ProximityAlertRadius int `json:"proximity_alert_radius"` + 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"` } // InlineQueryResultContact is an inline query response contact. @@ -964,8 +1012,10 @@ type InlineQueryResultVenue struct { Longitude float64 `json:"longitude"` // required Title string `json:"title"` // required Address string `json:"address"` // required - FoursquareID string `json:"foursquare_id"` - FoursquareType string `json:"foursquare_type"` + FoursquareID string `json:"foursquare_id,omitempty"` + FoursquareType string `json:"foursquare_type,omitempty"` + GooglePlaceID string `json:"google_place_id,omitempty"` + GooglePlaceType string `json:"google_place_type,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` InputMessageContent interface{} `json:"input_message_content,omitempty"` ThumbURL string `json:"thumb_url"` @@ -993,26 +1043,34 @@ type ChosenInlineResult struct { // InputTextMessageContent contains text for displaying // as an inline query result. type InputTextMessageContent struct { - Text string `json:"message_text"` - ParseMode string `json:"parse_mode"` - DisableWebPagePreview bool `json:"disable_web_page_preview"` + Text string `json:"message_text"` + ParseMode string `json:"parse_mode"` + Entities []MessageEntity `json:"entities,omitempty"` + DisableWebPagePreview bool `json:"disable_web_page_preview"` } // InputLocationMessageContent contains a location for displaying // as an inline query result. type InputLocationMessageContent struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + HorizontalAccuracy float64 `json:"horizontal_accuracy"` + LivePeriod int `json:"live_period"` + Heading int `json:"heading"` + ProximityAlertRadius int `json:"proximity_alert_radius"` } // InputVenueMessageContent contains a venue for displaying // as an inline query result. type InputVenueMessageContent struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` - Title string `json:"title"` - Address string `json:"address"` - FoursquareID string `json:"foursquare_id"` + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` + Title string `json:"title"` + Address string `json:"address"` + FoursquareID string `json:"foursquare_id"` + FoursquareType string `json:"foursquare_type"` + GooglePlaceID string `json:"google_place_id"` + GooglePlaceType string `json:"google_place_type"` } // InputContactMessageContent contains a contact for displaying @@ -1104,6 +1162,12 @@ type StickerSet struct { Thumb *PhotoSize `json:"thumb"` } +// ChatLocation represents a location to which a chat is connected. +type ChatLocation struct { + Location Location `json:"location"` + Address string `json:"address"` +} + // BotCommand represents Telegram's understanding of a command. type BotCommand struct { Command string `json:"command"` @@ -1112,10 +1176,11 @@ type BotCommand 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"` + Type string `json:"type"` + Media string `json:"media"` + Caption string `json:"caption"` + ParseMode string `json:"parse_mode"` + CaptionEntities []MessageEntity `json:"caption_entities"` } // InputMediaPhoto is a photo to send as part of a media group. @@ -1151,6 +1216,7 @@ type InputMediaAudio struct { // InputMediaDocument is a audio to send as part of a media group. type InputMediaDocument struct { BaseInputMedia + DisableContentTypeDetection bool `json:"disable_content_type_detection,omitempty"` } // Error is an error containing extra information returned by the Telegram API. diff --git a/types_test.go b/types_test.go index 401cb6a..afe1fa0 100644 --- a/types_test.go +++ b/types_test.go @@ -311,7 +311,7 @@ var ( _ Chattable = PhotoConfig{} _ Chattable = PinChatMessageConfig{} _ Chattable = PromoteChatMemberConfig{} - _ Chattable = RemoveWebhookConfig{} + _ Chattable = DeleteWebhookConfig{} _ Chattable = RestrictChatMemberConfig{} _ Chattable = SendPollConfig{} _ Chattable = SetChatDescriptionConfig{} From 4064ced03f921894c1c8ee0b40476903f7d10b40 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Fri, 6 Nov 2020 12:36:00 -0500 Subject: [PATCH 2/4] Add some tests, fix copyMessage. --- bot.go | 20 ++++++++++++++++++ bot_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++----- configs.go | 4 ++++ helpers.go | 12 +++++++++++ types.go | 5 +++++ types_test.go | 3 +++ 6 files changed, 96 insertions(+), 5 deletions(-) diff --git a/bot.go b/bot.go index a78e635..d7b5b88 100644 --- a/bot.go +++ b/bot.go @@ -668,3 +668,23 @@ func (bot *BotAPI) GetMyCommands() ([]BotCommand, error) { return commands, err } + +// CopyMessage copy messages of any kind. The method is analogous to the method +// forwardMessage, but the copied message doesn't have a link to the original +// message. Returns the MessageID of the sent message on success. +func (bot *BotAPI) CopyMessage(config CopyMessageConfig) (MessageID, error) { + params, err := config.params() + if err != nil { + return MessageID{}, err + } + + resp, err := bot.MakeRequest(config.method(), params) + if err != nil { + return MessageID{}, err + } + + var messageID MessageID + err = json.Unmarshal(resp.Result, &messageID) + + return messageID, err +} diff --git a/bot_test.go b/bot_test.go index ddb8fb2..c465909 100644 --- a/bot_test.go +++ b/bot_test.go @@ -73,7 +73,7 @@ func TestSendWithMessage(t *testing.T) { bot, _ := getBot(t) msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") - msg.ParseMode = "markdown" + msg.ParseMode = ModeMarkdown _, err := bot.Send(msg) if err != nil { @@ -104,6 +104,26 @@ func TestSendWithMessageForward(t *testing.T) { } } +func TestCopyMessage(t *testing.T) { + bot, _ := getBot(t) + + msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") + message, err := bot.Send(msg) + if err != nil { + t.Error(err) + } + + copyMessageConfig := NewCopyMessage(SupergroupChatID, message.Chat.ID, message.MessageID) + messageID, err := bot.CopyMessage(copyMessageConfig) + if err != nil { + t.Error(err) + } + + if messageID.MessageID == message.MessageID { + t.Error("copied message ID was the same as original message") + } +} + func TestSendWithNewPhoto(t *testing.T) { bot, _ := getBot(t) @@ -724,7 +744,7 @@ func TestDeleteMessage(t *testing.T) { bot, _ := getBot(t) msg := NewMessage(ChatID, "A test message from the test library in telegram-bot-api") - msg.ParseMode = "markdown" + msg.ParseMode = ModeMarkdown message, _ := bot.Send(msg) deleteMessageConfig := DeleteMessageConfig{ @@ -742,7 +762,7 @@ func TestPinChatMessage(t *testing.T) { bot, _ := getBot(t) msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") - msg.ParseMode = "markdown" + msg.ParseMode = ModeMarkdown message, _ := bot.Send(msg) pinChatMessageConfig := PinChatMessageConfig{ @@ -761,7 +781,7 @@ func TestUnpinChatMessage(t *testing.T) { bot, _ := getBot(t) msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") - msg.ParseMode = "markdown" + msg.ParseMode = ModeMarkdown message, _ := bot.Send(msg) // We need pin message to unpin something @@ -776,7 +796,8 @@ func TestUnpinChatMessage(t *testing.T) { } unpinChatMessageConfig := UnpinChatMessageConfig{ - ChatID: message.Chat.ID, + ChatID: message.Chat.ID, + MessageID: message.MessageID, } if _, err := bot.Request(unpinChatMessageConfig); err != nil { @@ -784,6 +805,32 @@ func TestUnpinChatMessage(t *testing.T) { } } +func TestUnpinAllChatMessages(t *testing.T) { + bot, _ := getBot(t) + + msg := NewMessage(SupergroupChatID, "A test message from the test library in telegram-bot-api") + msg.ParseMode = ModeMarkdown + message, _ := bot.Send(msg) + + pinChatMessageConfig := PinChatMessageConfig{ + ChatID: message.Chat.ID, + MessageID: message.MessageID, + DisableNotification: true, + } + + if _, err := bot.Request(pinChatMessageConfig); err != nil { + t.Error(err) + } + + unpinAllChatMessagesConfig := UnpinAllChatMessagesConfig{ + ChatID: message.Chat.ID, + } + + if _, err := bot.Request(unpinAllChatMessagesConfig); err != nil { + t.Error(err) + } +} + func TestPolls(t *testing.T) { bot, _ := getBot(t) diff --git a/configs.go b/configs.go index c1321cd..cf192a4 100644 --- a/configs.go +++ b/configs.go @@ -241,6 +241,10 @@ func (config CopyMessageConfig) params() (Params, error) { return params, err } +func (config CopyMessageConfig) method() string { + return "copyMessage" +} + // PhotoConfig contains information about a SendPhoto request. type PhotoConfig struct { BaseFile diff --git a/helpers.go b/helpers.go index 4338c0a..e98ae06 100644 --- a/helpers.go +++ b/helpers.go @@ -52,6 +52,18 @@ func NewForward(chatID int64, fromChatID int64, messageID int) ForwardConfig { } } +// NewCopyMessage creates a new copy message. +// +// chatID is where to send it, fromChatID is the source chat, +// and messageID is the ID of the original message. +func NewCopyMessage(chatID int64, fromChatID int64, messageID int) CopyMessageConfig { + return CopyMessageConfig{ + BaseChat: BaseChat{ChatID: chatID}, + FromChatID: fromChatID, + MessageID: messageID, + } +} + // NewPhotoUpload creates a new photo uploader. // // chatID is where to send it, file is a string path to the file, diff --git a/types.go b/types.go index 63bcb13..22ae9f1 100644 --- a/types.go +++ b/types.go @@ -584,6 +584,11 @@ func (m *Message) CommandArguments() string { return m.Text[entity.Length+1:] } +// MessageID represents a unique message identifier. +type MessageID struct { + MessageID int `json:"message_id"` +} + // MessageEntity represents one special entity in a text message. type MessageEntity struct { // Type of the entity. diff --git a/types_test.go b/types_test.go index 0b1be9d..e9e2026 100644 --- a/types_test.go +++ b/types_test.go @@ -286,6 +286,8 @@ var ( _ Chattable = ChatActionConfig{} _ Chattable = ChatInfoConfig{} _ Chattable = ChatInviteLinkConfig{} + _ Chattable = CloseConfig{} + _ Chattable = CopyMessageConfig{} _ Chattable = ContactConfig{} _ Chattable = DeleteChatPhotoConfig{} _ Chattable = DeleteChatStickerSetConfig{} @@ -306,6 +308,7 @@ var ( _ Chattable = KickChatMemberConfig{} _ Chattable = LeaveChatConfig{} _ Chattable = LocationConfig{} + _ Chattable = LogOutConfig{} _ Chattable = MediaGroupConfig{} _ Chattable = MessageConfig{} _ Chattable = PhotoConfig{} From 24d4f79474ff891d3ee1b2e3756a094556c78464 Mon Sep 17 00:00:00 2001 From: Syfaro Date: Tue, 9 Mar 2021 12:27:17 -0500 Subject: [PATCH 3/4] Updates for Bot API 5.1. --- configs.go | 102 +++++++++++++++++++++++++++++++++++++++------ types.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ types_test.go | 11 +++-- 3 files changed, 209 insertions(+), 16 deletions(-) diff --git a/configs.go b/configs.go index cf192a4..4b53b87 100644 --- a/configs.go +++ b/configs.go @@ -1067,7 +1067,8 @@ func (config UnbanChatMemberConfig) params() (Params, error) { // KickChatMemberConfig contains extra fields to kick user type KickChatMemberConfig struct { ChatMemberConfig - UntilDate int64 + UntilDate int64 + RevokeMessages bool } func (config KickChatMemberConfig) method() string { @@ -1080,6 +1081,7 @@ func (config KickChatMemberConfig) params() (Params, error) { params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) params.AddNonZero("user_id", config.UserID) params.AddNonZero64("until_date", config.UntilDate) + params.AddBool("revoke_messages", config.RevokeMessages) return params, nil } @@ -1110,15 +1112,17 @@ func (config RestrictChatMemberConfig) params() (Params, error) { // PromoteChatMemberConfig contains fields to promote members of chat type PromoteChatMemberConfig struct { ChatMemberConfig - IsAnonymous bool - CanChangeInfo bool - CanPostMessages bool - CanEditMessages bool - CanDeleteMessages bool - CanInviteUsers bool - CanRestrictMembers bool - CanPinMessages bool - CanPromoteMembers bool + IsAnonymous bool + CanManageChat bool + CanChangeInfo bool + CanPostMessages bool + CanEditMessages bool + CanDeleteMessages bool + CanManageVoiceChats bool + CanInviteUsers bool + CanRestrictMembers bool + CanPinMessages bool + CanPromoteMembers bool } func (config PromoteChatMemberConfig) method() string { @@ -1132,10 +1136,12 @@ func (config PromoteChatMemberConfig) params() (Params, error) { params.AddNonZero("user_id", config.UserID) params.AddBool("is_anonymous", config.IsAnonymous) + params.AddBool("can_manage_chat", config.CanManageChat) params.AddBool("can_change_info", config.CanChangeInfo) params.AddBool("can_post_messages", config.CanPostMessages) params.AddBool("can_edit_messages", config.CanEditMessages) params.AddBool("can_delete_messages", config.CanDeleteMessages) + params.AddBool("can_manage_voice_chats", config.CanManageVoiceChats) params.AddBool("can_invite_users", config.CanInviteUsers) params.AddBool("can_restrict_members", config.CanRestrictMembers) params.AddBool("can_pin_messages", config.CanPinMessages) @@ -1246,6 +1252,77 @@ func (config ChatInviteLinkConfig) params() (Params, error) { return params, nil } +// CreateChatInviteLinkConfig allows you to create an additional invite link for +// a chat. The bot must be an administrator in the chat for this to work and +// must have the appropriate admin rights. The link can be revoked using the +// RevokeChatInviteLinkConfig. +type CreateChatInviteLinkConfig struct { + ChatConfig + ExpireDate int + MemberLimit int +} + +func (CreateChatInviteLinkConfig) method() string { + return "createChatInviteLink" +} + +func (config CreateChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params.AddNonZero("expire_date", config.ExpireDate) + params.AddNonZero("member_limit", config.MemberLimit) + + return params, nil +} + +// EditChatInviteLinkConfig allows you to edit a non-primary invite link created +// by the bot. The bot must be an administrator in the chat for this to work and +// must have the appropriate admin rights. +type EditChatInviteLinkConfig struct { + ChatConfig + InviteLink string + ExpireDate int + MemberLimit int +} + +func (EditChatInviteLinkConfig) method() string { + return "editChatInviteLink" +} + +func (config EditChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params["invite_link"] = config.InviteLink + params.AddNonZero("expire_date", config.ExpireDate) + params.AddNonZero("member_limit", config.MemberLimit) + + return params, nil +} + +// RevokeChatInviteLinkConfig allows you to revoke an invite link created by the +// bot. If the primary link is revoked, a new link is automatically generated. +// The bot must be an administrator in the chat for this to work and must have +// the appropriate admin rights. +type RevokeChatInviteLinkConfig struct { + ChatConfig + InviteLink string +} + +func (RevokeChatInviteLinkConfig) method() string { + return "revokeChatInviteLink" +} + +func (config RevokeChatInviteLinkConfig) params() (Params, error) { + params := make(Params) + + params.AddFirstValid("chat_id", config.ChatID, config.SuperGroupUsername) + params["invite_link"] = config.InviteLink + + return params, nil +} + // LeaveChatConfig allows you to leave a chat. type LeaveChatConfig struct { ChatID int64 @@ -1885,8 +1962,9 @@ func (config SetMyCommandsConfig) params() (Params, error) { type DiceConfig struct { BaseChat // Emoji on which the dice throw animation is based. - // Currently, must be one of “🎲”, “🎯”, or “🏀”. - // Dice can have values 1-6 for “🎲” and “🎯”, and values 1-5 for “🏀”. + // Currently, must be one of 🎲, 🎯, 🏀, ⚽, 🎳, or 🎰. + // Dice can have values 1-6 for 🎲, 🎯, and 🎳, values 1-5 for 🏀 and ⚽, + // and values 1-64 for 🎰. // Defaults to “🎲” Emoji string } diff --git a/types.go b/types.go index 22ae9f1..e6810ae 100644 --- a/types.go +++ b/types.go @@ -96,6 +96,18 @@ type Update struct { // // optional PollAnswer *PollAnswer `json:"poll_answer,omitempty"` + // MyChatMember is the bot's chat member status was updated in a chat. For + // private chats, this update is received only when the bot is blocked or + // unblocked by the user. + // + // optional + MyChatMember *ChatMemberUpdated `json:"my_chat_member"` + // ChatMember is a chat member's status was updated in a chat. The bot must + // be an administrator in the chat and must explicitly specify "chat_member" + // in the list of allowed_updates to receive these updates. + // + // optional + ChatMember *ChatMemberUpdated `json:"chat_member"` } // UpdatesChannel is the channel for getting updates. @@ -463,6 +475,11 @@ type Message struct { // // optional ChannelChatCreated bool `json:"channel_chat_created,omitempty"` + // MessageAutoDeleteTimerChanged is a service message: auto-delete timer + // settings changed in the chat. + // + // optional + MessageAutoDeleteTimerChanged *MessageAutoDeleteTimerChanged `json:"message_auto_delete_timer_changed"` // MigrateToChatID is the group has been migrated to a supergroup with the specified identifier. // This number may be greater than 32 bits and some programming languages // may have difficulty/silent defects in interpreting it. @@ -508,6 +525,19 @@ type Message struct { // // optional ProximityAlertTriggered *ProximityAlertTriggered `json:"proximity_alert_triggered"` + // VoiceChatStarted is a service message: voice chat started. + // + // optional + VoiceChatStarted *VoiceChatStarted `json:"voice_chat_started"` + // VoiceChatEnded is a service message: voice chat ended. + // + // optional + VoiceChatEnded *VoiceChatEnded `json:"voice_chat_ended"` + // VoiceChatParticipantsInvited is a service message: new participants + // invited to a voice chat. + // + // optional + VoiceChatParticipantsInvited *VoiceChatParticipantsInvited `json:"voice_chat_participants_invited"` // ReplyMarkup is the Inline keyboard attached to the message. // login_url buttons are represented as ordinary url buttons. // @@ -1037,6 +1067,33 @@ type ProximityAlertTriggered struct { Distance int `json:"distance"` } +// MessageAutoDeleteTimerChanged represents a service message about a change in +// auto-delete timer settings. +type MessageAutoDeleteTimerChanged struct { + // New auto-delete time for messages in the chat. + MessageAutoDeleteTime int `json:"message_auto_delete_time"` +} + +// VoiceChatStarted represents a service message about a voice chat started in +// the chat. +type VoiceChatStarted struct{} + +// VoiceChatEnded represents a service message about a voice chat ended in the +// chat. +type VoiceChatEnded struct { + // Voice chat duration; in seconds. + Duration int `json:"duration"` +} + +// VoiceChatParticipantsInvited represents a service message about new members +// invited to a voice chat. +type VoiceChatParticipantsInvited struct { + // New members that were invited to the voice chat. + // + // optional + Users []User `json:"users"` +} + // UserProfilePhotos contains a set of user profile photos. type UserProfilePhotos struct { // TotalCount total number of profile pictures the target user has @@ -1336,6 +1393,29 @@ type ChatPhoto struct { BigFileUniqueID string `json:"big_file_unique_id"` } +// ChatInviteLink represents an invite link for a chat. +type ChatInviteLink struct { + // InviteLink is the invite link. If the link was created by another chat + // administrator, then the second part of the link will be replaced with “…”. + InviteLink string `json:"invite_link"` + // Creator of the link. + Creator User `json:"creator"` + // IsPrimary is true, if the link is primary. + IsPrimary bool `json:"is_primary"` + // IsRevoked is true, if the link is revoked. + IsRevoked bool `json:"is_revoked"` + // ExpireDate is the point in time (Unix timestamp) when the link will + // expire or has been expired. + // + // optional + ExpireDate int `json:"expire_date"` + // MemberLimit is the maximum number of users that can be members of the + // chat simultaneously after joining the chat via this invite link; 1-99999. + // + // optional + MemberLimit int `json:"member_limit"` +} + // ChatMember contains information about one member of a chat. type ChatMember struct { // User information about the user @@ -1369,6 +1449,14 @@ type ChatMember struct { // // optional CanBeEdited bool `json:"can_be_edited,omitempty"` + // CanManageChat administrators only. + // True, if the administrator can access the chat event log, chat + // statistics, message statistics in channels, see channel members, see + // anonymous administrators in supergoups and ignore slow mode. Implied by + // any other administrator privilege. + // + // optional + CanManageChat bool `json:"can_manage_chat"` // CanPostMessages administrators only. // True, if the administrator can post in the channel; // channels only. @@ -1386,6 +1474,11 @@ type ChatMember struct { // // optional CanDeleteMessages bool `json:"can_delete_messages,omitempty"` + // CanManageVoiceChats administrators only. + // True, if the administrator can manage voice chats. + // + // optional + CanManageVoiceChats bool `json:"can_manage_voice_chats"` // CanRestrictMembers administrators only. // True, if the administrator can restrict, ban or unban chat members. // @@ -1455,6 +1548,25 @@ func (chat ChatMember) HasLeft() bool { return chat.Status == "left" } // WasKicked returns if the ChatMember was kicked from the chat. func (chat ChatMember) WasKicked() bool { return chat.Status == "kicked" } +// ChatMemberUpdated represents changes in the status of a chat member. +type ChatMemberUpdated struct { + // Chat the user belongs to. + Chat Chat `json:"chat"` + // From is the performer of the action, which resulted in the change. + From User `json:"from"` + // Date the change was done in Unix time. + Date int `json:"date"` + // Previous information about the chat member. + OldChatMember ChatMember `json:"old_chat_member"` + // New information about the chat member. + NewChatMember ChatMember `json:"new_chat_member"` + // InviteLink is the link which was used by the user to join the chat; + // for joining by invite link events only. + // + // optional + InviteLink *ChatInviteLink `json:"invite_link"` +} + // ChatPermissions describes actions that a non-administrator user is // allowed to take in a chat. All fields are optional. type ChatPermissions struct { diff --git a/types_test.go b/types_test.go index e9e2026..ba5f457 100644 --- a/types_test.go +++ b/types_test.go @@ -282,17 +282,20 @@ var ( _ Chattable = AnimationConfig{} _ Chattable = AudioConfig{} _ Chattable = CallbackConfig{} - _ Chattable = ChatAdministratorsConfig{} _ Chattable = ChatActionConfig{} + _ Chattable = ChatAdministratorsConfig{} _ Chattable = ChatInfoConfig{} _ Chattable = ChatInviteLinkConfig{} _ Chattable = CloseConfig{} - _ Chattable = CopyMessageConfig{} _ Chattable = ContactConfig{} + _ Chattable = CopyMessageConfig{} + _ Chattable = CreateChatInviteLinkConfig{} _ Chattable = DeleteChatPhotoConfig{} _ Chattable = DeleteChatStickerSetConfig{} _ Chattable = DeleteMessageConfig{} + _ Chattable = DeleteWebhookConfig{} _ Chattable = DocumentConfig{} + _ Chattable = EditChatInviteLinkConfig{} _ Chattable = EditMessageCaptionConfig{} _ Chattable = EditMessageLiveLocationConfig{} _ Chattable = EditMessageMediaConfig{} @@ -315,8 +318,8 @@ var ( _ Chattable = PinChatMessageConfig{} _ Chattable = PreCheckoutConfig{} _ Chattable = PromoteChatMemberConfig{} - _ Chattable = DeleteWebhookConfig{} _ Chattable = RestrictChatMemberConfig{} + _ Chattable = RevokeChatInviteLinkConfig{} _ Chattable = SendPollConfig{} _ Chattable = SetChatDescriptionConfig{} _ Chattable = SetChatPhotoConfig{} @@ -324,8 +327,8 @@ var ( _ Chattable = SetGameScoreConfig{} _ Chattable = ShippingConfig{} _ Chattable = StickerConfig{} - _ Chattable = StopPollConfig{} _ Chattable = StopMessageLiveLocationConfig{} + _ Chattable = StopPollConfig{} _ Chattable = UnbanChatMemberConfig{} _ Chattable = UnpinChatMessageConfig{} _ Chattable = UpdateConfig{} From 3b5c8a96d7bd1c2b89d8eb1f22db94034e05b820 Mon Sep 17 00:00:00 2001 From: TJ Horner Date: Tue, 9 Mar 2021 15:52:22 -0500 Subject: [PATCH 4/4] Add allowed_updates to GetUpdates --- bot.go | 2 +- configs.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/bot.go b/bot.go index d7b5b88..4590eab 100644 --- a/bot.go +++ b/bot.go @@ -387,7 +387,7 @@ func (bot *BotAPI) GetFile(config FileConfig) (File, error) { // GetUpdates fetches updates. // If a WebHook is set, this will not return any data! // -// Offset, Limit, and Timeout are optional. +// Offset, Limit, Timeout, and AllowedUpdates are optional. // To avoid stale items, set Offset to one higher than the previous item. // Set Timeout to a large number to reduce requests so you can get updates // instantly instead of having to wait between requests. diff --git a/configs.go b/configs.go index 4b53b87..b08c3eb 100644 --- a/configs.go +++ b/configs.go @@ -42,6 +42,54 @@ const ( ModeHTML = "HTML" ) +// Constant values for update types +const ( + // New incoming message of any kind — text, photo, sticker, etc. + UpdateTypeMessage = "message" + + // New version of a message that is known to the bot and was edited + UpdateTypeEditedMessage = "edited_message" + + // New incoming channel post of any kind — text, photo, sticker, etc. + UpdateTypeChannelPost = "channel_post" + + // New version of a channel post that is known to the bot and was edited + UpdateTypeEditedChannelPost = "edited_channel_post" + + // New incoming inline query + UpdateTypeInlineQuery = "inline_query" + + // The result of an inline query that was chosen by a user and sent to their + // chat partner. Please see the documentation on the feedback collecting for + // details on how to enable these updates for your bot. + UpdateTypeChosenInlineResult = "chosen_inline_result" + + // New incoming callback query + UpdateTypeCallbackQuery = "callback_query" + + // New incoming shipping query. Only for invoices with flexible price + UpdateTypeShippingQuery = "shipping_query" + + // New incoming pre-checkout query. Contains full information about checkout + UpdateTypePreCheckoutQuery = "pre_checkout_query" + + // New poll state. Bots receive only updates about stopped polls and polls + // which are sent by the bot + UpdateTypePoll = "poll" + + // A user changed their answer in a non-anonymous poll. Bots receive new votes + // only in polls that were sent by the bot itself. + UpdateTypePollAnswer = "poll_answer" + + // The bot's chat member status was updated in a chat. For private chats, this + // update is received only when the bot is blocked or unblocked by the user. + UpdateTypeMyChatMember = "my_chat_member" + + // The bot must be an administrator in the chat and must explicitly specify + // this update in the list of allowed_updates to receive these updates. + UpdateTypeChatMember = "chat_member" +) + // Library errors const ( // ErrBadFileType happens when you pass an unknown type @@ -888,9 +936,10 @@ func (config FileConfig) params() (Params, error) { // UpdateConfig contains information about a GetUpdates request. type UpdateConfig struct { - Offset int - Limit int - Timeout int + Offset int + Limit int + Timeout int + AllowedUpdates []string } func (UpdateConfig) method() string { @@ -903,6 +952,7 @@ func (config UpdateConfig) params() (Params, error) { params.AddNonZero("offset", config.Offset) params.AddNonZero("limit", config.Limit) params.AddNonZero("timeout", config.Timeout) + params.AddInterface("allowed_updates", config.AllowedUpdates) return params, nil }