from __future__ import annotations
import re
from html import escape
from typing import Any
from yuxi.channels.models import ChannelResponse
_ESCAPE_CHARS = {"&": "&", "<": "<", ">": ">"}
_CODE_BLOCK_RE = re.compile(r"```(?:\w+)?\n.+?```", re.DOTALL)
_INLINE_CODE_RE = re.compile(r"`([^`\n]+?)`")
_PLACEHOLDER_PREFIX = "\x00MDPLACEHOLDER"
TRUNCATION_MARKER = "\n...(内容过长已截断)"
def markdown_to_html(text: str) -> str:
code_blocks: dict[str, str] = {}
text = _protect_code_spans(text, _CODE_BLOCK_RE, code_blocks)
text = _protect_code_spans(text, _INLINE_CODE_RE, code_blocks)
text = re.sub(r"\*\*(.+?)\*\*", r"\1", text)
text = re.sub(r"(?\1", text)
text = re.sub(r"(?\1", text)
text = re.sub(r"~~(.+?)~~", r"\1", text)
text = re.sub(r"__(\w.*?\w)__", r"\1", text)
text = re.sub(r"\[(.+?)\]\((.+?)\)", r'\1', text)
text = re.sub(r"\|\|(.+?)\|\|", r"
{safe}")
elif placeholder in result:
inner = code_text.strip("`")
safe = escape(inner, quote=False)
result = result.replace(placeholder, f"{safe}")
return result
def _sanitize_html(text: str) -> str:
result = []
depth = 0
for ch in text:
if ch == "<":
depth += 1
elif ch == ">":
depth = max(0, depth - 1)
elif depth == 0:
if ch in _ESCAPE_CHARS:
result.append(_ESCAPE_CHARS[ch])
continue
result.append(ch)
return "".join(result)
def build_basic_outbound(
response: ChannelResponse,
*,
text_field: str = "text",
text: str | None = None,
extra: dict[str, Any] | None = None,
) -> dict[str, Any]:
payload: dict[str, Any] = {text_field: text or response.content}
if response.reply_to_message_id:
payload["reply_to_message_id"] = response.reply_to_message_id
if extra:
payload.update(extra)
return payload