diff --git a/build_request_form.go b/build_request_form.go index ff23188..2d3a5fb 100644 --- a/build_request_form.go +++ b/build_request_form.go @@ -44,7 +44,7 @@ func buildRequestForm(form *multipart.Writer, params any) (int, error) { continue } - // check fields by interface + // check fields by interface (declared type) if v.Field(i).Type().Implements(customMarshalInterface) { err := addFormFieldCustomMarshal(form, fieldName, v.Field(i).Interface().(customMarshal)) if err != nil { @@ -60,6 +60,24 @@ func buildRequestForm(form *multipart.Writer, params any) (int, error) { continue } + // check fields by interface (runtime concrete type behind interface fields) + if v.Field(i).Kind() == reflect.Interface && !v.Field(i).IsNil() { + if v.Field(i).Elem().Type().Implements(customMarshalInterface) { + err := addFormFieldCustomMarshal(form, fieldName, v.Field(i).Interface().(customMarshal)) + if err != nil { + return 0, err + } + continue + } + if v.Field(i).Elem().Type().Implements(inputMediaInterface) { + err := addFormFieldInputMedia(form, fieldName, v.Field(i).Interface().(inputMedia)) + if err != nil { + return 0, err + } + continue + } + } + var err error // check fields by type diff --git a/methods.go b/methods.go index 55e1520..9869f05 100644 --- a/methods.go +++ b/methods.go @@ -1,7 +1,9 @@ package bot import ( + "bytes" "context" + "encoding/json" "github.com/go-telegram/bot/models" ) @@ -155,16 +157,12 @@ func (b *Bot) SendLocation(ctx context.Context, params *SendLocationParams) (*mo // EditMessageLiveLocation https://core.telegram.org/bots/api#editmessagelivelocation func (b *Bot) EditMessageLiveLocation(ctx context.Context, params *EditMessageLiveLocationParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "editMessageLiveLocation", params, result) - return result, err + return b.editMethodRequest(ctx, "editMessageLiveLocation", params) } // StopMessageLiveLocation https://core.telegram.org/bots/api#stopmessagelivelocation func (b *Bot) StopMessageLiveLocation(ctx context.Context, params *StopMessageLiveLocationParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "stopMessageLiveLocation", params, result) - return result, err + return b.editMethodRequest(ctx, "stopMessageLiveLocation", params) } // SendVenue https://core.telegram.org/bots/api#sendvenue @@ -652,37 +650,27 @@ func (b *Bot) GetMyDefaultAdministratorRights(ctx context.Context, params *GetMy // EditMessageText https://core.telegram.org/bots/api#editmessagetext func (b *Bot) EditMessageText(ctx context.Context, params *EditMessageTextParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "editMessageText", params, result) - return result, err + return b.editMethodRequest(ctx, "editMessageText", params) } // EditMessageCaption https://core.telegram.org/bots/api#editmessagecaption func (b *Bot) EditMessageCaption(ctx context.Context, params *EditMessageCaptionParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "editMessageCaption", params, result) - return result, err + return b.editMethodRequest(ctx, "editMessageCaption", params) } // EditMessageMedia https://core.telegram.org/bots/api#editmessagemedia func (b *Bot) EditMessageMedia(ctx context.Context, params *EditMessageMediaParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "editMessageMedia", params, result) - return result, err + return b.editMethodRequest(ctx, "editMessageMedia", params) } // EditMessageChecklist https://core.telegram.org/bots/api#editmessagechecklist func (b *Bot) EditMessageChecklist(ctx context.Context, params *EditMessageChecklistParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "editMessageChecklist", params, result) - return result, err + return b.editMethodRequest(ctx, "editMessageChecklist", params) } // EditMessageReplyMarkup https://core.telegram.org/bots/api#editmessagereplymarkup func (b *Bot) EditMessageReplyMarkup(ctx context.Context, params *EditMessageReplyMarkupParams) (*models.Message, error) { - result := &models.Message{} - err := b.rawRequest(ctx, "editMessageReplyMarkup", params, result) - return result, err + return b.editMethodRequest(ctx, "editMessageReplyMarkup", params) } // StopPoll https://core.telegram.org/bots/api#stoppoll @@ -1153,3 +1141,28 @@ func (b *Bot) GetUserProfileAudios(ctx context.Context, params *GetUserProfileAu err := b.rawRequest(ctx, "getUserProfileAudios", params, &result) return result, err } + +// editMethodRequest is a helper for edit/stop methods that return *Message for regular messages +// or true (bool) for inline messages. When the API returns a boolean, nil Message is returned. +func (b *Bot) editMethodRequest(ctx context.Context, method string, params any) (*models.Message, error) { + var raw json.RawMessage + err := b.rawRequest(ctx, method, params, &raw) + if err != nil { + return nil, err + } + if bytes.Equal(raw, []byte("true")) { + return nil, nil + } + result := &models.Message{} + if err := json.Unmarshal(raw, result); err != nil { + return nil, err + } + return result, nil +} + +// SetChatMemberTag https://core.telegram.org/bots/api#setchatmembertag +func (b *Bot) SetChatMemberTag(ctx context.Context, params *SetChatMemberTagParams) (bool, error) { + var result bool + err := b.rawRequest(ctx, "setChatMemberTag", params, &result) + return result, err +} diff --git a/methods_params.go b/methods_params.go index e72f031..66ccd31 100644 --- a/methods_params.go +++ b/methods_params.go @@ -499,6 +499,7 @@ type PromoteChatMemberParams struct { CanDeleteStories bool `json:"can_delete_stories,omitempty"` CanManageTopics bool `json:"can_manage_topics,omitempty"` CanManageDirectMessages bool `json:"can_manage_direct_messages,omitempty"` + CanManageTags bool `json:"can_manage_tags,omitempty"` } type SetChatAdministratorCustomTitleParams struct { @@ -1328,3 +1329,10 @@ type GetUserProfileAudiosParams struct { Offset int `json:"offset,omitempty"` Limit int `json:"limit,omitempty"` } + +// SetChatMemberTagParams https://core.telegram.org/bots/api#setchatmembertag +type SetChatMemberTagParams struct { + ChatID any `json:"chat_id" rules:"required,chat_id"` + UserID int64 `json:"user_id" rules:"required"` + Tag string `json:"tag,omitempty"` +} diff --git a/models/chat.go b/models/chat.go index f3ffc8f..da96b53 100644 --- a/models/chat.go +++ b/models/chat.go @@ -49,6 +49,7 @@ type ChatAdministratorRights struct { CanDeleteStories bool `json:"can_delete_stories,omitempty"` CanManageTopics bool `json:"can_manage_topics,omitempty"` CanManageDirectMessages bool `json:"can_manage_direct_messages,omitempty"` + CanManageTags bool `json:"can_manage_tags,omitempty"` } // ChatPermissions https://core.telegram.org/bots/api#chatpermissions @@ -67,6 +68,7 @@ type ChatPermissions struct { CanInviteUsers bool `json:"can_invite_users,omitempty"` CanPinMessages bool `json:"can_pin_messages,omitempty"` CanManageTopics bool `json:"can_manage_topics,omitempty"` + CanEditTag bool `json:"can_edit_tag,omitempty"` } // ChatLocation https://core.telegram.org/bots/api#chatlocation diff --git a/models/chat_member.go b/models/chat_member.go index 264f5cf..0eac6f0 100644 --- a/models/chat_member.go +++ b/models/chat_member.go @@ -105,7 +105,7 @@ func (c *ChatMember) MarshalJSON() ([]byte, error) { // ChatMemberOwner https://core.telegram.org/bots/api#chatmemberowner type ChatMemberOwner struct { - Status ChatMemberType `json:"status"` // The member's status in the chat, always “creator” + Status ChatMemberType `json:"status"` // The member's status in the chat, always "creator" User *User `json:"user"` IsAnonymous bool `json:"is_anonymous"` CustomTitle string `json:"custom_title,omitempty"` @@ -113,7 +113,7 @@ type ChatMemberOwner struct { // ChatMemberAdministrator https://core.telegram.org/bots/api#chatmemberadministrator type ChatMemberAdministrator struct { - Status ChatMemberType `json:"status"` // The member's status in the chat, always “administrator” + Status ChatMemberType `json:"status"` // The member's status in the chat, always "administrator" User User `json:"user"` CanBeEdited bool `json:"can_be_edited"` IsAnonymous bool `json:"is_anonymous"` @@ -132,19 +132,21 @@ type ChatMemberAdministrator struct { CanDeleteStories bool `json:"can_delete_stories,omitempty"` CanManageTopics bool `json:"can_manage_topics,omitempty"` CanManageDirectMessages bool `json:"can_manage_direct_messages,omitempty"` + CanManageTags bool `json:"can_manage_tags,omitempty"` CustomTitle string `json:"custom_title,omitempty"` } // ChatMemberMember https://core.telegram.org/bots/api#chatmembermember type ChatMemberMember struct { - Status ChatMemberType `json:"status"` // The member's status in the chat, always “member” + Status ChatMemberType `json:"status"` // The member's status in the chat, always "member" User *User `json:"user"` UntilDate int `json:"until_date,omitempty"` + Tag string `json:"tag,omitempty"` } // ChatMemberRestricted https://core.telegram.org/bots/api#chatmemberrestricted type ChatMemberRestricted struct { - Status ChatMemberType `json:"status"` // The member's status in the chat, always “restricted” + Status ChatMemberType `json:"status"` // The member's status in the chat, always "restricted" User *User `json:"user"` IsMember bool `json:"is_member"` CanSendMessages bool `json:"can_send_messages"` @@ -161,18 +163,20 @@ type ChatMemberRestricted struct { CanInviteUsers bool `json:"can_invite_users"` CanPinMessages bool `json:"can_pin_messages"` CanManageTopics bool `json:"can_manage_topics,omitempty"` + CanEditTag bool `json:"can_edit_tag,omitempty"` UntilDate int `json:"until_date"` + Tag string `json:"tag,omitempty"` } // ChatMemberLeft https://core.telegram.org/bots/api#chatmemberleft type ChatMemberLeft struct { - Status ChatMemberType `json:"status"` // The member's status in the chat, always “left” + Status ChatMemberType `json:"status"` // The member's status in the chat, always "left" User *User `json:"user"` } // ChatMemberBanned https://core.telegram.org/bots/api#chatmemberbanned type ChatMemberBanned struct { - Status ChatMemberType `json:"status"` // The member's status in the chat, always “kicked” + Status ChatMemberType `json:"status"` // The member's status in the chat, always "kicked" User *User `json:"user"` UntilDate int `json:"until_date"` } diff --git a/models/message.go b/models/message.go index 586fc02..530a102 100644 --- a/models/message.go +++ b/models/message.go @@ -87,6 +87,7 @@ type Message struct { SenderChat *Chat `json:"sender_chat,omitempty"` SenderBoostCount int `json:"sender_boost_count,omitempty"` SenderBusinessBot *User `json:"sender_business_bot,omitempty"` + SenderTag string `json:"sender_tag,omitempty"` Date int `json:"date"` BusinessConnectionID string `json:"business_connection_id,omitempty"` Chat Chat `json:"chat"` diff --git a/models/message_entity.go b/models/message_entity.go index 16f10b3..34f436b 100644 --- a/models/message_entity.go +++ b/models/message_entity.go @@ -22,6 +22,7 @@ const ( MessageEntityTypeTextLink MessageEntityType = "text_link" MessageEntityTypeTextMention MessageEntityType = "text_mention" MessageEntityTypeCustomEmoji MessageEntityType = "custom_emoji" + MessageEntityTypeDateTime MessageEntityType = "date_time" ) // MessageEntity https://core.telegram.org/bots/api#messageentity @@ -32,5 +33,7 @@ type MessageEntity struct { URL string `json:"url,omitempty"` User *User `json:"user,omitempty"` Language string `json:"language,omitempty"` - CustomEmojiID string `json:"custom_emoji_id,omitempty"` + CustomEmojiID string `json:"custom_emoji_id,omitempty"` + UnixTime int `json:"unix_time,omitempty"` + DateTimeFormat string `json:"date_time_format,omitempty"` } diff --git a/models/reply_markup.go b/models/reply_markup.go index a59bc8f..39353ae 100644 --- a/models/reply_markup.go +++ b/models/reply_markup.go @@ -38,10 +38,10 @@ type InlineKeyboardButton struct { CallbackData string `json:"callback_data,omitempty"` WebApp *WebAppInfo `json:"web_app,omitempty"` LoginURL *LoginURL `json:"login_url,omitempty"` - SwitchInlineQuery string `json:"switch_inline_query,omitempty"` - SwitchInlineQueryCurrentChat string `json:"switch_inline_query_current_chat,omitempty"` + SwitchInlineQuery *string `json:"switch_inline_query,omitempty"` + SwitchInlineQueryCurrentChat *string `json:"switch_inline_query_current_chat,omitempty"` SwitchInlineQueryChosenChat *SwitchInlineQueryChosenChat `json:"switch_inline_query_chosen_chat,omitempty"` - CopyText CopyTextButton `json:"copy_text,omitempty"` + CopyText *CopyTextButton `json:"copy_text,omitempty"` CallbackGame *CallbackGame `json:"callback_game,omitempty"` Pay bool `json:"pay,omitempty"` } @@ -61,7 +61,7 @@ type KeyboardButton struct { Text string `json:"text"` IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"` Style string `json:"style,omitempty"` - RequestUser *KeyboardButtonRequestUsers `json:"request_user,omitempty"` + RequestUser *KeyboardButtonRequestUser `json:"request_user,omitempty"` RequestUsers *KeyboardButtonRequestUsers `json:"request_users,omitempty"` RequestChat *KeyboardButtonRequestChat `json:"request_chat,omitempty"` RequestContact bool `json:"request_contact,omitempty"` @@ -73,8 +73,8 @@ type KeyboardButton struct { // KeyboardButtonRequestUser https://core.telegram.org/bots/api#keyboardbuttonrequestuser type KeyboardButtonRequestUser struct { RequestID int32 `json:"request_id"` - UserIsBot bool `json:"user_is_bot,omitempty"` - UserIsPremium bool `json:"user_is_premium,omitempty"` + UserIsBot *bool `json:"user_is_bot,omitempty"` + UserIsPremium *bool `json:"user_is_premium,omitempty"` } // KeyboardButtonRequestUsers https://core.telegram.org/bots/api#keyboardbuttonrequestusers