Calendupe copies obfuscated events from one calendar into another. For example, use it to copy events from a personal calendar into a work one.
Calendupe is a GCP-native program that uses the following cloud products:
- Google Calendar API to read and update calendars
- Google Cloud Storage to store calendar synchronization state and implement a global lock
- Google Cloud Functions to deploy a serverless API that listens for calendar updates
- Google Cloud Tasks to schedule resubscription to calendars when the notification channel is close to expiry
Follow these instructions to deploy Calendupe.
You'll need at least one bucket. It's recommended to create a separate bucket for the lock, with a Lifecycle Rule to expire objects after 1 day. This will ensure that if a function fails to release the lock, it will be cleared by GCS and prevent permanent deadlock.
Create a Tasks queue, e.g. gcloud tasks queues create calendupe-subscribe
- Copy the config example:
cp config_example.py config.py
- Populate the correct values
- Create a service account with the "Storage Objects Admin" and "Cloud Tasks Enqueuer" roles
- Download a key for the service account and save it as
service-account.json
in thecalendupe
folder - Share your source calendar with the service account email, allowing at least "See all event details"
- Share your target calendar with the service account email, allowing at least "Make changes to events"
From the calendupe
folder:
gcloud functions deploy calendupe --runtime python39 --trigger-http --allow-unauthenticated --service-account=<SERVICE_ACCOUNT>
Note that authentication is handled inside the API by matching the TOKEN
configuration parameter.
Ensure REMAIN_SUBSCRIBED = False
in your latest deployment config. Run subscribe.py
to subscribe to the calendar
for slightly over 1 hour, and ensure everything works as expected. Note that you'll need to modify the source
calendar before an initial sync is made. If you need to unsubscribe, the required resource ID will be printed at
subscribe time as well as written into the subscribe_logs
folder.
If your test subscription hasn't yet expired, unsubscribe. Update REMAIN_SUBSCRIBED = True
in your config and
redeploy the function. Re-run the subscription, unsubscribing before if needed. Check your cloud function logs to
ensure that it subscribed to the channel and scheduled a successful resubscription shortly after.
As long as the function is configured with REMAIN_SUBSCRIBED = True
, it will continue refreshing its subscription by
scheduling a resubscribe task for shortly before the channel expiry.
In order to disable calendupe:
- Ensure the current deployment has
REMAIN_SUBSCRIBED = False
- Run the
subscribe.unsubscribe()
method - Remove any pending resubscription tasks. This can be done from the Cloud Tasks Console
I hope to add:
- Custom naming and behavior for "free" events: they'll currently be copied with the same name (default "busy (personal)") and same availability
- Notification override: target calendar events use the default notification scheme, and don't respect the service account trying to override it. Depends on this Calendar bug