From e8e00d1884af9d36a2335cb44f4248d309a819b2 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sat, 11 Apr 2026 15:40:39 +0700 Subject: [PATCH] fix(telegram): preserve italic content in htmlTagToMarkdown Go regexp's $1_ is parsed as a reference to a named group "1_" (underscores are valid identifier chars), not $1 followed by literal underscore. This dropped the captured italic content, turning foo into "__" instead of "_foo_". Use ${1}_ with explicit braces to delimit the group reference, and update the format_extended_test suite to assert the full round-trip for and . --- internal/channels/telegram/format.go | 6 +++-- .../channels/telegram/format_extended_test.go | 22 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/internal/channels/telegram/format.go b/internal/channels/telegram/format.go index e81bf411..5ab481b2 100644 --- a/internal/channels/telegram/format.go +++ b/internal/channels/telegram/format.go @@ -21,8 +21,10 @@ var htmlToMdReplacers = []struct { {regexp.MustCompile(`(?i)`), "\n"}, {regexp.MustCompile(`(?i)([\s\S]*?)`), "**$1**"}, {regexp.MustCompile(`(?i)([\s\S]*?)`), "**$1**"}, - {regexp.MustCompile(`(?i)([\s\S]*?)`), "_$1_"}, - {regexp.MustCompile(`(?i)([\s\S]*?)`), "_$1_"}, + // ${1}_ (not $1_) — Go regexp treats $1_ as a named group reference (identifier + // characters include `_`), which drops the capture. Curly braces delimit the group. + {regexp.MustCompile(`(?i)([\s\S]*?)`), "_${1}_"}, + {regexp.MustCompile(`(?i)([\s\S]*?)`), "_${1}_"}, {regexp.MustCompile(`(?i)([\s\S]*?)`), "~~$1~~"}, {regexp.MustCompile(`(?i)([\s\S]*?)`), "~~$1~~"}, {regexp.MustCompile(`(?i)([\s\S]*?)`), "~~$1~~"}, diff --git a/internal/channels/telegram/format_extended_test.go b/internal/channels/telegram/format_extended_test.go index e5eb09e6..8b7cd4fa 100644 --- a/internal/channels/telegram/format_extended_test.go +++ b/internal/channels/telegram/format_extended_test.go @@ -161,12 +161,17 @@ func TestMarkdownToTelegramHTML_HTMLTagsConvertedFirst(t *testing.T) { t.Errorf("bold should remain bold in output, got: %q", got) } }) - t.Run("html italic input produces output", func(t *testing.T) { - // italic goes through htmlTagToMarkdown (→ _italic_ with known $1_ caveat) - // then through markdownToTelegramHTML — the result is non-empty, no panics. + t.Run("html italic tag round-trips", func(t *testing.T) { + // italic → _italic_ → italic through the full pipeline. got := markdownToTelegramHTML("italic") - if got == "" { - t.Error("markdownToTelegramHTML should return non-empty for italic") + if !strings.Contains(got, "italic") { + t.Errorf("italic should remain italic in output, got: %q", got) + } + }) + t.Run("html em tag round-trips", func(t *testing.T) { + got := markdownToTelegramHTML("emphasis") + if !strings.Contains(got, "emphasis") { + t.Errorf("emphasis should become emphasis, got: %q", got) } }) t.Run("html strike tag round-trips", func(t *testing.T) { @@ -419,11 +424,8 @@ func TestHTMLTagToMarkdown(t *testing.T) { {"
", []string{"\n"}}, {"bold", []string{"**bold**"}}, {"bold", []string{"**bold**"}}, - // NOTE: / use "_$1_" replacement where Go regexp treats $1_ as - // a named group reference (not group 1 + literal _), so content is lost. - // This is documented behavior of the current implementation. - // The pipeline still works end-to-end because only appears in LLM - // output which gets processed through the full markdownToTelegramHTML chain. + {"italic", []string{"_italic_"}}, + {"italic", []string{"_italic_"}}, {"struck", []string{"~~struck~~"}}, {"deleted", []string{"~~deleted~~"}}, {"var", []string{"`var`"}},