""" 通知工具 提供发送通知的功能(邮件、Webhook等) """ import httpx from typing import Dict, Any, Optional, List from dataclasses import dataclass from enum import Enum class NotificationType(Enum): """通知类型""" EMAIL = "email" WEBHOOK = "webhook" SMS = "sms" DINGTALK = "dingtalk" WECHAT = "wechat" SLACK = "slack" @dataclass class NotificationConfig: """通知配置""" # Email配置 smtp_host: str = "" smtp_port: int = 587 smtp_user: str = "" smtp_password: str = "" from_email: str = "" # Webhook配置 webhook_url: str = "" webhook_secret: str = "" # 钉钉配置 dingtalk_webhook: str = "" # Slack配置 slack_webhook: str = "" class NotificationTool: """ 通知工具 支持多种通知渠道: - Email (SMTP) - Webhook - 钉钉 - Slack """ def __init__(self, config: Optional[NotificationConfig] = None): self.config = config or NotificationConfig() async def send_email( self, to: str, subject: str, body: str, cc: Optional[List[str]] = None, is_html: bool = False ) -> Dict[str, Any]: """ 发送邮件 Args: to: 收件人 subject: 主题 body: 内容 cc: 抄送列表 is_html: 是否HTML格式 Returns: 发送结果 """ if not self.config.smtp_host: return { "success": False, "error": "Email not configured" } try: import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart # 构建邮件 msg = MIMEMultipart('alternative') msg['Subject'] = subject msg['From'] = self.config.from_email or self.config.smtp_user msg['To'] = to if cc: msg['Cc'] = ",".join(cc) # 添加内容 content_type = "html" if is_html else "plain" msg.attach(MIMEText(body, content_type)) # 发送 with smtplib.SMTP(self.config.smtp_host, self.config.smtp_port) as server: server.starttls() server.login(self.config.smtp_user, self.config.smtp_password) server.send_message(msg) return { "success": True, "type": "email", "to": to, "subject": subject } except Exception as e: return { "success": False, "error": str(e), "type": "email" } async def send_webhook( self, url: str, data: Dict[str, Any], method: str = "POST", headers: Optional[Dict[str, str]] = None ) -> Dict[str, Any]: """ 发送Webhook Args: url: Webhook URL data: 请求数据 method: HTTP方法 headers: 请求头 Returns: 发送结果 """ try: async with httpx.AsyncClient(timeout=10) as client: response = await client.request( method=method, url=url, json=data, headers=headers ) return { "success": response.status_code < 400, "status_code": response.status_code, "type": "webhook", "url": url } except Exception as e: return { "success": False, "error": str(e), "type": "webhook" } async def send_dingtalk( self, message: str, webhook: Optional[str] = None ) -> Dict[str, Any]: """ 发送钉钉消息 Args: message: 消息内容 webhook: 自定义webhook URL Returns: 发送结果 """ url = webhook or self.config.dingtalk_webhook if not url: return { "success": False, "error": "Dingtalk webhook not configured" } try: async with httpx.AsyncClient(timeout=10) as client: response = await client.post( url, json={ "msgtype": "text", "text": { "content": message } } ) result = response.json() return { "success": result.get("errcode") == 0, "type": "dingtalk", "response": result } except Exception as e: return { "success": False, "error": str(e), "type": "dingtalk" } async def send_slack( self, message: str, channel: Optional[str] = None, webhook: Optional[str] = None ) -> Dict[str, Any]: """ 发送Slack消息 Args: message: 消息内容 channel: 频道 webhook: 自定义webhook URL Returns: 发送结果 """ url = webhook or self.config.slack_webhook if not url: return { "success": False, "error": "Slack webhook not configured" } try: payload = {"text": message} if channel: payload["channel"] = channel async with httpx.AsyncClient(timeout=10) as client: response = await client.post(url, json=payload) return { "success": response.status_code == 200, "type": "slack", "status_code": response.status_code } except Exception as e: return { "success": False, "error": str(e), "type": "slack" } async def send( self, type: str, message: str, **kwargs ) -> Dict[str, Any]: """ 统一发送接口 Args: type: 通知类型 (email, webhook, dingtalk, slack) message: 消息内容 **kwargs: 其他参数 Returns: 发送结果 """ type = type.lower() if type == "email": return await self.send_email( to=kwargs.get("to", ""), subject=kwargs.get("subject", "Notification"), body=message, cc=kwargs.get("cc") ) elif type == "webhook": return await self.send_webhook( url=kwargs.get("url", ""), data=kwargs.get("data", {"message": message}) ) elif type == "dingtalk": return await self.send_dingtalk( message=message, webhook=kwargs.get("webhook") ) elif type == "slack": return await self.send_slack( message=message, channel=kwargs.get("channel"), webhook=kwargs.get("webhook") ) else: return { "success": False, "error": f"Unknown notification type: {type}" } # 全局通知工具 notification_tool = NotificationTool() # 便捷函数 async def send_notification( type: str, message: str, **kwargs ) -> Dict[str, Any]: """发送通知""" return await notification_tool.send(type, message, **kwargs) async def send_email( to: str, subject: str, body: str ) -> Dict[str, Any]: """发送邮件""" return await notification_tool.send_email(to, subject, body) async def send_webhook( url: str, data: Dict[str, Any] ) -> Dict[str, Any]: """发送Webhook""" return await notification_tool.send_webhook(url, data) # 工具定义 SEND_NOTIFICATION_TOOL = { "name": "send_notification", "description": "Send notifications via email, webhook, dingtalk, or slack.", "parameters": { "type": "object", "properties": { "type": { "type": "string", "description": "Notification type: email, webhook, dingtalk, slack", "enum": ["email", "webhook", "dingtalk", "slack"] }, "message": { "type": "string", "description": "The notification message" }, "to": { "type": "string", "description": "For email: recipient email address" }, "subject": { "type": "string", "description": "For email: email subject" }, "url": { "type": "string", "description": "For webhook: webhook URL" }, "data": { "type": "object", "description": "For webhook: JSON data to send" }, "webhook": { "type": "string", "description": "Custom webhook URL for dingtalk/slack" }, "channel": { "type": "string", "description": "For slack: channel name" } }, "required": ["type", "message"] } }