ForcePilot/backend/package/yuxi/channels/adapters/imessage/formatter.py
Kris 8f352d91bb feat(imessage): 新增投票管理、审计日志功能并优化多项细节
1. 新增poll命令支持创建、关闭、列出投票
2. 新增审计日志记录功能
3. 优化远程附件URL安全校验逻辑
4. 修复表格匹配正则,支持包含竖线的表格分隔行
5. 新增媒体AI处理能力,支持图片描述和音频转录
6. 完善配置校验和错误处理
7. 重构文本发送逻辑,增加重试机制
8. 新增投票投票处理逻辑,支持数字快捷投票
2026-05-13 16:10:03 +08:00

125 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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