feat: 新增 agent/app/core 目录
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
379
agent/app/core/tools/impl/notify.py
Normal file
379
agent/app/core/tools/impl/notify.py
Normal file
@@ -0,0 +1,379 @@
|
||||
"""
|
||||
通知工具
|
||||
提供发送通知的功能(邮件、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"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user