019fddc079
Deploy Dashboard (Dev) / Backend Tests (push) Failing after 2m17s
Deploy Dashboard (Dev) / Frontend Tests (push) Failing after 3m34s
Deploy Dashboard (Dev) / Deploy to Dev (push) Has been skipped
Run Tests / Secret Detection (pull_request) Failing after 30s
Run Tests / Backend Tests (pull_request) Failing after 2m18s
Run Tests / Frontend Tests (pull_request) Failing after 18s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
161 lines
4.7 KiB
Python
161 lines
4.7 KiB
Python
"""
|
|
Email notification service for OPC
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import smtplib
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
|
|
import config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Email configuration from environment
|
|
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com")
|
|
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
|
|
SMTP_USER = os.getenv("SMTP_USER", "")
|
|
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
|
|
SMTP_FROM = os.getenv("SMTP_FROM", SMTP_USER)
|
|
SMTP_TO = os.getenv("SMTP_TO", "")
|
|
|
|
|
|
async def send_email(subject: str, body: str, html_body: str = None):
|
|
"""Send email notification"""
|
|
if not SMTP_USER or not SMTP_PASSWORD or not SMTP_TO:
|
|
logger.warning("Email not configured, skipping notification")
|
|
return
|
|
|
|
try:
|
|
# Create message
|
|
msg = MIMEMultipart("alternative")
|
|
msg["Subject"] = subject
|
|
msg["From"] = SMTP_FROM
|
|
msg["To"] = SMTP_TO
|
|
|
|
# Add plain text part
|
|
msg.attach(MIMEText(body, "plain"))
|
|
|
|
# Add HTML part if provided
|
|
if html_body:
|
|
msg.attach(MIMEText(html_body, "html"))
|
|
|
|
# Send email
|
|
with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server:
|
|
server.starttls()
|
|
server.login(SMTP_USER, SMTP_PASSWORD)
|
|
server.send_message(msg)
|
|
|
|
logger.info(f"Email sent: {subject}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to send email: {e}")
|
|
|
|
|
|
async def send_task_assigned_email(task: dict, assigned_to: str, assigned_type: str):
|
|
"""Send email when task is assigned"""
|
|
subject = f"Task Assigned: {task['title']}"
|
|
|
|
body = f"""You have been assigned a new task:
|
|
|
|
Title: {task['title']}
|
|
Priority: {task['priority']}
|
|
Status: {task['status']}
|
|
|
|
Description:
|
|
{task.get('description', 'No description')}
|
|
|
|
View task: {config.DASHBOARD_URL}/?page=opc
|
|
"""
|
|
|
|
html_body = f"""
|
|
<html>
|
|
<body>
|
|
<h2>Task Assigned</h2>
|
|
<p>You have been assigned a new task:</p>
|
|
<table style="border-collapse: collapse; margin: 20px 0;">
|
|
<tr><td style="padding: 8px; font-weight: bold;">Title:</td><td style="padding: 8px;">{task['title']}</td></tr>
|
|
<tr><td style="padding: 8px; font-weight: bold;">Priority:</td><td style="padding: 8px;">{task['priority']}</td></tr>
|
|
<tr><td style="padding: 8px; font-weight: bold;">Status:</td><td style="padding: 8px;">{task['status']}</td></tr>
|
|
</table>
|
|
<p><strong>Description:</strong></p>
|
|
<p>{task.get('description', 'No description')}</p>
|
|
<p><a href="{config.DASHBOARD_URL}/?page=opc" style="background-color: #4F46E5; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">View Task</a></p>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
await send_email(subject, body, html_body)
|
|
|
|
|
|
async def send_agent_approval_email(agent_name: str, task: dict, reasoning: str, actions: list):
|
|
"""Send email for agent approval request"""
|
|
subject = f"Agent Approval Required: {agent_name}"
|
|
|
|
actions_text = "\n".join(
|
|
[f"- {action.get('type')}: {action.get('title', action.get('message', 'N/A'))}" for action in actions]
|
|
)
|
|
|
|
body = f"""Agent approval required:
|
|
|
|
Agent: {agent_name}
|
|
Task: {task['title']}
|
|
|
|
Reasoning:
|
|
{reasoning}
|
|
|
|
Proposed Actions:
|
|
{actions_text}
|
|
|
|
Please review and approve in the OPC dashboard:
|
|
{config.DASHBOARD_URL}/?page=opc
|
|
"""
|
|
|
|
html_body = f"""
|
|
<html>
|
|
<body>
|
|
<h2>🤖 Agent Approval Required</h2>
|
|
<p><strong>Agent:</strong> {agent_name}</p>
|
|
<p><strong>Task:</strong> {task['title']}</p>
|
|
<h3>Reasoning:</h3>
|
|
<p>{reasoning}</p>
|
|
<h3>Proposed Actions:</h3>
|
|
<ul>
|
|
{"".join([f"<li><strong>{action.get('type')}</strong>: {action.get('title', action.get('message', 'N/A'))}</li>" for action in actions])}
|
|
</ul>
|
|
<p><a href="{config.DASHBOARD_URL}/?page=opc" style="background-color: #4F46E5; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">Review & Approve</a></p>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
await send_email(subject, body, html_body)
|
|
|
|
|
|
async def send_agent_completion_email(agent_name: str, task: dict, actions_count: int):
|
|
"""Send email when agent completes task"""
|
|
subject = f"Agent Task Completed: {agent_name}"
|
|
|
|
body = f"""Agent has completed a task:
|
|
|
|
Agent: {agent_name}
|
|
Task: {task['title']}
|
|
Actions Executed: {actions_count}
|
|
|
|
View details: {config.DASHBOARD_URL}/?page=opc
|
|
"""
|
|
|
|
html_body = f"""
|
|
<html>
|
|
<body>
|
|
<h2>✅ Agent Task Completed</h2>
|
|
<p><strong>Agent:</strong> {agent_name}</p>
|
|
<p><strong>Task:</strong> {task['title']}</p>
|
|
<p><strong>Actions Executed:</strong> {actions_count}</p>
|
|
<p><a href="{config.DASHBOARD_URL}/?page=opc" style="background-color: #10B981; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;">View Details</a></p>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
await send_email(subject, body, html_body)
|