From f13baa9af54c842234f3b54aa936d661378388ea Mon Sep 17 00:00:00 2001 From: Sangeeth Thilakarathna <46221775+sanmaxdev@users.noreply.github.com> Date: Mon, 24 Aug 2026 16:33:37 +0530 Subject: [PATCH] fix(tgbot): split long messages at line boundaries (#6293) Individual-link batches contain single line breaks, so the previous blank-line-only pagination could send oversized replies unchanged. Co-authored-by: sanmaxdev --- internal/web/service/tgbot/tgbot_send.go | 65 ++++++++++++------- internal/web/service/tgbot/tgbot_send_test.go | 35 ++++++++++ 2 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 internal/web/service/tgbot/tgbot_send_test.go diff --git a/internal/web/service/tgbot/tgbot_send.go b/internal/web/service/tgbot/tgbot_send.go index b53aa6400..b86a13b7b 100644 --- a/internal/web/service/tgbot/tgbot_send.go +++ b/internal/web/service/tgbot/tgbot_send.go @@ -77,6 +77,48 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) { t.SendMsgToTgbot(chatId, msg, ReplyMarkup) } +const telegramPageLimit = 2000 + +func pageMessage(message string, limit int) []string { + if len(message) <= limit { + return []string{message} + } + + pages := make([]string, 0) + for _, block := range strings.Split(message, "\r\n\r\n") { + for _, page := range splitMessageLines(block, limit) { + last := len(pages) - 1 + if last >= 0 && len(pages[last])+len("\r\n\r\n")+len(page) <= limit { + pages[last] += "\r\n\r\n" + page + continue + } + pages = append(pages, page) + } + } + if len(pages) > 0 && strings.TrimSpace(pages[len(pages)-1]) == "" { + pages = pages[:len(pages)-1] + } + return pages +} + +func splitMessageLines(block string, limit int) []string { + if len(block) <= limit { + return []string{block} + } + + lines := strings.Split(block, "\r\n") + pages := []string{lines[0]} + for _, line := range lines[1:] { + last := len(pages) - 1 + if len(pages[last])+len("\r\n")+len(line) > limit { + pages = append(pages, line) + continue + } + pages[last] += "\r\n" + line + } + return pages +} + // SendMsgToTgbot sends a message to the Telegram bot with optional reply markup. func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.ReplyMarkup) { if !isRunning { @@ -88,28 +130,7 @@ func (t *Tgbot) SendMsgToTgbot(chatId int64, msg string, replyMarkup ...telego.R return } - var allMessages []string - limit := 2000 - - // paging message if it is big - if len(msg) > limit { - messages := strings.Split(msg, "\r\n\r\n") - lastIndex := -1 - - for _, message := range messages { - if (len(allMessages) == 0) || (len(allMessages[lastIndex])+len(message) > limit) { - allMessages = append(allMessages, message) - lastIndex++ - } else { - allMessages[lastIndex] += "\r\n\r\n" + message - } - } - if strings.TrimSpace(allMessages[len(allMessages)-1]) == "" { - allMessages = allMessages[:len(allMessages)-1] - } - } else { - allMessages = append(allMessages, msg) - } + allMessages := pageMessage(msg, telegramPageLimit) for n, message := range allMessages { params := telego.SendMessageParams{ ChatID: tu.ID(chatId), diff --git a/internal/web/service/tgbot/tgbot_send_test.go b/internal/web/service/tgbot/tgbot_send_test.go new file mode 100644 index 000000000..054162774 --- /dev/null +++ b/internal/web/service/tgbot/tgbot_send_test.go @@ -0,0 +1,35 @@ +package tgbot + +import ( + "strings" + "testing" +) + +func TestPageMessageSplitsLinkListWithoutBlankLines(t *testing.T) { + var message strings.Builder + message.WriteString("Individual links:\r\n") + for range 50 { + message.WriteString("vless://" + strings.Repeat("a", 300) + "\r\n") + } + + pages := pageMessage(message.String(), telegramPageLimit) + if len(pages) < 2 { + t.Fatalf("pageMessage() returned %d page, want multiple", len(pages)) + } + + links := 0 + for index, page := range pages { + if len(page) > telegramPageLimit { + t.Errorf("page %d has %d bytes, want at most %d", index, len(page), telegramPageLimit) + } + openingTags := strings.Count(page, "") + closingTags := strings.Count(page, "") + if openingTags != closingTags { + t.Errorf("page %d has %d opening tags and %d closing tags", index, openingTags, closingTags) + } + links += openingTags + } + if links != 50 { + t.Errorf("pages contain %d links, want 50", links) + } +}