Skip to content

Commit 3626384

Browse files
Feat: Adding more details to the reminder messages generated.
1 parent 5c0b394 commit 3626384

File tree

1 file changed

+78
-19
lines changed

1 file changed

+78
-19
lines changed

zulip/integrations/google/google-calendar

+78-19
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import logging
99
import os
1010
import sys
1111
import time
12-
from typing import List, Optional, Set, Tuple
12+
from typing import List, Optional, Set, Tuple, TypedDict
1313

1414
import dateutil.parser
1515
import pytz
@@ -28,8 +28,22 @@ CLIENT_SECRET_FILE = "client_secret.json" # noqa: S105
2828
APPLICATION_NAME = "Zulip"
2929
HOME_DIR = os.path.expanduser("~")
3030

31+
32+
class Event(TypedDict):
33+
id: int
34+
start: datetime.datetime
35+
end: datetime.datetime
36+
summary: str
37+
html_link: str
38+
status: str
39+
location: str
40+
description: str
41+
organizer: str
42+
hangout_link: str
43+
44+
3145
# Our cached view of the calendar, updated periodically.
32-
events: List[Tuple[int, datetime.datetime, str]] = []
46+
events: List[Event] = []
3347

3448
# Unique keys for events we've already sent, so we don't remind twice.
3549
sent: Set[Tuple[int, datetime.datetime]] = set()
@@ -123,63 +137,108 @@ def populate_events() -> Optional[None]:
123137
)
124138
.execute()
125139
)
126-
127140
events.clear()
128141
for event in feed["items"]:
129142
try:
130143
start = dateutil.parser.parse(event["start"]["dateTime"])
144+
end = dateutil.parser.parse(event["end"]["dateTime"])
131145
# According to the API documentation, a time zone offset is required
132146
# for start.dateTime unless a time zone is explicitly specified in
133147
# start.timeZone.
134-
if start.tzinfo is None:
148+
if start.tzinfo is None or end.tzinfo is None:
135149
event_timezone = pytz.timezone(event["start"]["timeZone"])
136150
# pytz timezones include an extra localize method that's not part
137151
# of the tzinfo base class.
138152
start = event_timezone.localize(start)
153+
end = event_timezone.localize(end)
139154
except KeyError:
140155
# All-day events can have only a date.
141156
start_naive = dateutil.parser.parse(event["start"]["date"])
142-
157+
end_naive = dateutil.parser.parse(event["end"]["date"])
143158
# All-day events don't have a time zone offset; instead, we use the
144159
# time zone of the calendar.
145160
calendar_timezone = pytz.timezone(feed["timeZone"])
146161
# pytz timezones include an extra localize method that's not part
147162
# of the tzinfo base class.
148163
start = calendar_timezone.localize(start_naive)
164+
end = calendar_timezone.localize(end_naive)
165+
id = event["id"]
166+
summary = event.get("summary", "(No Title)")
167+
html_link = event["htmlLink"]
168+
status = event.get("status", "confirmed")
169+
location = event.get("location", "")
170+
description = event.get("description", "")
171+
organizer = (
172+
""
173+
if (event["organizer"]["email"] == options.zulip_email or event["organizer"]["self"])
174+
else event["organizer"]["displayName"]
175+
)
176+
hangout_link = event.get("hangoutLink", "")
177+
events.append(
178+
{
179+
"id": id,
180+
"start": start,
181+
"end": end,
182+
"summary": summary,
183+
"html_link": html_link,
184+
"status": status,
185+
"location": location,
186+
"description": description,
187+
"organizer": organizer,
188+
"hangout_link": hangout_link,
189+
}
190+
)
149191

150-
try:
151-
events.append((event["id"], start, event["summary"]))
152-
except KeyError:
153-
events.append((event["id"], start, "(No Title)"))
192+
193+
def event_to_message(event: Event) -> str:
194+
"""Parse the event dictionary and return a string that can be sent as a message.
195+
196+
The message includes the event title, start and end times, location, organizer, hangout link, and description.
197+
198+
Returns:
199+
str: The message to be sent.
200+
"""
201+
line = f"**[{event['summary']}]({event['html_link']})**\n"
202+
if event["start"].hour == 0 and event["start"].minute == 0:
203+
line += "Scheduled for today.\n"
204+
else:
205+
line += f"Scheduled from **{event['start'].strftime('%H:%M')}** to **{event['end'].strftime('%H:%M')}**.\n"
206+
line += f"**Location:** {event['location']}\n" if event["location"] else ""
207+
line += f"**Organizer:** {event['organizer']}\n" if event["organizer"] else ""
208+
line += (
209+
f"**Hangout Link:** [{event['hangout_link'].split('/')[2]}]({event['hangout_link']})\n"
210+
if event["hangout_link"]
211+
else ""
212+
)
213+
line += f"**Description:** {event['description']}\n" if event["description"] else ""
214+
return line
154215

155216

156217
def send_reminders() -> Optional[None]:
157218
messages = []
158219
keys = set()
159220
now = datetime.datetime.now(tz=pytz.utc)
160221

161-
for id, start, summary in events:
162-
dt = start - now
222+
for event in events:
223+
dt = event["start"] - now
163224
if dt.days == 0 and dt.seconds < 60 * options.interval:
164225
# The unique key includes the start time, because of
165226
# repeating events.
166-
key = (id, start)
227+
key = (event["id"], event["start"])
167228
if key not in sent:
168-
if start.hour == 0 and start.minute == 0:
169-
line = f"{summary} is today."
170-
else:
171-
line = "{} starts at {}".format(summary, start.strftime("%H:%M"))
229+
line = event_to_message(event)
172230
print("Sending reminder:", line)
173231
messages.append(line)
174232
keys.add(key)
175-
176233
if not messages:
177234
return
178235

179236
if len(messages) == 1:
180-
message = "Reminder: " + messages[0]
237+
message = "**Reminder:**\n\n " + messages[0]
181238
else:
182-
message = "Reminder:\n\n" + "\n".join("* " + m for m in messages)
239+
message = "**Reminders:**\n\n" + "\n".join(
240+
str(i + 1) + ". " + m for i, m in enumerate(messages)
241+
)
183242

184243
zulip_client.send_message(dict(type="private", to=options.zulip_email, content=message))
185244

0 commit comments

Comments
 (0)