ForcePilot/backend/package/yuxi/channel/extensions/intercom/format.py
Kris 2ddcf1cc55 feat(channel): 添加 Intercom 渠道扩展
新增 Intercom 渠道扩展,支持在 Yuxi 平台中集成 Intercom 客服渠道。

包含以下功能模块:
- client: Intercom API 客户端封装
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- streaming: 流式消息处理
- format: 消息格式转换
- outbound: 外发消息管理
- handoff: 转人工会话切换
- pairing: 用户配对与绑定
- security: 安全签名校验
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- session_guard: 会话防护
- types: 类型定义
2026-05-21 11:01:49 +08:00

107 lines
3.2 KiB
Python

import html
import re
from html.parser import HTMLParser
class HTMLToTextParser(HTMLParser):
def __init__(self):
super().__init__()
self._parts: list[str] = []
self._skip_depth = 0
self._current_href: str | None = None
def handle_starttag(self, tag, attrs):
if tag in ("script", "style", "head", "meta", "link"):
self._skip_depth += 1
if tag == "a":
for name, value in attrs:
if name == "href":
self._current_href = value
break
def handle_endtag(self, tag):
if tag in ("script", "style", "head", "meta", "link") and self._skip_depth > 0:
self._skip_depth -= 1
if tag in ("p", "br", "li", "div", "tr", "h1", "h2", "h3", "h4", "h5", "h6"):
self._parts.append("\n")
if tag == "a" and self._current_href:
self._parts.append(f" ({self._current_href})")
self._current_href = None
def handle_data(self, data):
if self._skip_depth == 0:
self._parts.append(data)
def get_text(self) -> str:
return "".join(self._parts).strip()
def html_to_text(html_body: str) -> str:
parser = HTMLToTextParser()
parser.feed(html_body)
return parser.get_text()
def markdown_to_html(md_text: str) -> str:
text = html.escape(md_text)
text = re.sub(r"```(\w+)?\n?(.*?)```", r"<pre><code>\2</code></pre>", text, flags=re.DOTALL)
text = re.sub(r"`([^`]+)`", r"<code>\1</code>", text)
text = re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
text = re.sub(r"\*(.+?)\*", r"<em>\1</em>", text)
text = re.sub(r"~~(.+?)~~", r"<s>\1</s>", text)
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<a href="\2">\1</a>', text)
lines = text.split("\n")
result: list[str] = []
list_buffer: list[str] = []
in_list = False
for line in lines:
stripped = line.strip()
ul_match = re.match(r"^[\-\*]\s+(.+)$", stripped)
ol_match = re.match(r"^\d+\.\s+(.+)$", stripped)
if ul_match:
if not in_list:
in_list = True
list_buffer = []
list_buffer.append(f"<li>{ul_match.group(1)}</li>")
continue
if ol_match:
if not in_list:
in_list = True
list_buffer = []
list_buffer.append(f"<li>{ol_match.group(1)}</li>")
continue
if in_list:
in_list = False
result.append(f"<ul>{''.join(list_buffer)}</ul>")
list_buffer = []
heading_match = re.match(r"^#{1,6}\s+(.+)$", stripped)
if heading_match:
result.append(f"<p><strong>{heading_match.group(1)}</strong></p>")
continue
if not stripped:
result.append("<br>")
elif stripped.startswith("<") and not stripped.startswith("<br"):
result.append(stripped)
else:
result.append(f"<p>{stripped}</p>")
if in_list and list_buffer:
result.append(f"<ul>{''.join(list_buffer)}</ul>")
return "\n".join(result)
def get_agent_prompt_rules() -> list[str]:
from yuxi.channel.extensions.intercom.constants import INTERCOM_AGENT_PROMPT_RULES
return INTERCOM_AGENT_PROMPT_RULES