import os from typing import Type from dotenv import load_dotenv from pydantic import BaseModel, Field from crewai.tools import BaseTool # Try importing sendgrid with fallback try: import sendgrid from sendgrid.helpers.mail import Mail SENDGRID_AVAILABLE = True print("✅ SendGrid package imported successfully") except ImportError: SENDGRID_AVAILABLE = False print("❌ SendGrid package not available - add 'sendgrid' to requirements.txt") # Load environment variables load_dotenv() class SendEmailInput(BaseModel): sender_email: str = Field(..., description="The email address of the visitor sending the message.") subject: str = Field(..., description="A concise subject line for the email.") body: str = Field(..., description="The main content of the email, formatted professionally.") class SendEmailTool(BaseTool): name: str = "SendEmailTool" description: str = "Sends an email using the SendGrid API." args_schema: Type[BaseModel] = SendEmailInput def _run(self, sender_email: str, subject: str, body: str) -> str: print("🚀 Starting SendGrid email process...") # Check if sendgrid is available if not SENDGRID_AVAILABLE: return "❌ Error: SendGrid package not installed. Add 'sendgrid' to requirements.txt and redeploy." # Get environment variables api_key = os.getenv("SENDGRID_API_KEY") or os.environ.get("SENDGRID_API_KEY") verified_sender = os.getenv("VERIFIED_SENDER_EMAIL") or os.environ.get("VERIFIED_SENDER_EMAIL") receiver_email = os.getenv("RECEIVER_EMAIL") or os.environ.get("RECEIVER_EMAIL") # Debug info print(f"🔍 Debug Info:") print(f" - SendGrid API Key present: {bool(api_key)}") print(f" - Verified sender present: {bool(verified_sender)}") print(f" - Receiver email present: {bool(receiver_email)}") print(f" - Contact sender: {sender_email}") # Check required variables if not api_key: return "❌ Error: SENDGRID_API_KEY not found in environment variables." if not verified_sender: return "❌ Error: VERIFIED_SENDER_EMAIL not found in environment variables." if not receiver_email: return "❌ Error: RECEIVER_EMAIL not found in environment variables." # Validate API key format if not api_key.startswith('SG.'): return f"❌ Error: Invalid SendGrid API key format. Should start with 'SG.' but starts with '{api_key[:5]}'." try: # Initialize SendGrid sg = sendgrid.SendGridAPIClient(api_key=api_key) print("✅ SendGrid client initialized") # Create HTML content (fix f-string backslash issue) formatted_body = body.replace('\n', '
').replace(chr(10), '
') html_content = f"""

📧 New Portfolio Contact

From: {sender_email}

Subject: {subject}

Message:

{formatted_body}

This message was sent through your portfolio contact form.
Reply to this email to respond directly to {sender_email}

""" # Create message message = Mail( from_email=verified_sender, to_emails=receiver_email, subject=f"Portfolio Contact: {subject}", html_content=html_content ) message.reply_to = sender_email print(f"📧 Prepared email:") print(f" - From: {verified_sender}") print(f" - To: {receiver_email}") print(f" - Reply-to: {sender_email}") # Send email print("📤 Sending via SendGrid...") response = sg.send(message) print(f"📨 Response: Status {response.status_code}") if response.status_code == 202: return f"✅ Email sent successfully via SendGrid! (Status: {response.status_code})" elif response.status_code == 401: return "❌ SendGrid Error: Invalid API key" elif response.status_code == 403: return "❌ SendGrid Error: Permission denied - check sender email verification" else: return f"⚠️ SendGrid Response: Status {response.status_code}" except Exception as e: error_msg = str(e) print(f"❌ SendGrid Error: {type(e).__name__}: {error_msg}") if "Unauthorized" in error_msg or "401" in error_msg: return "❌ SendGrid: Invalid API key" elif "Forbidden" in error_msg or "403" in error_msg: return "❌ SendGrid: Sender email not verified or insufficient permissions" else: return f"❌ SendGrid Error: {error_msg}"