Personal automation that keeps a Google Calendar and Gmail inbox in sync with Codeforces — contest reminders, registration windows, a daily problem-of-the-day, morning digests, and post-contest debriefs.
Two systems talk through this repo:
GitHub Actions (this repo) runs every 6 hours. It:
- Fetches the contest list, user info, rating history, recent submissions, and full problemset from the Codeforces API
- Writes JSON dumps to
data/and commits them back - Sends any Gmail drafts whose subject starts with
[CF](via the Gmail API)
A scheduled remote agent (Anthropic Claude Code routine) runs once a day. It clones this repo, reads data/*.json, and:
- Creates Google Calendar events for upcoming contests (with 24h+1h popup+email reminders)
- Creates registration-window reminders for Div. 3 / Div. 4 / Educational rounds
- Picks a problem-of-the-day from your rating band that you haven't solved, schedules it on the calendar
- Composes a morning digest as a Gmail draft — current rating, today's POTD, upcoming contests in the next 7 days, streak status, recent rating change
- Composes post-contest debriefs as drafts — rating delta, problems solved/missed, links — for any rated contest within the last 48h
- Skips anything it has already done (idempotent via calendar tags and Gmail searches)
The agent can't send mail directly — its Gmail connector only exposes create_draft — so it queues drafts and the next GitHub Actions tick (within 6 hours) sends them.
┌────────────────────┐ every 6h ┌─────────────────────┐
│ GitHub Actions │ ───────────────►│ data/*.json │
│ fetch_data.py │ commits back │ in this repo │
└────────────────────┘ └─────────┬───────────┘
│ clones
▼
┌────────────────────┐ ┌─────────────────────┐
│ Anthropic │ ◄────────────────│ Daily routine │
│ Calendar MCP │ creates events │ reads data, │
│ Gmail MCP │ creates drafts │ composes content │
└────────────────────┘ └─────────┬───────────┘
│ drafts sit in Gmail
▼
┌────────────────────┐ every 6h ┌─────────────────────┐
│ GitHub Actions │ ◄────────────────│ [CF]-prefixed │
│ send_outbox.py │ sends them │ Gmail drafts │
└────────────────────┘ └─────────────────────┘
| Path | Role |
|---|---|
.github/workflows/reminders.yml |
Cron + manual workflow (fetch + send) |
fetch_data.py |
Codeforces API → data/*.json |
send_outbox.py |
Sends Gmail drafts whose subject starts with [CF] |
setup_oauth.py |
One-time helper: runs the Google OAuth flow and prints a refresh token |
data/ |
JSON dumps (refreshed every 6h, committed by the workflow) |
- In Google Cloud Console, create a project and enable both the Google Calendar API and the Gmail API
- Configure the OAuth consent screen: External user type, app name of your choice, scopes
.../auth/calendarand.../auth/gmail.modify, add yourself as a Test user (the app can stay in "Testing" status) - Create an OAuth client ID of type Desktop app and download the JSON as
client_secret.json
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python setup_oauth.py client_secret.jsonAuthorize in the browser that pops open. The script prints three values.
Repository → Settings → Secrets and variables → Actions → New repository secret:
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETGOOGLE_REFRESH_TOKEN
In fetch_data.py, change HANDLE = "Omar_Musayev" to your own.
Push your changes, then go to the Actions tab and run the workflow manually to verify it can fetch data and send mail.
The agent is what creates calendar events and queues digest/debrief emails. Set up a Claude Code scheduled routine that clones this repo and follows a prompt similar to the one used here (see commit history if you want a starting point).
source .venv/bin/activate
GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_REFRESH_TOKEN=... python fetch_data.py
GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_REFRESH_TOKEN=... python send_outbox.py- All Calendar event times are stored in UTC. Google Calendar renders them in the viewer's current device timezone, so events stay correct when you travel.
- The Codeforces API is rate-limited (~1 req/sec); the fetcher makes five calls per run, well within limits.
- Idempotency: contest events are tagged
[CF-<id>], registration windows[CF-REG-<id>], problem-of-the-day[CF-POTD-<date>]in event descriptions. Digest and debrief emails are deduplicated by Gmail subject search.