From a32301e16fb8244d0f5929c582f5cf53a4075b0d Mon Sep 17 00:00:00 2001 From: schkedy Date: Fri, 5 Jun 2026 11:25:26 +0300 Subject: [PATCH 1/4] Add DeleteMessage function --- bot.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bot.go b/bot.go index ff84bf07..42edee12 100644 --- a/bot.go +++ b/bot.go @@ -375,6 +375,13 @@ func (bot *BotAPI) Send(c Chattable) (Message, error) { return message, err } +// DeleteMessage deletes a message +func (bot *BotAPI) DeleteMessage(chatID int64, messageID int) error { + req := NewDeleteMessage(chatID, messageID) + _, err := bot.Request(req) + return err +} + func (bot *BotAPI) requestBool(c Chattable) (bool, error) { resp, err := bot.Request(c) if err != nil { From dfc22c06b923b4baaca796b1822f197e9c06c286 Mon Sep 17 00:00:00 2001 From: schkedy Date: Mon, 15 Jun 2026 13:44:31 +0300 Subject: [PATCH 2/4] Add bot's deleteMessage and EditMessage --- bot.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++ configs_test.go | 55 +++++++++++++++ helper_methods.go | 31 +++++++++ helper_structs.go | 10 ++- types.go | 11 +++ 5 files changed, 273 insertions(+), 3 deletions(-) diff --git a/bot.go b/bot.go index 42edee12..610844b5 100644 --- a/bot.go +++ b/bot.go @@ -382,6 +382,175 @@ func (bot *BotAPI) DeleteMessage(chatID int64, messageID int) error { return err } +func (bot *BotAPI) EditMessageText(chatID int64, messageID int, text string) (Message, error) { + req := NewEditMessageText(chatID, messageID, text) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKeyboardMarkup) (Message, error) { + req := NewEditMessageReplyMarkup(chatID, messageID, replyMarkup) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageMedia(chatID int64, messageID int, media InputMedia) (Message, error) { + req := NewEditMessageMedia(chatID, messageID, media) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageMediaAndMarkup(chatID int64, messageID int, media InputMedia, replyMarkup InlineKeyboardMarkup) (Message, error) { + req := NewEditMessageMediaAndMarkup(chatID, messageID, media, replyMarkup) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessagePhoto(chatID int64, messageID int, media InputMediaPhoto) (Message, error) { + req := NewEditMessagePhoto(chatID, messageID, media) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageVideo(chatID int64, messageID int, media InputMediaVideo) (Message, error) { + req := NewEditMessageVideo(chatID, messageID, media) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageAnimation(chatID int64, messageID int, media InputMediaAnimation) (Message, error) { + req := NewEditMessageAnimation(chatID, messageID, media) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageAudio(chatID int64, messageID int, media InputMediaAudio) (Message, error) { + req := NewEditMessageAudio(chatID, messageID, media) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageDocument(chatID int64, messageID int, media InputMediaDocument) (Message, error) { + req := NewEditMessageDocument(chatID, messageID, media) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageCaption(chatID int64, messageID int, caption string) (Message, error) { + req := NewEditMessageCaption(chatID, messageID, caption) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageTextAndMarkup(chatID int64, messageID int, text string, replyMarkup InlineKeyboardMarkup) (Message, error) { + req := NewEditMessageTextAndMarkup(chatID, messageID, text, replyMarkup) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageCaptionAndMarkup(chatID int64, messageID int, caption string, replyMarkup InlineKeyboardMarkup) (Message, error) { + req := NewEditMessageCaptionAndMarkup(chatID, messageID, caption, replyMarkup) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + +func (bot *BotAPI) EditMessageChecklist(chatID int64, messageID int, checklist InputChecklist) (Message, error) { + req := NewEditMessageChecklist(chatID, messageID, checklist) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + func (bot *BotAPI) requestBool(c Chattable) (bool, error) { resp, err := bot.Request(c) if err != nil { diff --git a/configs_test.go b/configs_test.go index 444dfaeb..9b493d41 100644 --- a/configs_test.go +++ b/configs_test.go @@ -1523,3 +1523,58 @@ func TestMediaGroupConfig(t *testing.T) { }) } } + +func TestEditMessageKeyboardBehavior(t *testing.T) { + t.Run("nil ReplyMarkup does not send reply_markup", func(t *testing.T) { + cfg := NewEditMessageCaption(123, 456, "caption") + params, err := cfg.params() + if err != nil { + t.Fatal(err) + } + if _, ok := params["reply_markup"]; ok { + t.Errorf("reply_markup must NOT be sent when ReplyMarkup is nil (keyboard must be preserved), got: %s", params["reply_markup"]) + } + }) + + t.Run("nil ReplyMarkup on EditMessageText does not send reply_markup", func(t *testing.T) { + cfg := NewEditMessageText(123, 456, "text") + params, err := cfg.params() + if err != nil { + t.Fatal(err) + } + if _, ok := params["reply_markup"]; ok { + t.Errorf("reply_markup must NOT be sent when ReplyMarkup is nil, got: %s", params["reply_markup"]) + } + }) + + t.Run("empty InlineKeyboardMarkup removes keyboard", func(t *testing.T) { + cfg := NewEditMessageCaption(123, 456, "caption") + cfg.ReplyMarkup = &InlineKeyboardMarkup{} + params, err := cfg.params() + if err != nil { + t.Fatal(err) + } + val, ok := params["reply_markup"] + if !ok { + t.Fatal("reply_markup must be sent when ReplyMarkup is non-nil") + } + if val != `{"inline_keyboard":[]}` { + t.Errorf("expected inline_keyboard=[], got: %s", val) + } + }) + + t.Run("InlineKeyboardMarkup with buttons sends correct markup", func(t *testing.T) { + cfg := NewEditMessageCaption(123, 456, "caption") + cb := "ok" + cfg.ReplyMarkup = &InlineKeyboardMarkup{ + InlineKeyboard: [][]InlineKeyboardButton{{{Text: "OK", CallbackData: &cb}}}, + } + params, err := cfg.params() + if err != nil { + t.Fatal(err) + } + if _, ok := params["reply_markup"]; !ok { + t.Fatal("reply_markup must be present") + } + }) +} diff --git a/helper_methods.go b/helper_methods.go index 5bec80b4..701ce2e7 100644 --- a/helper_methods.go +++ b/helper_methods.go @@ -739,6 +739,21 @@ func NewEditMessageMedia(chatID int64, messageID int, inputMedia InputMedia) Edi } } +func NewEditMessageMediaAndMarkup(chatID int64, messageID int, inputMedia InputMedia, replyMarkup InlineKeyboardMarkup) EditMessageMediaConfig { + return EditMessageMediaConfig{ + BaseEdit: BaseEdit{ + BaseChatMessage: BaseChatMessage{ + ChatConfig: ChatConfig{ + ChatID: chatID, + }, + MessageID: messageID, + }, + ReplyMarkup: &replyMarkup, + }, + Media: inputMedia, + } +} + // NewEditMessagePhoto allows you to edit the photo content of a message. func NewEditMessagePhoto(chatID int64, messageID int, inputPhoto InputMediaPhoto) EditMessageMediaConfig { return NewEditMessageMedia(chatID, messageID, &inputPhoto) @@ -810,6 +825,22 @@ func NewEditMessageCaption(chatID int64, messageID int, caption string) EditMess } } +// NewEditMessageCaptionAndMarkup allows you to edit the caption and reply markup of a message. +func NewEditMessageCaptionAndMarkup(chatID int64, messageID int, caption string, replyMarkup InlineKeyboardMarkup) EditMessageCaptionConfig { + return EditMessageCaptionConfig{ + BaseEdit: BaseEdit{ + BaseChatMessage: BaseChatMessage{ + ChatConfig: ChatConfig{ + ChatID: chatID, + }, + MessageID: messageID, + }, + ReplyMarkup: &replyMarkup, + }, + Caption: caption, + } +} + // NewEditMessageReplyMarkup allows you to edit the inline // keyboard markup. func NewEditMessageReplyMarkup(chatID int64, messageID int, replyMarkup InlineKeyboardMarkup) EditMessageReplyMarkupConfig { diff --git a/helper_structs.go b/helper_structs.go index 79ec3017..2f9844fa 100644 --- a/helper_structs.go +++ b/helper_structs.go @@ -90,10 +90,14 @@ func (edit BaseEdit) params() (Params, error) { } params.Merge(p1) } + if edit.ReplyMarkup != nil { + err := params.AddInterface("reply_markup", edit.ReplyMarkup) + if err != nil { + return params, err + } + } - err := params.AddInterface("reply_markup", edit.ReplyMarkup) - - return params, err + return params, nil } // BaseSpoiler is base type of structures with spoilers. diff --git a/types.go b/types.go index 25f48ee4..3cc28fb3 100644 --- a/types.go +++ b/types.go @@ -2863,6 +2863,17 @@ type InlineKeyboardMarkup struct { InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"` } +// MarshalJSON ensures that a nil InlineKeyboard is serialized as an empty array +// rather than null, so that sending an empty InlineKeyboardMarkup explicitly +// removes the keyboard instead of producing an invalid {"inline_keyboard":null}. +func (m InlineKeyboardMarkup) MarshalJSON() ([]byte, error) { + type alias InlineKeyboardMarkup + if m.InlineKeyboard == nil { + m.InlineKeyboard = make([][]InlineKeyboardButton, 0) + } + return json.Marshal(alias(m)) +} + // InlineKeyboardButton represents one button of an inline keyboard. You must // use exactly one of the optional fields. // From c13f4f13d2f3b25eee405fd66fca968783e61eb3 Mon Sep 17 00:00:00 2001 From: schkedy Date: Mon, 15 Jun 2026 13:46:32 +0300 Subject: [PATCH 3/4] remove tests --- bot.go | 13 ----------- configs_test.go | 55 ----------------------------------------------- helper_methods.go | 15 ------------- types.go | 11 ---------- 4 files changed, 94 deletions(-) diff --git a/bot.go b/bot.go index 610844b5..6007d46c 100644 --- a/bot.go +++ b/bot.go @@ -421,19 +421,6 @@ func (bot *BotAPI) EditMessageMedia(chatID int64, messageID int, media InputMedi return message, err } -func (bot *BotAPI) EditMessageMediaAndMarkup(chatID int64, messageID int, media InputMedia, replyMarkup InlineKeyboardMarkup) (Message, error) { - req := NewEditMessageMediaAndMarkup(chatID, messageID, media, replyMarkup) - resp, err := bot.Request(req) - if err != nil { - return Message{}, err - } - - var message Message - err = json.Unmarshal(resp.Result, &message) - - return message, err -} - func (bot *BotAPI) EditMessagePhoto(chatID int64, messageID int, media InputMediaPhoto) (Message, error) { req := NewEditMessagePhoto(chatID, messageID, media) resp, err := bot.Request(req) diff --git a/configs_test.go b/configs_test.go index 9b493d41..444dfaeb 100644 --- a/configs_test.go +++ b/configs_test.go @@ -1523,58 +1523,3 @@ func TestMediaGroupConfig(t *testing.T) { }) } } - -func TestEditMessageKeyboardBehavior(t *testing.T) { - t.Run("nil ReplyMarkup does not send reply_markup", func(t *testing.T) { - cfg := NewEditMessageCaption(123, 456, "caption") - params, err := cfg.params() - if err != nil { - t.Fatal(err) - } - if _, ok := params["reply_markup"]; ok { - t.Errorf("reply_markup must NOT be sent when ReplyMarkup is nil (keyboard must be preserved), got: %s", params["reply_markup"]) - } - }) - - t.Run("nil ReplyMarkup on EditMessageText does not send reply_markup", func(t *testing.T) { - cfg := NewEditMessageText(123, 456, "text") - params, err := cfg.params() - if err != nil { - t.Fatal(err) - } - if _, ok := params["reply_markup"]; ok { - t.Errorf("reply_markup must NOT be sent when ReplyMarkup is nil, got: %s", params["reply_markup"]) - } - }) - - t.Run("empty InlineKeyboardMarkup removes keyboard", func(t *testing.T) { - cfg := NewEditMessageCaption(123, 456, "caption") - cfg.ReplyMarkup = &InlineKeyboardMarkup{} - params, err := cfg.params() - if err != nil { - t.Fatal(err) - } - val, ok := params["reply_markup"] - if !ok { - t.Fatal("reply_markup must be sent when ReplyMarkup is non-nil") - } - if val != `{"inline_keyboard":[]}` { - t.Errorf("expected inline_keyboard=[], got: %s", val) - } - }) - - t.Run("InlineKeyboardMarkup with buttons sends correct markup", func(t *testing.T) { - cfg := NewEditMessageCaption(123, 456, "caption") - cb := "ok" - cfg.ReplyMarkup = &InlineKeyboardMarkup{ - InlineKeyboard: [][]InlineKeyboardButton{{{Text: "OK", CallbackData: &cb}}}, - } - params, err := cfg.params() - if err != nil { - t.Fatal(err) - } - if _, ok := params["reply_markup"]; !ok { - t.Fatal("reply_markup must be present") - } - }) -} diff --git a/helper_methods.go b/helper_methods.go index 701ce2e7..348ddbac 100644 --- a/helper_methods.go +++ b/helper_methods.go @@ -739,21 +739,6 @@ func NewEditMessageMedia(chatID int64, messageID int, inputMedia InputMedia) Edi } } -func NewEditMessageMediaAndMarkup(chatID int64, messageID int, inputMedia InputMedia, replyMarkup InlineKeyboardMarkup) EditMessageMediaConfig { - return EditMessageMediaConfig{ - BaseEdit: BaseEdit{ - BaseChatMessage: BaseChatMessage{ - ChatConfig: ChatConfig{ - ChatID: chatID, - }, - MessageID: messageID, - }, - ReplyMarkup: &replyMarkup, - }, - Media: inputMedia, - } -} - // NewEditMessagePhoto allows you to edit the photo content of a message. func NewEditMessagePhoto(chatID int64, messageID int, inputPhoto InputMediaPhoto) EditMessageMediaConfig { return NewEditMessageMedia(chatID, messageID, &inputPhoto) diff --git a/types.go b/types.go index 3cc28fb3..25f48ee4 100644 --- a/types.go +++ b/types.go @@ -2863,17 +2863,6 @@ type InlineKeyboardMarkup struct { InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"` } -// MarshalJSON ensures that a nil InlineKeyboard is serialized as an empty array -// rather than null, so that sending an empty InlineKeyboardMarkup explicitly -// removes the keyboard instead of producing an invalid {"inline_keyboard":null}. -func (m InlineKeyboardMarkup) MarshalJSON() ([]byte, error) { - type alias InlineKeyboardMarkup - if m.InlineKeyboard == nil { - m.InlineKeyboard = make([][]InlineKeyboardButton, 0) - } - return json.Marshal(alias(m)) -} - // InlineKeyboardButton represents one button of an inline keyboard. You must // use exactly one of the optional fields. // From 624adf687190a4e2bed728205f4eed8bd7b4e195 Mon Sep 17 00:00:00 2001 From: schkedy Date: Fri, 24 Jul 2026 14:23:50 +0300 Subject: [PATCH 4/4] Add new features to bot and helper methods --- bot.go | 13 +++++++++++++ helper_methods.go | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/bot.go b/bot.go index 6007d46c..610844b5 100644 --- a/bot.go +++ b/bot.go @@ -421,6 +421,19 @@ func (bot *BotAPI) EditMessageMedia(chatID int64, messageID int, media InputMedi return message, err } +func (bot *BotAPI) EditMessageMediaAndMarkup(chatID int64, messageID int, media InputMedia, replyMarkup InlineKeyboardMarkup) (Message, error) { + req := NewEditMessageMediaAndMarkup(chatID, messageID, media, replyMarkup) + resp, err := bot.Request(req) + if err != nil { + return Message{}, err + } + + var message Message + err = json.Unmarshal(resp.Result, &message) + + return message, err +} + func (bot *BotAPI) EditMessagePhoto(chatID int64, messageID int, media InputMediaPhoto) (Message, error) { req := NewEditMessagePhoto(chatID, messageID, media) resp, err := bot.Request(req) diff --git a/helper_methods.go b/helper_methods.go index 348ddbac..faa7aac3 100644 --- a/helper_methods.go +++ b/helper_methods.go @@ -739,6 +739,22 @@ func NewEditMessageMedia(chatID int64, messageID int, inputMedia InputMedia) Edi } } +// NewEditMessageMediaAndMarkup allows you to edit media and keyboard content of a message +func NewEditMessageMediaAndMarkup(chatID int64, messageID int, inputMedia InputMedia, replyMarkup InlineKeyboardMarkup) EditMessageMediaConfig { + return EditMessageMediaConfig{ + BaseEdit: BaseEdit{ + BaseChatMessage: BaseChatMessage{ + ChatConfig: ChatConfig{ + ChatID: chatID, + }, + MessageID: messageID, + }, + ReplyMarkup: &replyMarkup, + }, + Media: inputMedia, + } +} + // NewEditMessagePhoto allows you to edit the photo content of a message. func NewEditMessagePhoto(chatID int64, messageID int, inputPhoto InputMediaPhoto) EditMessageMediaConfig { return NewEditMessageMedia(chatID, messageID, &inputPhoto)