ForcePilot/backend/package/yuxi/channels/adapters/dingding/media.py
Kris 86105d163d feat(dingding): add dingtalk channel adapter implementation
实现钉钉官方的频道适配器,包含完整的消息收发、事件处理、媒体上传下载、流式响应以及webhook支持,覆盖了认证、签名校验、速率限制、健康检查等完整功能流程。
2026-05-12 00:43:14 +08:00

101 lines
2.9 KiB
Python

from __future__ import annotations
import logging
from typing import Any
import httpx
from yuxi.channels.models import DeliveryResult
from .token import DingDingTokenManager
logger = logging.getLogger(__name__)
async def upload_media(
token_manager: DingDingTokenManager,
media_type: str,
media_data: bytes,
filename: str = "file",
*,
http_client: httpx.AsyncClient | None = None,
) -> dict[str, Any]:
token = await token_manager.get_token()
url = "https://api.dingtalk.com/v1.0/media/upload"
headers = {"x-acs-dingtalk-access-token": token}
files = {"media": (filename, media_data)}
params = {"type": media_type}
if http_client is not None:
resp = await http_client.post(url, headers=headers, files=files, params=params)
else:
async with httpx.AsyncClient(timeout=httpx.Timeout(30)) as client:
resp = await client.post(url, headers=headers, files=files, params=params)
if resp.status_code != 200:
logger.error(f"upload_media HTTP {resp.status_code}: {resp.text[:300]}")
raise RuntimeError(f"Media upload failed: HTTP {resp.status_code}")
return resp.json()
async def download_media(
token_manager: DingDingTokenManager,
download_code: str,
*,
http_client: httpx.AsyncClient | None = None,
) -> bytes:
token = await token_manager.get_token()
url = "https://api.dingtalk.com/v1.0/media/download"
headers = {"x-acs-dingtalk-access-token": token}
params = {"downloadCode": download_code}
if http_client is not None:
resp = await http_client.get(url, headers=headers, params=params)
else:
async with httpx.AsyncClient(timeout=httpx.Timeout(30)) as client:
resp = await client.get(url, headers=headers, params=params)
if resp.status_code != 200:
logger.error(f"download_media HTTP {resp.status_code}: {resp.text[:300]}")
raise RuntimeError(f"Media download failed: HTTP {resp.status_code}")
return resp.content
async def send_media(
token_manager: DingDingTokenManager,
open_conversation_id: str,
robot_code: str,
media_id: str,
media_type: str,
*,
chat_type: str = "direct",
http_client: httpx.AsyncClient | None = None,
) -> DeliveryResult:
try:
msg_key_map = {
"image": "sampleImageMsg",
"voice": "sampleAudio",
"file": "sampleFile",
"video": "sampleVideo",
}
msg_key = msg_key_map.get(media_type, "sampleFile")
msg_param = {"mediaId": media_id}
from .send import _send_msg
return await _send_msg(
token_manager,
open_conversation_id,
robot_code,
msg_key,
msg_param,
chat_type=chat_type,
http_client=http_client,
)
except Exception as e:
logger.error(f"send_media failed: {e}")
return DeliveryResult(success=False, error=str(e))