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),
|
||
|
|
)
|