from __future__ import annotations import logging from typing import TYPE_CHECKING, Any import httpx from ._validate import validate_params if TYPE_CHECKING: from yuxi.channels.adapters.dingding.token import DingDingTokenManager logger = logging.getLogger(__name__) DINGTALK_API_BASE = "https://api.dingtalk.com" @validate_params async def create_doc( token_manager: DingDingTokenManager, title: str, *, content: str = "", folder_id: str = "", http_client: httpx.AsyncClient | None = None, ) -> dict[str, Any]: token = await token_manager.get_token() url = f"{DINGTALK_API_BASE}/v1.0/doc/word/create" headers = { "x-acs-dingtalk-access-token": token, "Content-Type": "application/json", } payload: dict[str, Any] = {"title": title} if content: payload["content"] = content if folder_id: payload["folderId"] = folder_id try: 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: raise RuntimeError(f"创建文档失败: HTTP {resp.status_code}: {resp.text[:200]}") data = resp.json() return { "document_id": data.get("docId", data.get("documentId", "")), "title": data.get("title", title), "url": data.get("url", data.get("docUrl", "")), } except Exception as e: logger.error(f"[DingDing Doc] create_doc failed: {e}") raise RuntimeError(f"创建文档失败: {e}") from e @validate_params async def get_doc_content( token_manager: DingDingTokenManager, document_id: str, *, http_client: httpx.AsyncClient | None = None, ) -> dict[str, Any]: token = await token_manager.get_token() url = f"{DINGTALK_API_BASE}/v1.0/doc/word/{document_id}" headers = {"x-acs-dingtalk-access-token": token} try: if http_client is not None: resp = await http_client.get(url, headers=headers) else: async with httpx.AsyncClient(timeout=httpx.Timeout(15)) as client: resp = await client.get(url, headers=headers) if resp.status_code != 200: raise RuntimeError(f"获取文档内容失败: HTTP {resp.status_code}: {resp.text[:200]}") data = resp.json() return { "document_id": data.get("docId", data.get("documentId", document_id)), "title": data.get("title", ""), "content": data.get("content", data.get("body", "")), "blocks": data.get("blocks", []), "create_time": data.get("createTime", ""), "update_time": data.get("updateTime", ""), } except Exception as e: logger.error(f"[DingDing Doc] get_doc_content failed: {e}") raise RuntimeError(f"获取文档内容失败: {e}") from e @validate_params async def append_doc_block( token_manager: DingDingTokenManager, document_id: str, content: str, *, block_type: str = "text", http_client: httpx.AsyncClient | None = None, ) -> dict[str, Any]: token = await token_manager.get_token() url = f"{DINGTALK_API_BASE}/v1.0/doc/word/{document_id}/blocks" headers = { "x-acs-dingtalk-access-token": token, "Content-Type": "application/json", } payload: dict[str, Any] = { "blockType": block_type, "content": content, } try: 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: raise RuntimeError(f"追加文档块失败: HTTP {resp.status_code}: {resp.text[:200]}") data = resp.json() return { "document_id": document_id, "block_id": data.get("blockId", ""), "success": True, } except Exception as e: logger.error(f"[DingDing Doc] append_doc_block failed: {e}") raise RuntimeError(f"追加文档块失败: {e}") from e @validate_params async def delete_doc( token_manager: DingDingTokenManager, document_id: str, *, http_client: httpx.AsyncClient | None = None, ) -> dict[str, Any]: token = await token_manager.get_token() url = f"{DINGTALK_API_BASE}/v1.0/doc/word/{document_id}" headers = {"x-acs-dingtalk-access-token": token} try: if http_client is not None: resp = await http_client.delete(url, headers=headers) else: async with httpx.AsyncClient(timeout=httpx.Timeout(15)) as client: resp = await client.delete(url, headers=headers) if resp.status_code != 200: raise RuntimeError(f"删除文档失败: HTTP {resp.status_code}: {resp.text[:200]}") return {"document_id": document_id, "deleted": True} except Exception as e: logger.error(f"[DingDing Doc] delete_doc failed: {e}") raise RuntimeError(f"删除文档失败: {e}") from e