Files
common-availability/backend/src/app/email_service.py
Joyce 26311c867a feat: add email, scheduler, and Zulip integration services
- Add email service for sending meeting invites with ICS attachments
- Add scheduler for background calendar sync jobs
- Add Zulip service for meeting notifications
- Make ics_url optional for participants
- Add /api/schedule endpoint with 2-hour lead time validation
- Update frontend to support scheduling flow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 13:52:02 -05:00

83 lines
2.4 KiB
Python

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