import html
import re
MAX_SSML_CHARS = 8000
VOICE_FRIENDLY_MAX = 1000
class MarkdownToSSML:
def __init__(self, max_chars: int = VOICE_FRIENDLY_MAX):
self._max_chars = max_chars
def convert(self, markdown_text: str) -> str:
if not markdown_text:
return ""
text = self._strip_markdown(markdown_text)
text = html.escape(text, quote=False)
text = self._restore_placeholders(text)
text = self._truncate_if_needed(text)
return f"{text}"
def _strip_markdown(self, md: str) -> str:
self._placeholders = []
p = self._ph
md = re.sub(r'```[\s\S]*?```', p('此处有代码,请在手机 App 中查看。'), md)
md = re.sub(r'`([^`]+)`', r'\1', md)
md = re.sub(r'!\[.*?\]\(.*?\)', p('此处有图片。'), md)
md = re.sub(r'\*\*(.+?)\*\*', lambda m: p(f'{m.group(1)}'), md)
md = re.sub(r'\*([^*]+)\*', lambda m: p(f'{m.group(1)}'), md)
md = re.sub(r'__([^_]+)__', r'\1', md)
md = re.sub(r'_([^_]+)_', r'\1', md)
md = re.sub(r'~~(.+?)~~', r'\1', md)
md = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', md)
md = re.sub(r'^#{1,6}\s+', '', md, flags=re.MULTILINE)
md = re.sub(r'^[\-\*\+]\s+', '', md, flags=re.MULTILINE)
md = re.sub(r'^\d+\.\s+', '', md, flags=re.MULTILINE)
md = re.sub(r'^>\s+', '', md, flags=re.MULTILINE)
md = md.replace('---', p(''))
md = md.replace('***', p(''))
md = re.sub(r'\|.*\|', p('表格内容请在手机 App 中查看。'), md)
md = re.sub(r'https?://\S+', '', md)
return md.strip()
def _ph(self, s: str) -> str:
placeholder = f"\x00SSML{len(self._placeholders)}\x00"
self._placeholders.append(s)
return placeholder
def _restore_placeholders(self, text: str) -> str:
for i, val in enumerate(self._placeholders):
text = text.replace(f"\x00SSML{i}\x00", val)
return text
def _truncate_if_needed(self, text: str) -> str:
if len(text) <= self._max_chars:
return text
truncated = text[:self._max_chars]
delimiters = ['。', '.', '\n', ';', ';', ',']
best_pos = -1
for delim in delimiters:
pos = truncated.rfind(delim)
if pos > best_pos:
best_pos = pos
if best_pos > 0:
truncated = truncated[:best_pos + 1]
truncated += '以上为部分回答,完整内容请在手机 App 中查看。'
return truncated
def ssml_safe(text: str) -> str:
if not text:
return ""
text = html.escape(text, quote=False)
return f"{text}"