mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-10-25 17:36:29 +08:00
fix(telegram): keep link targets literal during Markdown rendering
This commit is contained in:
1 parent
5f54fc5beb
commit
939b2f151f
3 files changed
+74
-7
No files matched your search
@@ -104,14 +104,27 @@ def _render_line(line: str) -> str:
|
||||
|
||||
|
||||
def _render_inline(text: str) -> str:
|
||||
links = {}
|
||||
source = text
|
||||
|
||||
def emphasis(value: str) -> str:
|
||||
value = _BOLD_RE.sub(r"<b>\1</b>", value)
|
||||
value = _BOLD_ALT_RE.sub(r"<b>\1</b>", value)
|
||||
value = _STRIKE_RE.sub(r"<s>\1</s>", value)
|
||||
value = _ITALIC_RE.sub(r"<i>\1</i>", value)
|
||||
return _ITALIC_ALT_RE.sub(r"<i>\1</i>", value)
|
||||
|
||||
def link(m: re.Match) -> str:
|
||||
url = m.group(2).replace('"', """)
|
||||
return f'<a href="{url}">{m.group(1)}</a>'
|
||||
token = f"\x01link{len(links)}\x01"
|
||||
while token in source:
|
||||
token += "\x01"
|
||||
links[token] = f'<a href="{url}">{emphasis(m.group(1))}</a>'
|
||||
return token
|
||||
|
||||
text = _LINK_RE.sub(link, text)
|
||||
text = _BOLD_RE.sub(r"<b>\1</b>", text)
|
||||
text = _BOLD_ALT_RE.sub(r"<b>\1</b>", text)
|
||||
text = _STRIKE_RE.sub(r"<s>\1</s>", text)
|
||||
text = _ITALIC_RE.sub(r"<i>\1</i>", text)
|
||||
text = _ITALIC_ALT_RE.sub(r"<i>\1</i>", text)
|
||||
# URLs are not Markdown source. Park the anchor while surrounding prose
|
||||
# is formatted, then restore it with the separately formatted label.
|
||||
text = emphasis(_LINK_RE.sub(link, text))
|
||||
for token, rendered in links.items():
|
||||
text = text.replace(token, rendered)
|
||||
return text
|
||||
@@ -0,0 +1,19 @@
|
||||
# ADR 027: fix(telegram): keep link targets literal during Markdown rendering
|
||||
|
||||
Status: Accepted for this candidate.
|
||||
|
||||
## Context
|
||||
|
||||
The Telegram renderer creates anchor tags before running emphasis substitutions over the complete HTML. URL path segments such as __archive__ become bold tags inside href, corrupting the destination and Telegram parse mode.
|
||||
|
||||
## Decision
|
||||
|
||||
Park rendered links during the emphasis passes, apply formatting to labels, and restore links afterward. Use collision-safe placeholders so original text is not mistaken for a parked link.
|
||||
|
||||
## Consequences
|
||||
|
||||
Link destinations remain escaped literal URLs; label, surrounding emphasis, and inline code formatting continue to work.
|
||||
|
||||
## Validation
|
||||
|
||||
The focused regression fails on the unmodified base and passes with this change. tests/test_telegram_markdown_link_targets.py tests/test_telegram_markdown_fences.py passed locally; changed Python files passed CI-equivalent Ruff F checks. These are local native-contract checks using controlled doubles, not live provider or channel acceptance.
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Markdown link targets are literal URLs, not emphasis source."""
|
||||
import pytest
|
||||
|
||||
from channel.telegram.telegram_markdown import to_telegram_html
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", [
|
||||
"https://example.invalid/__archive__/report",
|
||||
"https://example.invalid/a*b*c",
|
||||
"https://example.invalid/~~draft~~",
|
||||
"https://example.invalid/api?first=1&path=__value__",
|
||||
])
|
||||
def test_link_url_is_not_rewritten_as_inline_markup(url):
|
||||
expected = url.replace("&", "&")
|
||||
assert to_telegram_html(f"[report]({url})") == f'<a href="{expected}">report</a>'
|
||||
|
||||
|
||||
def test_link_label_and_surrounding_emphasis_keep_formatting():
|
||||
assert to_telegram_html("**[Read *this*](https://example.invalid/__docs__)**") == (
|
||||
'<b><a href="https://example.invalid/__docs__">Read <i>this</i></a></b>'
|
||||
)
|
||||
|
||||
|
||||
def test_inline_code_in_link_label_still_restores():
|
||||
assert to_telegram_html("[`__name__`](https://example.invalid/__docs__)") == (
|
||||
'<a href="https://example.invalid/__docs__"><code>__name__</code></a>'
|
||||
)
|
||||
|
||||
|
||||
def test_multiple_links_and_literal_placeholder_text_are_preserved():
|
||||
source = "\x01link0\x01 [one](https://example.invalid/__a__) and [two](https://example.invalid/__b__)"
|
||||
assert to_telegram_html(source) == (
|
||||
'\x01link0\x01 <a href="https://example.invalid/__a__">one</a> and '
|
||||
'<a href="https://example.invalid/__b__">two</a>'
|
||||
)
|
||||
Reference in new issue
Block a user