Telegram formatting#2
Conversation
…ating Previously, responses >= 4096 chars were sent as a .md file, and if that failed, truncated with "(response truncated)". Now long responses are split at paragraph/line boundaries into multiple messages. File upload is used as fallback only if chunk sending fails. https://claude.ai/code/session_01VtMjyBkcnrX8AZDuPj67XT
Long messages were sent as files (or truncated plain text), bypassing the markdown-to-HTML converter entirely. Now: split markdown into chunks, convert each to HTML, verify each fits in Telegram's 4096 char limit (accounting for HTML tag expansion), and send as multiple messages. Also adds checkbox rendering: [ ] → ☐, [x] → ☑ in bullet lists. Fallback chain: split HTML msgs → plain text → file → truncated. https://claude.ai/code/session_01VtMjyBkcnrX8AZDuPj67XT
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Hard break in splitMessage may split multi-byte UTF-8 characters
- Added UTF-8 boundary check that backs up from maxLen until finding a valid start byte before splitting, ensuring all chunks contain valid UTF-8.
Or push these changes by commenting:
@cursor push 8ee6ffb5b0
Preview (8ee6ffb5b0)
diff --git a/internal/telegram/handler.go b/internal/telegram/handler.go
--- a/internal/telegram/handler.go
+++ b/internal/telegram/handler.go
@@ -12,6 +12,7 @@
"strings"
"sync"
"time"
+ "unicode/utf8"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
@@ -849,9 +850,16 @@
continue
}
- // Hard break at maxLen.
- parts = append(parts, chunk)
- text = text[maxLen:]
+ // Hard break at maxLen, but respect UTF-8 character boundaries.
+ breakAt := maxLen
+ for breakAt > 0 && !utf8.RuneStart(text[breakAt]) {
+ breakAt--
+ }
+ if breakAt == 0 {
+ breakAt = maxLen // Avoid empty chunk; shouldn't happen with valid UTF-8.
+ }
+ parts = append(parts, text[:breakAt])
+ text = text[breakAt:]
}
return parts
}
diff --git a/internal/telegram/markdown_test.go b/internal/telegram/markdown_test.go
--- a/internal/telegram/markdown_test.go
+++ b/internal/telegram/markdown_test.go
@@ -2,6 +2,7 @@
import (
"testing"
+ "unicode/utf8"
)
func TestMarkdown_PlainText(t *testing.T) {
@@ -205,6 +206,29 @@
}
}
+func TestSplitMessage_UTF8Boundary(t *testing.T) {
+ // "日本語" is 9 bytes (3 chars × 3 bytes each). No newlines, so hard break is needed.
+ // Create a string that requires hard break in the middle of a multi-byte character.
+ text := "日本語日本語日本語" // 27 bytes, 9 characters
+ parts := splitMessage(text, 10)
+ if len(parts) < 2 {
+ t.Errorf("expected multiple parts, got %d: %v", len(parts), parts)
+ }
+ for i, p := range parts {
+ if !utf8.ValidString(p) {
+ t.Errorf("part %d is invalid UTF-8: %q", i, p)
+ }
+ }
+ // Verify all parts joined equal the original
+ joined := ""
+ for _, p := range parts {
+ joined += p
+ }
+ if joined != text {
+ t.Errorf("parts don't reassemble to original:\n got: %q\nwant: %q", joined, text)
+ }
+}
+
// --- helpers ---
func assertMD(t *testing.T, input, want string) {This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
|
|
||
| // Hard break at maxLen. | ||
| parts = append(parts, chunk) | ||
| text = text[maxLen:] |
There was a problem hiding this comment.
Hard break in splitMessage may split multi-byte UTF-8 characters
Low Severity
The hard-break fallback in splitMessage slices at a raw byte offset (text[:maxLen]), which can split a multi-byte UTF-8 character in half. This produces invalid UTF-8 in the resulting chunk, potentially causing garbled text in the Telegram message or an API rejection. The test suite already uses Cyrillic text, indicating multi-byte input is expected. Long paragraphs without newlines in CJK or other non-ASCII languages could trigger this path.



Note
Medium Risk
Moderate risk because it changes core Telegram response delivery logic (HTML conversion, splitting, fallbacks) and could affect message ordering/formatting or introduce truncation edge cases for long outputs.
Overview
Improves Telegram output formatting by converting Markdown checklist items in bullet lists into Unicode checkboxes (☐/☑) and adding tests for the new rendering.
Reworks
sendResponseto prefer HTML messages, then split and send long responses in multiple HTML/plain chunks (with retry fallbacks), only falling back to sending a.mddocument when chunked delivery fails. AddssplitMessage(paragraph/line-aware) plus unit tests to ensure chunks respect Telegram’s 4096-byte limit.Written by Cursor Bugbot for commit 3da2235. This will update automatically on new commits. Configure here.