新增京东(JD)渠道扩展,支持在 Yuxi 平台中集成京东客服渠道。 包含以下功能模块: - client: 京东 API 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - webhook: Webhook 事件处理 - outbound: 外发消息管理 - pairing: 用户配对与绑定 - security: 安全校验 - signature: 请求签名验证 - crypto: 加解密处理 - dedupe: 消息去重 - monitor: 渠道状态监控 - status: 会话状态管理 - session: 会话管理 - business: 业务逻辑处理 - types: 类型定义
84 lines
2.7 KiB
Python
84 lines
2.7 KiB
Python
import asyncio
|
|
import logging
|
|
|
|
import httpx
|
|
|
|
from yuxi.channel.extensions.jd.signature import build_signed_params
|
|
from yuxi.channel.extensions.jd.types import JOS_API_GATEWAY, JOS_MAX_RETRIES, JOS_RETRY_BASE_DELAY, OutboundResult
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class JOSClient:
|
|
def __init__(self, app_key: str, app_secret: str, shop_id: str):
|
|
self._app_key = app_key
|
|
self._app_secret = app_secret
|
|
self._shop_id = shop_id
|
|
self._http = httpx.AsyncClient(timeout=15.0)
|
|
|
|
async def close(self):
|
|
await self._http.aclose()
|
|
|
|
async def call(self, method: str, biz_params: dict, access_token: str) -> dict:
|
|
params = build_signed_params(
|
|
method=method,
|
|
biz_params=biz_params,
|
|
app_key=self._app_key,
|
|
app_secret=self._app_secret,
|
|
access_token=access_token,
|
|
)
|
|
|
|
for attempt in range(JOS_MAX_RETRIES):
|
|
try:
|
|
resp = await self._http.post(JOS_API_GATEWAY, data=params)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
error_response = data.get("error_response")
|
|
if error_response:
|
|
code = error_response.get("code")
|
|
msg = error_response.get("zh_desc", "Unknown error")
|
|
logger.error("JOS API error [%s]: %s", code, msg)
|
|
raise ValueError(f"JOS API error {code}: {msg}")
|
|
|
|
return data
|
|
|
|
except (httpx.TimeoutException, httpx.NetworkError) as e:
|
|
logger.warning("JOS API retry %d/%d: %s", attempt + 1, JOS_MAX_RETRIES, e)
|
|
if attempt == JOS_MAX_RETRIES - 1:
|
|
raise
|
|
await asyncio.sleep(JOS_RETRY_BASE_DELAY * (2**attempt))
|
|
|
|
raise RuntimeError("JOS API max retries exceeded")
|
|
|
|
async def send_msg(
|
|
self,
|
|
chat_id: str,
|
|
to_id: str,
|
|
msg_type: int,
|
|
content: str,
|
|
access_token: str,
|
|
) -> OutboundResult:
|
|
biz_params = {
|
|
"chat_id": chat_id,
|
|
"from_id": self._shop_id,
|
|
"to_id": to_id,
|
|
"msg_type": msg_type,
|
|
"content": content,
|
|
}
|
|
|
|
try:
|
|
data = await self.call("jingdong.im.sendMsg", biz_params, access_token)
|
|
result = data.get("jingdong_im_sendMsg_responce", {}).get("result", {})
|
|
return OutboundResult(
|
|
message_id=result.get("msg_id", ""),
|
|
success=True,
|
|
)
|
|
except Exception as e:
|
|
return OutboundResult(
|
|
message_id="",
|
|
success=False,
|
|
error_code="JOS_ERROR",
|
|
error_msg=str(e),
|
|
)
|