190 lines
5.1 KiB
Python
190 lines
5.1 KiB
Python
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", ""),
|
|
)
|