ForcePilot/backend/package/yuxi/channel/extensions/taobao/gateway.py
Kris 904ab9a6c2 feat(channel): 添加淘宝渠道扩展
新增淘宝(Taobao)渠道扩展,支持在 Yuxi 平台中集成淘宝电商客服渠道。

包含以下功能模块:
- client: 淘宝 API 客户端封装
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- message: 消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- crypto: 加解密处理
- status: 会话状态管理
- tools: Agent 工具集成
- types: 类型定义
2026-05-21 11:46:45 +08:00

106 lines
3.9 KiB
Python

import asyncio
import logging
import time
from datetime import datetime, timedelta
from yuxi.channel.extensions.taobao.client import TaobaoClient
from yuxi.channel.extensions.taobao.config import TaobaoConfigAdapter
from yuxi.channel.extensions.taobao.types import TaobaoAccount
logger = logging.getLogger(__name__)
_current_gateway: TaobaoGateway | None = None
class TaobaoGateway:
def __init__(self):
self._running = False
self._account: TaobaoAccount | None = None
self._client: TaobaoClient | None = None
self._token_refresh_task: asyncio.Task | None = None
self._message_queue: asyncio.Queue | None = None
self._last_message_at: float | None = None
async def start(self, ctx) -> dict:
global _current_gateway
account = _resolve_account(ctx)
if not account.is_configured():
logger.warning("Taobao account %s not configured, skipping", account.account_id)
return {"running": False, "reason": "not-configured", "account_id": account.account_id}
self._account = account
self._client = TaobaoClient(
app_key=account.app_key,
app_secret=account.app_secret,
sign_method=account.sign_method,
sandbox=account.sandbox,
timeout=account.timeout,
)
self._message_queue = asyncio.Queue(maxsize=1000)
_current_gateway = self
if account.has_valid_token():
self._token_refresh_task = asyncio.create_task(self._token_refresh_loop())
self._running = True
logger.info("Taobao gateway started for account %s", account.account_id)
return {"running": True, "account_id": account.account_id, "client": self._client}
async def stop(self, ctx) -> None:
self._running = False
if self._token_refresh_task:
self._token_refresh_task.cancel()
self._token_refresh_task = None
self._message_queue = None
self._client = None
self._account = None
logger.info("Taobao gateway stopped")
async def _token_refresh_loop(self):
while self._running:
await asyncio.sleep(600)
if not self._account or not self._account.refresh_token:
continue
try:
resp = await self._client.refresh_token(self._account.refresh_token)
token_data = resp.get("top_auth_token_create_response", {}).get("token_result", {})
if token_data:
self._account.access_token = token_data.get("access_token", "")
self._account.refresh_token = token_data.get("refresh_token", "")
expires_in = token_data.get("expires_in", 0)
self._account.token_expires_at = datetime.now() + timedelta(seconds=expires_in)
logger.info("Taobao token refreshed for %s", self._account.account_id)
except asyncio.CancelledError:
break
except Exception as e:
logger.error("Taobao token refresh failed for %s: %s", self._account.account_id, e)
async def enqueue_message(self, unified_msg: dict) -> None:
if self._message_queue is not None:
self._last_message_at = time.monotonic()
await self._message_queue.put(unified_msg)
def get_message_queue(self) -> asyncio.Queue | None:
return self._message_queue
def get_last_message_at(self) -> float | None:
return self._last_message_at
def _resolve_account(ctx) -> TaobaoAccount:
config = getattr(ctx, "config", {}) if ctx else {}
accounts = config.get("accounts", {})
account_id = getattr(ctx, "account_id", "default")
raw = accounts.get(account_id, {})
adapter = TaobaoConfigAdapter()
return adapter.build_account(account_id, raw)
def get_current_gateway() -> TaobaoGateway | None:
return _current_gateway