-
-
Notifications
You must be signed in to change notification settings - Fork 393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update Google Calendar integration #856
Conversation
Fixes: zulip#847 Co-authored-by: Vedant Joshi <[email protected]> Co-authored-by: Pransh Gupta <[email protected]>
Remove "sender" parameter. Rename the "type" parameter value from "private" to "direct". Pass in a list to the "to" parameter. Switch to using the {} syntax for the dict. Co-authored-by: Vedant Joshi <[email protected]>
Previously used scope: `calendar.readonly` New scope: `calendar.events.readonly` Other than events, a calendar contains `settings`, `addons` ,`app`, `calendarlist`, `calendars`, `acls` (permissions), `freebusy` (availability), and more. Since this integration only sends reminders, we need access only to the events. More narrow scopes like `calendar.events.owned.readonly` and `calendar.events.public.readonly` are available, but we want to be able to support shared calendars as well, so we're not using them. Also removed a comment regarding SCOPES that has now become redundant.
Instead of using the hardcoded file name value everywhere directly. This enables us to edit the file name with a single change in the following commit.
Replaces some occurrences of the term "credentials" with "tokens" for clarity, to conform with the terms used in Google API documentation. Renamed: - "google-credentials.json" to "google-tokens.json" - `credential_path` to `tokens_path` Google documentation refers to the client secret file sometimes as "credentials.json", and the tokens file as "tokens.json". We have been using "client-secret.json" and "google-credentials.json" respectively, which can be confusing. So, this commit switches to using the term "tokens" instead of "credentials" wherever appropriate, to avoid confusion. The term "credentials" is still used to refer to the Credentials object returned after authorization.
- Added a link to the integration doc. - Removed the initial space. - Fixed the command usage for the calendar option. - Added the integration-provided options to the first line. - Mentioned downloading the client secret file in the instructions. - Made other minor edits to the writing.
- Removed add_argument() parameters set to default values. - Renamed `dest` of calendarID to calendar_id, in order to make flag names uniform, in preparation for adding support for loading them from a config file. - Cleared up spacing, and made minor clarifications to `help`.
Leveraged the provisioning support provided by `init_from_options`. Enabled the `--provision` argument by setting `allow_provisiong` to True in `add_default_arguments`. Re-ordered the script such that the imports are executed after `init_from_options`. Updated the import error message to recommend using the command with the --provision argument to install the dependencies.
Currently, the integration prints all reminders to the terminal. Set logging level to info when --verbose is set, and switched `print` to `logging.info`, to restrict that output to only when the --verbose option is set.
Log the error response without halting the script.
Removed it from google-calendar script, as it is only used for the first run authorization by get-google-credentials. Edited a comment clarifying its purpose in get-google-credentials.
The auth tokens expire every hour. So, the google-calendar script would stop functioning one hour after start, if it only loads from the token file. The get-google-credential script needs to be called directly from the google-calendar script, without the user needing to run that as a separate command. The get-google-credentials script is not a module. And it uses hyphens in its name, hence it cannot be directly imported into google-calendar. To avoid renaming files, and smoothly pass in arguments, runpy is used. Though google-calendar script is currently the only google service integration that uses get-google-credentials, the script is not merged with the google-calendar script, to keep the logic separate, and easy to expand. The get-google-credentials script can no longer be run directly, as the get_credentials() arguments do not have any default values, with the constants deleted, to avoid redundancy with the google-calendar script. Updated the function docstrings, usage help and error messages.
The script currently mandatorily requires the --user argument to be passed, this commit removes the need for the --user flag.
The reminders were previously being sent to the DMs of the current user, requiring the zuliprc of the user. But, we can create a generic bot for the Google Calendar integration, and use its zuliprc to send direct messages to its owner. This tightens security. The support for sending DMs to the same user is retained, for cases where a bot is not used.
Previously, until last commit, the zuliprc of the user was being used directly. Now that we're using the zuliprc of a bot, it is safe for multiple users with a shared calendar to have a bot send the reminders to a channel. Added channel and topic arguments, with the default behavior set to sending a direct message.
This allows the integration to be run from non-interactive environments or on devices without browsers, like remote servers. Co-authored-by: Vedant Joshi <[email protected]>
For passing in custom file paths.
By loading from the new "google-calendar" section of the zuliprc. Moved the default values out of the argparse arguments, and into a dataclass, to implement the below hierarchy of loading options: - initialize options to default values - overwrite with the options present in the config file - overwrite with the options passed-in via the command line
Currently, all the reminders are collected together before sending a message, and are formatted as bullet points in the same message. This commit uses a message per event, in preparation of adding more details to each reminder message.
The event tuple is switched to a TypedDict. Moved the code logic to construct the message string from the event into its own function. This is in preparation for adding support for more event fields.
Moved the parsing logic into `get_start_or_end` to enable re-using the function when we add support for the `end` field of event.
Added the following Event fields to the message template: - event end time - description - link to event in Google Calendar - location of event, if any - link to Google Meet, if any Co-authored-by: Vedant Joshi <[email protected]>
By adding a --format-message command line option, and a format_message config file option.
@laurynmm Are you up for reviewing this one? I haven't tried to wrap my head around it. Not specifically a release goal. |
@@ -166,7 +163,7 @@ def send_reminders() -> Optional[None]: | |||
message = "Reminder:\n\n" + "\n".join("* " + m for m in messages) | |||
|
|||
result = zulip_client.send_message( | |||
{"type": "direct", "to": [options.zulip_email], "content": message} | |||
{"type": "direct", "to": [zulip_client.get_profile()["email"]], "content": message} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The zulip_client.get_profile()["email"]]
call should probably be at the beginning, not done every time one sends -- at least if we use a user ID rather than email. (I've not checked if later commits do that already).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This moves around, but I think we might want to add a tweak to do ["id"]
instead -- and also probably a to
parameter to just set a user ID who it should DM, like you have for the channel/topic parameters.
(Maybe there's some precedent in zulip/examples
for how to name it)
# Boolean arguments (store-true) have a default value of False when not passed in. | ||
# So, we ignore them, to prevent overwriting the config file option that is set. | ||
if key in valid_keys and value is not None and value is not False: | ||
setattr(calendar_options, key, value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably these two functions will want to eventually move to common code that other integrations can use too. But this is fine for this PR.
@@ -251,6 +258,19 @@ def populate_events() -> Optional[None]: | |||
|
|||
|
|||
def construct_message_from_event(event: Event) -> str: | |||
if calendar_options.format_message: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feature might be overkill. I guess I'll merge it.
Looks great, merged, thanks @Niloth-p! |
Fixes: #847
Fixes: #850 (to auto-close absorbed PRs)
Fixes: #851 (to auto-close absorbed PRs)
Please don't get intimidated by the number of commits, it's an attempt to make things more readable and easy to review, not less so.
Updates to the integration - summarized
Major changes in the behavior of the integration include:
Bugs
Integration Features
Security Improvements
Usability Features
Developer Features - Vastly improved the code readability. All decisions are explained either in the comments or the commit messages (which add only 1 feature at a time, to clarify the intention, and create verifiable history).
Failing CI is unrelated
The failing CI seems to be unrelated to the changes in this PR.
Error:
Which has nothing to do with this PR.
User Experience - Feedback welcome!
Includes the message template, and the default file location
~/client_secret.json
.credentials.json
. But, we went against naming it that way to avoid confusing with other credentials.client_secret.json
.Manual and automated testing
Self-review checklist
(variable names, code reuse, readability, etc.).
Communicate decisions, questions, and potential concerns.
Individual commits are ready for review (see commit discipline).
Completed manual review and testing of the following: