[Scheduling] Emailed iCalendar invites on session confirm / reschedule / cancel #189
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Replaces the dropped Google Calendar OAuth push (#107 Phase B — see that PR's investigation: sensitive-scope verification + a non-resettable 100-user cap make per-self-hoster Google OAuth poor value). The #107 iCal feed gives subscription sync, but Google refreshes external ICS feeds only every 8–24h.
Emailed iCalendar invites are the near-instant, provider-agnostic, no-OAuth alternative: an invite email auto-adds the session to Gmail / Outlook / Apple Calendar, updates in place on reschedule, and is removed on cancel — via the standard iTIP
METHOD:REQUEST/METHOD:CANCELmechanism.Current state to be aware of: today's
send_confirmation(app/notifications/email.py) sends a plain HTML email to a single configured SMTP address — there is no per-member email path and no calendar part. So this is a real feature, not just "attach an .ics".Spec
Per-member calendar invites (
app/notifications/email.py+ a new/extended service):multipart/mixedwith the HTML part and atext/calendar; method=REQUESTpart (plus a.icsattachment for clients that prefer it), built via the existingapp/services/calendar_service.pyfrom #107.UID={session.id}@questboard(stable — matches the feed, so a client that has both won't duplicate).SEQUENCE=int(session.updated_at.timestamp())(reuse #107'supdated_at).ORGANIZER= the configured from-address;ATTENDEE= the member's email;DTSTART/DTENDUTC.METHOD:REQUESTwith the (higher)SEQUENCE→ clients update in place.METHOD:CANCELwith a higherSEQUENCE→ clients remove the event.Opt-in:
users.calendar_invite_opt_in(boolean, default false), mirroringrecap_email_opt_in. Editable on Profile. Only opted-in members with a non-nullemailreceive invites. No-op entirely if SMTP is unconfigured.Wiring: hook into the existing
send_session_confirmed/ reschedule /send_session_cancelledtask paths. Calendar invites are an additional channel alongside Discord/webhook notifications, not a replacement.Out of scope
METHOD:REPLYhandling (attendee responses back into Quest Board).Acceptance criteria
METHOD:REQUEST, correctUID/ATTENDEE/DTSTART).SEQUENCEand updates the existing event (sameUID).METHOD:CANCELand removes it.calendar_invite_opt_inapplies and round-trips.References
app/notifications/email.py(send_confirmation/_send),app/tasks/reminder_tasks.py(send_session_confirmed,send_session_cancelled, reschedule path)app/services/calendar_service.py(#107 ICS builder — extend withMETHOD+ORGANIZER/ATTENDEE)app/models/user.py(email,recap_email_opt_inas the opt-in precedent)Origin: replacement for #107 Phase B, chosen 2026-07-17.