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)
+ }
+}