@@ -25,17 +25,23 @@ def create_host_request_ics(
2525 host_request : HostRequest , other_name : str , hosting : bool , loc_context : LocalizationContext
2626) -> str :
2727 event = create_host_request_event (host_request , other_name , hosting , loc_context )
28- return event_to_ics (event , loc_context )
28+
29+ # METHOD:PUBLISH means this is part of a stream of calendar event information.
30+ # It allows for later cancellation, and doesn't expose accept/decline functionality.
31+ return event_to_ics (event , "PUBLISH" , loc_context )
2932
3033
3134def create_host_request_event (
32- host_request : HostRequest , other_name : str , hosting : bool , loc_context : LocalizationContext
35+ host_request : HostRequest , other_name : str , hosting : bool , loc_context : LocalizationContext , sequence : int = 0
3336) -> Event :
3437 """Creates an ics event for a host request."""
3538
3639 event = Event ()
3740 event .uid = get_host_request_event_uid (host_request .host_request_id )
3841
42+ # Explicitly allow later sequencing of a cancellation with SEQUENCE:1
43+ event .extra .append (ContentLine (name = "SEQUENCE" , value = str (sequence )))
44+
3945 if hosting :
4046 event .name = get_emails_i18next ().localize (
4147 "calendar_events.host_requests.title_host" , loc_context .locale , {"name" : other_name }
@@ -70,23 +76,23 @@ def create_host_request_cancellation_attachment(
7076def create_host_request_cancellation_ics (
7177 host_request : HostRequest , other_name : str , hosting : bool , loc_context : LocalizationContext
7278) -> str :
73- event = create_host_request_event (host_request , other_name , hosting , loc_context )
79+ event = create_host_request_event (host_request , other_name , hosting , loc_context , sequence = 1 )
7480 event .name = get_emails_i18next ().localize (
7581 "calendar_events.title_cancelled" , loc_context .locale , {"title" : event .name }
7682 )
7783 event .status = "CANCELLED"
7884
79- # Cancellation is sequenced after creation (which defaults to sequence number 0)
80- event . extra . append ( ContentLine ( name = "SEQUENCE" , value = "1" ))
81-
82- return event_to_ics (event , loc_context )
85+ # METHOD:PUBLISH means this is part of a stream of calendar event information.
86+ # Gmail™ will immediately remove the event from the user's calendar.
87+ # METHOD:CANCEL might leave the event in cancelled state or not work.
88+ return event_to_ics (event , "PUBLISH" , loc_context )
8389
8490
85- def event_to_ics (event : Event , loc_context : LocalizationContext ) -> str :
91+ def event_to_ics (event : Event , method : str | None , loc_context : LocalizationContext ) -> str :
8692 # PRODID is mandatory and generally follows "-//[Organization]//[Product Name]//[Language]"
8793 calendar = Calendar (creator = f"-//Couchers.org//Couchers//{ loc_context .locale .upper ()} " )
88- if event . status == "CANCELLED" :
89- calendar .method = "CANCEL"
94+ if method :
95+ calendar .method = method
9096 calendar .events .add (event )
9197 return cast (str , calendar .serialize ())
9298
0 commit comments