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 <i>foo</i> 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 <i> and <em>.
This commit is contained in:
viettranx committed 2026-04-11 21:22:23 +07:00
1 parent eaddc68796
commit e8e00d1884
2 files changed
+16 -12

No files matched your search

+4 -2
View File
@@ -21,8 +21,10 @@ var htmlToMdReplacers = []struct {
{regexp.MustCompile(`(?i)</?p\s*>`), "\n"},
{regexp.MustCompile(`(?i)<b>([\s\S]*?)</b>`), "**$1**"},
{regexp.MustCompile(`(?i)<strong>([\s\S]*?)</strong>`), "**$1**"},
{regexp.MustCompile(`(?i)<i>([\s\S]*?)</i>`), "_$1_"},
{regexp.MustCompile(`(?i)<em>([\s\S]*?)</em>`), "_$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)<i>([\s\S]*?)</i>`), "_${1}_"},
{regexp.MustCompile(`(?i)<em>([\s\S]*?)</em>`), "_${1}_"},
{regexp.MustCompile(`(?i)<s>([\s\S]*?)</s>`), "~~$1~~"},
{regexp.MustCompile(`(?i)<strike>([\s\S]*?)</strike>`), "~~$1~~"},
{regexp.MustCompile(`(?i)<del>([\s\S]*?)</del>`), "~~$1~~"},
@@ -161,12 +161,17 @@ func TestMarkdownToTelegramHTML_HTMLTagsConvertedFirst(t *testing.T) {
t.Errorf("<b>bold</b> should remain bold in output, got: %q", got)
}
})
t.Run("html italic input produces output", func(t *testing.T) {
// <i>italic</i> 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) {
// <i>italic</i> → _italic_ → <i>italic</i> through the full pipeline.
got := markdownToTelegramHTML("<i>italic</i>")
if got == "" {
t.Error("markdownToTelegramHTML should return non-empty for <i>italic</i>")
if !strings.Contains(got, "<i>italic</i>") {
t.Errorf("<i>italic</i> should remain italic in output, got: %q", got)
}
})
t.Run("html em tag round-trips", func(t *testing.T) {
got := markdownToTelegramHTML("<em>emphasis</em>")
if !strings.Contains(got, "<i>emphasis</i>") {
t.Errorf("<em>emphasis</em> should become <i>emphasis</i>, got: %q", got)
}
})
t.Run("html strike tag round-trips", func(t *testing.T) {
@@ -419,11 +424,8 @@ func TestHTMLTagToMarkdown(t *testing.T) {
{"<br/>", []string{"\n"}},
{"<b>bold</b>", []string{"**bold**"}},
{"<strong>bold</strong>", []string{"**bold**"}},
// NOTE: <i>/<em> 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 <i> only appears in LLM
// output which gets processed through the full markdownToTelegramHTML chain.
{"<i>italic</i>", []string{"_italic_"}},
{"<em>italic</em>", []string{"_italic_"}},
{"<s>struck</s>", []string{"~~struck~~"}},
{"<del>deleted</del>", []string{"~~deleted~~"}},
{"<code>var</code>", []string{"`var`"}},