From b88715d6d33b1eb8eaa1db6870b973df291afbaf Mon Sep 17 00:00:00 2001 From: ScuroNeko Date: Wed, 14 Jan 2026 12:07:05 +0300 Subject: [PATCH] some fixes --- bot.go | 62 +++++++++++++++++++++++++++++++++++++++++----- methods.go | 73 +++++++++++++++++++++++++++++++++++++++++++----------- utils.go | 21 +++++++++++++++- 3 files changed, 135 insertions(+), 21 deletions(-) diff --git a/bot.go b/bot.go index bf9a0bc..c63e761 100644 --- a/bot.go +++ b/bot.go @@ -11,8 +11,8 @@ import ( "strings" "github.com/redis/go-redis/v9" + "github.com/vinovest/sqlx" "go.mongodb.org/mongo-driver/v2/mongo" - "gorm.io/gorm" ) type ParseMode string @@ -74,7 +74,7 @@ type MsgContext struct { } type DatabaseContext struct { - PostgresSQL *gorm.DB + PostgresSQL *sqlx.DB MongoDB *mongo.Client Redis *redis.Client } @@ -217,6 +217,10 @@ func (b *Bot) Run() { } u := queue.Dequeue() + if u == nil { + b.logger.Error("update is nil") + continue + } ctx := &MsgContext{ Bot: b, Update: u, @@ -298,18 +302,28 @@ func (b *Bot) checkPrefixes(text string) (string, bool) { return "", false } -func (ctx *MsgContext) Answer(text string) { - _, err := ctx.Bot.SendMessage(&SendMessageP{ +type AnswerMessage struct { + MessageID int + IsMedia bool + ctx *MsgContext +} + +func (ctx *MsgContext) Answer(text string) *AnswerMessage { + msg, err := ctx.Bot.SendMessage(&SendMessageP{ ChatID: ctx.Msg.Chat.ID, Text: text, ParseMode: ParseMD, }) if err != nil { ctx.Bot.logger.Error(err) + return nil + } + return &AnswerMessage{ + MessageID: msg.MessageID, ctx: ctx, IsMedia: false, } } -func (ctx *MsgContext) AnswerPhoto(photoId string, text string) { +func (ctx *MsgContext) AnswerPhoto(photoId string, text string) *AnswerMessage { _, err := ctx.Bot.SendPhoto(&SendPhotoP{ ChatID: ctx.Msg.Chat.ID, Caption: text, @@ -319,6 +333,41 @@ func (ctx *MsgContext) AnswerPhoto(photoId string, text string) { if err != nil { ctx.Bot.logger.Error(err) } + return &AnswerMessage{ + MessageID: ctx.Msg.MessageID, ctx: ctx, IsMedia: true, + } +} + +func (m *AnswerMessage) Edit(text string) *AnswerMessage { + var msg *Message + var err error + if m.IsMedia { + msg, err = m.ctx.Bot.EditMessageCaption(&EditMessageCaptionP{ + MessageID: m.MessageID, + ChatID: m.ctx.Msg.Chat.ID, + Caption: text, + ParseMode: ParseMD, + }) + } else { + msg, err = m.ctx.Bot.EditMessageText(&EditMessageTextP{ + MessageID: m.MessageID, + ChatID: m.ctx.Msg.Chat.ID, + Text: text, + ParseMode: ParseMD, + }) + } + if err != nil { + m.ctx.Bot.logger.Error(err) + } + m.MessageID = msg.MessageID + return m +} + +func (m *AnswerMessage) Delete() { + _, err := m.ctx.Bot.DeleteMessage(&DeleteMessageP{MessageID: m.MessageID, ChatID: m.ctx.Msg.Chat.ID}) + if err != nil { + m.ctx.Bot.logger.Error(err) + } } func (ctx *MsgContext) Error(err error) { @@ -351,7 +400,7 @@ type ApiResponseA struct { } // request is a low-level call to api. -func (b *Bot) request(methodName string, params map[string]interface{}) (map[string]interface{}, error) { +func (b *Bot) request(methodName string, params any) (map[string]interface{}, error) { var buf bytes.Buffer err := json.NewEncoder(&buf).Encode(params) if err != nil { @@ -370,6 +419,7 @@ func (b *Bot) request(methodName string, params map[string]interface{}) (map[str if err != nil { return nil, err } + defer r.Body.Close() data, err := io.ReadAll(r.Body) if err != nil { return nil, err diff --git a/methods.go b/methods.go index 2793a48..87375fd 100644 --- a/methods.go +++ b/methods.go @@ -2,7 +2,7 @@ package laniakea import "fmt" -var NO_PARAMS = make(map[string]interface{}) +var NoParams = make(map[string]interface{}) func (b *Bot) Updates() ([]*Update, error) { params := make(map[string]interface{}) @@ -40,7 +40,7 @@ func (b *Bot) Updates() ([]*Update, error) { } func (b *Bot) GetMe() (*User, error) { - data, err := b.request("getMe", NO_PARAMS) + data, err := b.request("getMe", NoParams) if err != nil { return nil, err } @@ -53,8 +53,8 @@ type SendMessageP struct { BusinessConnectionID string `json:"business_connection_id,omitempty"` ChatID int `json:"chat_id"` MessageThreadID int `json:"message_thread_id,omitempty"` - Text string `json:"text"` ParseMode ParseMode `json:"parse_mode,omitempty"` + Text string `json:"text"` Entities []*MessageEntity `json:"entities,omitempty"` LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"` DisableNotifications bool `json:"disable_notifications,omitempty"` @@ -67,11 +67,7 @@ type SendMessageP struct { } func (b *Bot) SendMessage(params *SendMessageP) (*Message, error) { - dataP, err := StructToMap(params) - if err != nil { - return nil, err - } - data, err := b.request("sendMessage", dataP) + data, err := b.request("sendMessage", params) if err != nil { return nil, err } @@ -84,9 +80,9 @@ type SendPhotoP struct { BusinessConnectionID string `json:"business_connection_id,omitempty"` ChatID int `json:"chat_id"` MessageThreadID int `json:"message_thread_id,omitempty"` + ParseMode ParseMode `json:"parse_mode,omitempty"` Photo string `json:"photo"` Caption string `json:"caption,omitempty"` - ParseMode ParseMode `json:"parse_mode,omitempty"` CaptionEntities []*MessageEntity `json:"caption_entities,omitempty"` ShowCaptionAboveMedia bool `json:"show_caption_above_media"` HasSpoiler bool `json:"has_spoiler"` @@ -97,11 +93,60 @@ type SendPhotoP struct { } func (b *Bot) SendPhoto(params *SendPhotoP) (*Message, error) { - dataP, err := StructToMap(params) - if err != nil { - return nil, err - } - data, err := b.request("sendPhoto", dataP) + data, err := b.request("sendPhoto", params) + if err != nil { + return nil, err + } + message := new(Message) + err = MapToStruct(data, message) + return message, err +} + +type EditMessageTextP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id,omitempty"` + MessageID int `json:"message_id,omitempty"` + InlineMessageID string `json:"inline_message_id,omitempty"` + Text string `json:"text"` + ParseMode ParseMode `json:"parse_mode,omitempty"` +} + +func (b *Bot) EditMessageText(params *EditMessageTextP) (*Message, error) { + data, err := b.request("editMessageText", params) + if err != nil { + return nil, err + } + message := new(Message) + err = MapToStruct(data, message) + return message, err +} + +type EditMessageCaptionP struct { + BusinessConnectionID string `json:"business_connection_id,omitempty"` + ChatID int `json:"chat_id,omitempty"` + MessageID int `json:"message_id,omitempty"` + InlineMessageID string `json:"inline_message_id,omitempty"` + Caption string `json:"caption"` + ParseMode ParseMode `json:"parse_mode,omitempty"` +} + +func (b *Bot) EditMessageCaption(params *EditMessageCaptionP) (*Message, error) { + data, err := b.request("editMessageCaption", params) + if err != nil { + return nil, err + } + message := new(Message) + err = MapToStruct(data, message) + return message, err +} + +type DeleteMessageP struct { + ChatID int `json:"chat_id"` + MessageID int `json:"message_id"` +} + +func (b *Bot) DeleteMessage(params *DeleteMessageP) (*Message, error) { + data, err := b.request("deleteMessage", params) if err != nil { return nil, err } diff --git a/utils.go b/utils.go index e8c6385..e015dc1 100644 --- a/utils.go +++ b/utils.go @@ -1,6 +1,10 @@ package laniakea -import "encoding/json" +import ( + "encoding/json" + "fmt" + "strings" +) func MapToStruct(m map[string]interface{}, s interface{}) error { data, err := json.Marshal(m) @@ -33,3 +37,18 @@ func Map[T, V any](ts []T, fn func(T) V) []V { } return result } + +func EscapeMarkdown(s string) string { + s = strings.ReplaceAll(s, "_", "\\_") + s = strings.ReplaceAll(s, "*", "\\*") + s = strings.ReplaceAll(s, "[", "\\[") + return strings.ReplaceAll(s, "`", "\\`") +} + +func EscapeMarkdownV2(s string) string { + symbols := []string{"_", "*", "[", "]", "(", ")", "~", "`", ">", "#", "+", "-", "=", "|", "{", "}", ".", "!"} + for _, symbol := range symbols { + s = strings.ReplaceAll(s, symbol, fmt.Sprintf("\\%s", symbol)) + } + return s +}