import logging import uuid from datetime import datetime, timezone import aiosmtplib from email.message import EmailMessage from icalendar import Calendar, Event, vCalAddress, vText from app.config import settings logger = logging.getLogger(__name__) async def send_meeting_invite( participants: list[dict], title: str, description: str, start_time: datetime, end_time: datetime, ) -> bool: if not settings.smtp_host or not settings.smtp_user or not settings.smtp_password: logger.warning("SMTP credentials not configured. Skipping email invite.") return False # Create the ICS content cal = Calendar() cal.add('prodid', '-//Common Availability//m.com//') cal.add('version', '2.0') cal.add('method', 'REQUEST') event = Event() event.add('summary', title) event.add('dtstart', start_time) event.add('dtend', end_time) event.add('dtstamp', datetime.now(timezone.utc)) event.add('description', description) event.add('uid', str(uuid.uuid4())) event.add('organizer', vCalAddress(f'MAILTO:{settings.smtp_user}')) attendee_emails = [] for p in participants: attendee = vCalAddress(f'MAILTO:{p["email"]}') attendee.params['cn'] = vText(p["name"]) attendee.params['ROLE'] = vText('REQ-PARTICIPANT') event.add('attendee', attendee, encode=0) attendee_emails.append(p["email"]) cal.add_component(event) ics_data = cal.to_ical() # Create Email msg = EmailMessage() msg["Subject"] = f"Invitation: {title}" msg["From"] = settings.smtp_user msg["To"] = ", ".join(attendee_emails) msg.set_content( f"You have been invited to: {title}\n" f"When: {start_time.strftime('%Y-%m-%d %H:%M %Z')}\n\n" f"{description}" ) # Attach ICS msg.add_attachment( ics_data, maintype="text", subtype="calendar", filename="invite.ics", params={"method": "REQUEST"} ) # Send try: await aiosmtplib.send( msg, hostname=settings.smtp_host, port=settings.smtp_port, username=settings.smtp_user, password=settings.smtp_password, start_tls=True ) logger.info(f"Sent meeting invites to {len(attendee_emails)} participants") return True except Exception as e: logger.error(f"Failed to send email invite: {e}") return False