主要变更: 1. 重构导入顺序,统一模块导入规范 2. 提取通用方法到session模块,减少代码重复 3. 为缓存类添加线程/异步锁,修复并发安全问题 4. 新增入站处理器和发送管理器模块,拆分业务逻辑 5. 优化凭证队列,改为异步实现 6. 移除废弃的SSE_POLLING能力标识 7. 修复轮询投票解析逻辑 8. 优化Markdown转换规则,避免格式冲突 9. 完善连接控制器的异常处理 10. 新增发送静默消息的API支持
139 lines
3.5 KiB
Python
139 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
_ALLOWED_TAGS = {
|
|
"b",
|
|
"strong",
|
|
"i",
|
|
"em",
|
|
"u",
|
|
"ins",
|
|
"s",
|
|
"strike",
|
|
"del",
|
|
"code",
|
|
"pre",
|
|
"a",
|
|
}
|
|
|
|
_TAG_PATTERN = re.compile(r"</?(\w+)[^>]*>")
|
|
|
|
_TABLE_ROW_PATTERN = re.compile(r"^\|(.+)\|$", re.MULTILINE)
|
|
_TABLE_SEPARATOR_PATTERN = re.compile(r"^\|[\s\-:|]+\|$", re.MULTILINE)
|
|
|
|
|
|
def text_sanitizer(text: str) -> str:
|
|
if not text:
|
|
return ""
|
|
|
|
text = text.replace(" ", " ")
|
|
|
|
def _replace_tag(m: re.Match) -> str:
|
|
tag = m.group(1).lower()
|
|
if tag in _ALLOWED_TAGS:
|
|
return m.group(0)
|
|
return ""
|
|
|
|
text = _TAG_PATTERN.sub(_replace_tag, text)
|
|
text = text.strip()
|
|
return text
|
|
|
|
|
|
def markdown_to_whatsapp(md_text: str) -> str:
|
|
if not md_text:
|
|
return ""
|
|
|
|
text = md_text
|
|
|
|
text = re.sub(r"```(\w+)?\n(.*?)```", _code_block, text, flags=re.DOTALL)
|
|
text = re.sub(r"`([^`]+)`", r"```\1```", text)
|
|
|
|
text = _convert_tables(text)
|
|
|
|
# All formatting -> temp markers to prevent cross-contamination
|
|
text = re.sub(r"\*\*\*(.+?)\*\*\*", r"<BI>\1</BI>", text)
|
|
text = re.sub(r"___(.+?)___", r"<BI>\1</BI>", text)
|
|
text = re.sub(r"\*\*(.+?)\*\*", r"<B>\1</B>", text)
|
|
text = re.sub(r"__(.+?)__", r"<B>\1</B>", text)
|
|
text = re.sub(r"(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)", r"<I>\1</I>", text)
|
|
text = re.sub(r"(?<!_)_(?!_)(.+?)(?<!_)_(?!_)", r"<I>\1</I>", text)
|
|
|
|
text = text.replace("<BI>", "*_")
|
|
text = text.replace("</BI>", "_*")
|
|
text = text.replace("<B>", "*")
|
|
text = text.replace("</B>", "*")
|
|
text = text.replace("<I>", "_")
|
|
text = text.replace("</I>", "_")
|
|
|
|
text = re.sub(r"~~(.+?)~~", r"~\1~", text)
|
|
|
|
text = re.sub(r"^### (.+)$", r"*_\1_*", text, flags=re.MULTILINE)
|
|
text = re.sub(r"^## (.+)$", r"*_\1_*", text, flags=re.MULTILINE)
|
|
text = re.sub(r"^# (.+)$", r"*_\1_*", text, flags=re.MULTILINE)
|
|
|
|
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", _link, text)
|
|
|
|
text = re.sub(r"^\- (.+)$", r"• \1", text, flags=re.MULTILINE)
|
|
text = re.sub(r"^\d+\. (.+)$", r"• \1", text, flags=re.MULTILINE)
|
|
|
|
text = text.replace("<br>", "\n")
|
|
text = re.sub(r"\n{3,}", "\n\n", text)
|
|
|
|
return text.strip()
|
|
|
|
|
|
def _convert_tables(text: str) -> str:
|
|
|
|
def _replace_table(m: re.Match) -> str:
|
|
table_block = m.group(0)
|
|
lines = table_block.strip().split("\n")
|
|
if len(lines) < 2:
|
|
return table_block
|
|
|
|
data_lines = []
|
|
header = None
|
|
for line in lines:
|
|
stripped = line.strip()
|
|
if not stripped.startswith("|"):
|
|
continue
|
|
if _TABLE_SEPARATOR_PATTERN.match(stripped):
|
|
continue
|
|
cells = [c.strip() for c in stripped[1:-1].split("|")]
|
|
if header is None:
|
|
header = " | ".join(cells)
|
|
else:
|
|
data_lines.append("• " + " | ".join(cells))
|
|
|
|
if header is None:
|
|
return table_block
|
|
|
|
result = [header]
|
|
result.extend(data_lines)
|
|
return "\n".join(result)
|
|
|
|
text = re.sub(
|
|
r"(?:^|\n)(\|.+\|\s*\n){2,}",
|
|
_replace_table,
|
|
text,
|
|
flags=re.MULTILINE,
|
|
)
|
|
return text
|
|
|
|
|
|
def _code_block(m: re.Match) -> str:
|
|
lang = m.group(1) or ""
|
|
code = m.group(2).strip()
|
|
prefix = f"[{lang}]\n" if lang else ""
|
|
return f"```{prefix}{code}```"
|
|
|
|
|
|
def _link(m: re.Match) -> str:
|
|
text = m.group(1)
|
|
url = m.group(2)
|
|
if text == url:
|
|
return url
|
|
if not text.strip():
|
|
return url
|
|
return f"{text}\n({url})"
|