"""Email notification service for domain and price alerts.""" import logging import asyncio from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from typing import Optional from dataclasses import dataclass import aiosmtplib from app.config import get_settings logger = logging.getLogger(__name__) settings = get_settings() @dataclass class EmailConfig: """Email configuration.""" smtp_host: str = "" smtp_port: int = 587 smtp_user: str = "" smtp_password: str = "" from_email: str = "noreply@pounce.dev" from_name: str = "Pounce Alerts" class EmailService: """ Async email service for sending notifications. Supports: - Domain availability alerts - Price change notifications - Weekly digest emails """ def __init__(self, config: EmailConfig = None): """Initialize email service.""" self.config = config or EmailConfig( smtp_host=getattr(settings, 'smtp_host', ''), smtp_port=getattr(settings, 'smtp_port', 587), smtp_user=getattr(settings, 'smtp_user', ''), smtp_password=getattr(settings, 'smtp_password', ''), ) self._enabled = bool(self.config.smtp_host and self.config.smtp_user) @property def is_enabled(self) -> bool: """Check if email service is configured.""" return self._enabled async def send_email( self, to_email: str, subject: str, html_body: str, text_body: str = None, ) -> bool: """ Send an email. Args: to_email: Recipient email address subject: Email subject html_body: HTML content text_body: Plain text content (optional) Returns: True if sent successfully, False otherwise """ if not self.is_enabled: logger.warning("Email service not configured, skipping send") return False try: message = MIMEMultipart("alternative") message["From"] = f"{self.config.from_name} <{self.config.from_email}>" message["To"] = to_email message["Subject"] = subject # Add text and HTML parts if text_body: message.attach(MIMEText(text_body, "plain")) message.attach(MIMEText(html_body, "html")) # Send via SMTP await aiosmtplib.send( message, hostname=self.config.smtp_host, port=self.config.smtp_port, username=self.config.smtp_user, password=self.config.smtp_password, use_tls=True, ) logger.info(f"Email sent to {to_email}: {subject}") return True except Exception as e: logger.error(f"Failed to send email to {to_email}: {e}") return False async def send_domain_available_alert( self, to_email: str, domain: str, user_name: str = None, ) -> bool: """Send alert when a watched domain becomes available.""" subject = f"🎉 Domain Available: {domain}" html_body = f"""
A domain you're watching just became available:
This is your chance to register it before someone else does!
Register Now →{"Now might be a good time to register!" if new_price < old_price else "Prices have gone up - act fast if you need this TLD."}
Hi {user_name}, here's your weekly summary:
| Domain | Status |
|---|---|
| No domains being watched | |
| TLD | Change | Price |
|---|---|---|
| No significant changes this week | ||