新增淘宝(Taobao)渠道扩展,支持在 Yuxi 平台中集成淘宝电商客服渠道。 包含以下功能模块: - client: 淘宝 API 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - webhook: Webhook 事件处理 - outbound: 外发消息管理 - message: 消息处理 - pairing: 用户配对与绑定 - security: 安全校验 - crypto: 加解密处理 - status: 会话状态管理 - tools: Agent 工具集成 - types: 类型定义
90 lines
2.2 KiB
Python
90 lines
2.2 KiB
Python
import logging
|
|
|
|
from yuxi.channel.extensions.taobao.client import TaobaoAPIError, TaobaoClient
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
MAX_TEXT_LENGTH = 2000
|
|
|
|
_GLOBAL_CLIENT: TaobaoClient | None = None
|
|
|
|
|
|
def set_global_client(client: TaobaoClient | None) -> None:
|
|
global _GLOBAL_CLIENT
|
|
_GLOBAL_CLIENT = client
|
|
|
|
|
|
def get_client() -> TaobaoClient | None:
|
|
return _GLOBAL_CLIENT
|
|
|
|
|
|
async def send_text(
|
|
target_id: str,
|
|
content: str,
|
|
*,
|
|
reply_to_id: str | None = None,
|
|
thread_id: str | None = None,
|
|
account_id: str | None = None,
|
|
session: str | None = None,
|
|
) -> None:
|
|
client = _GLOBAL_CLIENT
|
|
if client is None:
|
|
logger.error("Taobao client not initialized")
|
|
return
|
|
|
|
try:
|
|
resp = await client.send_customer_message(
|
|
to_user=target_id,
|
|
content=content[:MAX_TEXT_LENGTH],
|
|
msg_type=0,
|
|
session=session,
|
|
)
|
|
logger.debug("Taobao message sent to %s: %s", target_id, resp)
|
|
except TaobaoAPIError as e:
|
|
logger.error("Taobao send_text failed to %s: %s", target_id, e)
|
|
|
|
|
|
async def send_media(
|
|
target_id: str,
|
|
media_url: str,
|
|
media_type: str,
|
|
*,
|
|
reply_to_id: str | None = None,
|
|
thread_id: str | None = None,
|
|
session: str | None = None,
|
|
) -> None:
|
|
client = _GLOBAL_CLIENT
|
|
if client is None:
|
|
logger.error("Taobao client not initialized")
|
|
return
|
|
|
|
msg_type = _media_type_to_int(media_type)
|
|
try:
|
|
await client.send_customer_message(
|
|
to_user=target_id,
|
|
content="",
|
|
msg_type=msg_type,
|
|
media_id=media_url,
|
|
session=session,
|
|
)
|
|
except TaobaoAPIError as e:
|
|
logger.error("Taobao send_media failed to %s: %s", target_id, e)
|
|
|
|
|
|
def _media_type_to_int(media_type: str) -> int:
|
|
mapping = {"image": 1, "file": 4}
|
|
return mapping.get(media_type, 1)
|
|
|
|
|
|
def chunk_text(text: str, limit: int = MAX_TEXT_LENGTH) -> list[str]:
|
|
chunks = []
|
|
while len(text) > limit:
|
|
split_at = text.rfind("\n", 0, limit)
|
|
if split_at == -1:
|
|
split_at = limit
|
|
chunks.append(text[:split_at])
|
|
text = text[split_at:].lstrip("\n")
|
|
if text:
|
|
chunks.append(text)
|
|
return chunks
|