@@ -40,13 +40,14 @@ class Event(TypedDict):
4040 description : str
4141 organizer : str
4242 hangout_link : str
43+ reminder : int
4344
4445
4546# Our cached view of the calendar, updated periodically.
4647events : List [Event ] = []
4748
48- # Unique keys for events we've already sent, so we don't remind twice.
49- sent : Set [Tuple [int , datetime .datetime ]] = set ()
49+ # Unique keys for reminders we've already sent, so we don't remind twice.
50+ sent : Set [Tuple [int , datetime .datetime , int ]] = set ()
5051
5152sys .path .append (os .path .dirname (__file__ ))
5253
@@ -73,12 +74,11 @@ google-calendar --calendar calendarID@example.calendar.google.com
7374
7475
7576parser .add_argument (
76- "--interval" ,
77- dest = "interval" ,
78- default = 30 ,
77+ "--override" ,
78+ dest = "override" ,
7979 type = int ,
8080 action = "store" ,
81- help = "Minutes before event for reminder [default: 30] " ,
81+ help = "Override the reminder time for all events. " ,
8282 metavar = "MINUTES" ,
8383)
8484
@@ -125,13 +125,12 @@ def get_credentials() -> Credentials:
125125def populate_events () -> Optional [None ]:
126126 creds = get_credentials ()
127127 service = build ("calendar" , "v3" , credentials = creds )
128- now = datetime .datetime .now (pytz .utc ).isoformat ()
129128 feed = (
130129 service .events ()
131130 .list (
132131 calendarId = options .calendarID ,
133- timeMin = now ,
134- maxResults = 5 ,
132+ timeMin = datetime . datetime . now ( pytz . utc ). isoformat () ,
133+ timeMax = datetime . datetime . now ( pytz . utc ). isoformat (). split ( "T" )[ 0 ] + "T23:59:59Z" ,
135134 singleEvents = True ,
136135 orderBy = "startTime" ,
137136 )
@@ -162,6 +161,9 @@ def populate_events() -> Optional[None]:
162161 # of the tzinfo base class.
163162 start = calendar_timezone .localize (start_naive )
164163 end = calendar_timezone .localize (end_naive )
164+ now = datetime .datetime .now (tz = start .tzinfo )
165+ if start < now :
166+ continue
165167 id = event ["id" ]
166168 summary = event .get ("summary" , "(No Title)" )
167169 html_link = event ["htmlLink" ]
@@ -176,7 +178,25 @@ def populate_events() -> Optional[None]:
176178 else event ["organizer" ].get ("displayName" , event ["organizer" ]["email" ])
177179 )
178180 hangout_link = event .get ("hangoutLink" , "" )
179- events .append (
181+ reminders = event ["reminders" ]
182+ # If the user has specified an override, we use that for all events.
183+ # If the event uses the calendar's default reminders, we use that.
184+ # If the event has overrides on Google Calendar, we use that.
185+ # If none of the above, we don't set a reminder.
186+ if options .override :
187+ reminder_minutes = [options .override ]
188+ elif reminders .get ("useDefault" ):
189+ calendar_list = service .calendarList ().get (calendarId = options .calendarID ).execute ()
190+ reminder_minutes = (
191+ [reminder ["minutes" ] for reminder in calendar_list ["defaultReminders" ]]
192+ if calendar_list .get ("defaultReminders" )
193+ else []
194+ )
195+ elif reminders .get ("overrides" ):
196+ reminder_minutes = [reminder ["minutes" ] for reminder in reminders ["overrides" ]]
197+ else :
198+ reminder_minutes = []
199+ events .extend (
180200 {
181201 "id" : id ,
182202 "start" : start ,
@@ -188,7 +208,9 @@ def populate_events() -> Optional[None]:
188208 "description" : description ,
189209 "organizer" : organizer ,
190210 "hangout_link" : hangout_link ,
211+ "reminder" : reminder ,
191212 }
213+ for reminder in reminder_minutes
192214 )
193215
194216
@@ -218,21 +240,30 @@ def event_to_message(event: Event) -> str:
218240
219241
220242def send_reminders () -> Optional [None ]:
221- messages = []
243+ messages : List [ str ] = []
222244 keys = set ()
223- now = datetime .datetime .now (tz = pytz .utc )
224-
225- for event in events :
226- dt = event ["start" ] - now
227- if dt .days == 0 and dt .seconds < 60 * options .interval :
228- # The unique key includes the start time, because of
229- # repeating events.
230- key = (event ["id" ], event ["start" ])
245+ # Sort events by the time of the reminder.
246+ events .sort (
247+ key = lambda event : (event ["start" ] - datetime .timedelta (minutes = event ["reminder" ])),
248+ reverse = True ,
249+ )
250+ # Iterate through the events and send reminders for those whose reminder time has come or passed and remove them from the list.
251+ # The instant a reminder's time is greater than the current time, we stop sending reminders and break out of the loop.
252+ while len (events ):
253+ event = events [- 1 ]
254+ now = datetime .datetime .now (tz = event ["start" ].tzinfo )
255+ dt = event ["start" ] - datetime .timedelta (minutes = event ["reminder" ])
256+ if dt <= now :
257+ key = (event ["id" ], event ["start" ], event ["reminder" ])
231258 if key not in sent :
232259 line = event_to_message (event )
233260 print ("Sending reminder:" , line )
234- messages . append ( line )
261+ messages = [ line , * messages ]
235262 keys .add (key )
263+ events .pop ()
264+ else :
265+ break
266+
236267 if not messages :
237268 return
238269
0 commit comments