from __future__ import annotations import json import logging import uuid from typing import Any import httpx from yuxi.channels.models import DeliveryResult from .token import DingDingTokenManager logger = logging.getLogger(__name__) async def send_text( token_manager: DingDingTokenManager, open_conversation_id: str, robot_code: str, content: str, *, chat_type: str = "direct", msg_uuid: str | None = None, thread_root_id: str | None = None, http_client: httpx.AsyncClient | None = None, ) -> DeliveryResult: try: return await _send_msg( token_manager, open_conversation_id, robot_code, "sampleText", {"content": content}, chat_type=chat_type, msg_uuid=msg_uuid, thread_root_id=thread_root_id, http_client=http_client, ) except Exception as e: logger.error(f"send_text failed: {e}") return DeliveryResult(success=False, error=str(e)) async def send_markdown( token_manager: DingDingTokenManager, open_conversation_id: str, robot_code: str, title: str, text: str, *, chat_type: str = "direct", msg_uuid: str | None = None, thread_root_id: str | None = None, http_client: httpx.AsyncClient | None = None, ) -> DeliveryResult: try: return await _send_msg( token_manager, open_conversation_id, robot_code, "sampleMarkdown", {"title": title, "text": text}, chat_type=chat_type, msg_uuid=msg_uuid, thread_root_id=thread_root_id, http_client=http_client, ) except Exception as e: logger.error(f"send_markdown failed: {e}") return DeliveryResult(success=False, error=str(e)) async def send_card( token_manager: DingDingTokenManager, open_conversation_id: str, robot_code: str, msg_key: str, msg_param: dict[str, Any], *, chat_type: str = "direct", msg_uuid: str | None = None, thread_root_id: str | None = None, http_client: httpx.AsyncClient | None = None, ) -> DeliveryResult: try: return await _send_msg( token_manager, open_conversation_id, robot_code, msg_key, msg_param, chat_type=chat_type, msg_uuid=msg_uuid, thread_root_id=thread_root_id, http_client=http_client, ) except Exception as e: logger.error(f"send_card failed: {e}") return DeliveryResult(success=False, error=str(e)) async def _send_msg( token_manager: DingDingTokenManager, open_conversation_id: str, robot_code: str, msg_key: str, msg_param: dict[str, Any], *, chat_type: str = "direct", msg_uuid: str | None = None, thread_root_id: str | None = None, http_client: httpx.AsyncClient | None = None, ) -> DeliveryResult: token = await token_manager.get_token() if chat_type == "group": url = "https://api.dingtalk.com/v1.0/robot/groupMessages/send" else: url = "https://api.dingtalk.com/v1.0/robot/privateChatMessages/send" payload: dict[str, Any] = { "msgParam": json.dumps(msg_param, ensure_ascii=False), "msgKey": msg_key, "openConversationId": open_conversation_id, "robotCode": robot_code, "msgUuid": msg_uuid or str(uuid.uuid4()), } if thread_root_id: payload["rootId"] = thread_root_id headers = { "x-acs-dingtalk-access-token": token, "Content-Type": "application/json", } if http_client is not None: resp = await http_client.post(url, json=payload, headers=headers) else: async with httpx.AsyncClient(timeout=httpx.Timeout(15)) as client: resp = await client.post(url, json=payload, headers=headers) if resp.status_code != 200: logger.error(f"_send_msg HTTP {resp.status_code}: {resp.text[:300]} url={url}") return DeliveryResult( success=False, error=f"DingDing API HTTP {resp.status_code}: {resp.text[:200]}", ) data = resp.json() return DeliveryResult( success=True, message_id=data.get("processQueryKey") or data.get("messageId", ""), ) async def _send_via_webhook( http_client: httpx.AsyncClient, webhook_url: str, robot_code: str, content: str, open_conversation_id: str, ) -> DeliveryResult: payload = { "msgKey": "sampleMarkdown", "msgParam": json.dumps({"content": content}, ensure_ascii=False), "robotCode": robot_code, "openConversationId": open_conversation_id, } resp = await http_client.post( webhook_url, json=payload, headers={"Content-Type": "application/json"}, timeout=15.0, ) if resp.status_code != 200: return DeliveryResult( success=False, error=f"Webhook HTTP {resp.status_code}: {resp.text[:200]}", ) data = resp.json() return DeliveryResult( success=True, message_id=data.get("processQueryKey") or data.get("messageId", ""), )