from sendgrid import SendGridAPIClient from sendgrid.helpers.mail import Mail from strands import tool from config.settings import settings from typing import Dict, Union import logging logger = logging.getLogger(__name__) @tool( name="send_notification_email", description="Sends HTML email notifications about Topcoder challenges to registered users" ) def send_email(to: str, html_content: str, subject: str) -> Dict[str, Union[str, list]]: """Send an email notification about relevant Topcoder challenges using SendGrid. This tool handles the complete email delivery process including validation, formatting, and error handling. It uses the configured SendGrid API to send HTML-formatted challenge notifications to registered users. Args: to: The recipient's email address (must be a valid email format) html_content: The HTML-formatted content of the email notification subject: The subject line for the email notification Returns: Dictionary with status and content indicating success or failure details """ logger.info(f"Attempting to send email to {to} with subject: '{subject}'") # Validate required configuration if not settings.SENDGRID_API_KEY: error_msg = "SENDGRID_API_KEY environment variable is not set. Please configure SendGrid API key." logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] } if not settings.SENDGRID_FROM_EMAIL: error_msg = "SENDGRID_FROM_EMAIL is not configured. Please set the sender email address." logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] } # Validate input parameters if not to or "@" not in to: error_msg = f"Invalid recipient email address: {to}" logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] } if not html_content.strip(): error_msg = "Email content cannot be empty" logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] } if not subject.strip(): error_msg = "Email subject cannot be empty" logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] } # Construct the email message try: message = Mail( from_email=settings.SENDGRID_FROM_EMAIL, to_emails=to, subject=subject, html_content=html_content ) except Exception as e: error_msg = f"Failed to construct email message: {str(e)}" logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] } # Send the email via SendGrid API try: sg = SendGridAPIClient(settings.SENDGRID_API_KEY) response = sg.send(message) # Check response status if response.status_code in [200, 201, 202]: success_msg = f"Email sent successfully to {to}" logger.info(f"{success_msg}. SendGrid status: {response.status_code}") return { "status": "success", "content": [ {"text": success_msg}, {"json": { "recipient": to, "subject": subject, "status_code": response.status_code, "message_id": response.headers.get("X-Message-Id", "N/A") }} ] } else: error_msg = f"SendGrid returned unexpected status code: {response.status_code}" logger.warning(error_msg) return { "status": "error", "content": [ {"text": error_msg}, {"json": {"status_code": response.status_code, "response_body": str(response.body)}} ] } except Exception as e: error_msg = f"Error sending email to {to}: {str(e)}" logger.error(error_msg) return { "status": "error", "content": [ {"text": error_msg} ] }