- 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>
83 lines
2.4 KiB
Python
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
|