1. 新增poll命令支持创建、关闭、列出投票 2. 新增审计日志记录功能 3. 优化远程附件URL安全校验逻辑 4. 修复表格匹配正则,支持包含竖线的表格分隔行 5. 新增媒体AI处理能力,支持图片描述和音频转录 6. 完善配置校验和错误处理 7. 重构文本发送逻辑,增加重试机制 8. 新增投票投票处理逻辑,支持数字快捷投票
125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
from __future__ import annotations
|
||
|
||
import re
|
||
|
||
TABLE_ROW_RE = re.compile(r"^\|(.+)\|$")
|
||
TABLE_SEP_RE = re.compile(r"^\|[\s\-:\|]+\|$")
|
||
DIRECTIVE_RE = re.compile(r"<\s*/?directive[^>]*>", re.IGNORECASE)
|
||
|
||
|
||
def convert_markdown_tables(text: str) -> str:
|
||
"""将 Markdown 表格转换为可读的 bullets 格式。
|
||
|
||
iMessage 不支持 Markdown 表格渲染,此函数将表格行转为可读的列表。
|
||
"""
|
||
lines = text.split("\n")
|
||
result: list[str] = []
|
||
i = 0
|
||
while i < len(lines):
|
||
line = lines[i]
|
||
if i + 2 < len(lines) and TABLE_ROW_RE.match(line) and TABLE_SEP_RE.match(lines[i + 1]):
|
||
header = line
|
||
sep = lines[i + 1]
|
||
body_rows: list[str] = []
|
||
j = i + 2
|
||
while j < len(lines) and TABLE_ROW_RE.match(lines[j]):
|
||
body_rows.append(lines[j])
|
||
j += 1
|
||
|
||
converted = _table_to_bullets(header, sep, body_rows)
|
||
result.extend(converted)
|
||
i = j
|
||
continue
|
||
|
||
result.append(line)
|
||
i += 1
|
||
|
||
return "\n".join(result)
|
||
|
||
|
||
def _table_to_bullets(header: str, sep: str, body_rows: list[str]) -> list[str]:
|
||
header_cells = _parse_cells(header)
|
||
sep_cells = _parse_cells(sep)
|
||
|
||
if not header_cells:
|
||
return [header, sep] + body_rows
|
||
|
||
alignments = _detect_alignments(sep_cells, len(header_cells))
|
||
body_cells = [_parse_cells(row) for row in body_rows]
|
||
|
||
output: list[str] = []
|
||
for row_idx, cells in enumerate([header_cells] + body_cells):
|
||
if row_idx == 0:
|
||
output.append(f"• {', '.join(cells)}")
|
||
elif len(cells) <= 2:
|
||
output.append(" " + ": ".join(cells))
|
||
else:
|
||
parts: list[str] = []
|
||
for ci, (cell, hdr, align) in enumerate(zip(cells, header_cells, alignments)):
|
||
prefix = f" {hdr}: " if ci == 0 or align != "skip" else f" {hdr}: "
|
||
parts.append(f"{prefix}{cell}")
|
||
output.append("")
|
||
output.extend(parts)
|
||
|
||
return output
|
||
|
||
|
||
def _parse_cells(row: str) -> list[str]:
|
||
return [cell.strip() for cell in row.strip("|").split("|")]
|
||
|
||
|
||
def _detect_alignments(sep_cells: list[str], col_count: int) -> list[str]:
|
||
aligns: list[str] = []
|
||
for i in range(col_count):
|
||
if i < len(sep_cells):
|
||
cell = sep_cells[i]
|
||
left = cell.startswith(":")
|
||
right = cell.endswith(":")
|
||
if left and right:
|
||
aligns.append("center")
|
||
elif right:
|
||
aligns.append("right")
|
||
else:
|
||
aligns.append("left")
|
||
else:
|
||
aligns.append("left")
|
||
return aligns
|
||
|
||
|
||
def sanitize_imessage_text(text: str) -> str:
|
||
"""净化出站文本,移除 iMessage 中不支持的格式。
|
||
|
||
- 去除 \\r 字符(换行标准化)
|
||
- 去除 <directive> 内联指令标签
|
||
- 去除长度前缀损坏(如 \"123\\nThis is message\")
|
||
- 过滤控制字符
|
||
- 合并多余空白行
|
||
"""
|
||
text = text.replace("\r", "")
|
||
|
||
text = DIRECTIVE_RE.sub("", text)
|
||
|
||
text = _strip_length_prefix(text)
|
||
|
||
text = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]", "", text)
|
||
|
||
text = re.sub(r"\n{3,}", "\n\n", text)
|
||
text = text.strip()
|
||
|
||
return text
|
||
|
||
|
||
def _strip_length_prefix(text: str) -> str:
|
||
"""去除 iMessage 长度前缀损坏格式。
|
||
|
||
BlueBubbles 可能在某些消息前附加长度前缀如 \"42\\nHello world\",
|
||
去除数字换行前缀。
|
||
"""
|
||
match = re.match(r"^(\d+)\n", text)
|
||
if match:
|
||
prefix_len = int(match.group(1))
|
||
remaining = text[match.end() :]
|
||
if abs(len(remaining) - prefix_len) <= 5:
|
||
return remaining
|
||
return text
|