130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""List today's events from ALL Google Calendars (not just primary).
|
|
|
|
Usage:
|
|
python3 agenda_all_calendars.py [YYYYMMDD] # default: today
|
|
|
|
Outputs JSON array of events with added 'calendar' field.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
|
|
# ----------------------------------------------------------------
|
|
# Paths
|
|
# ----------------------------------------------------------------
|
|
HERMES_HOME = Path(os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")))
|
|
TOKEN_PATH = HERMES_HOME / "google_token.json"
|
|
CLIENT_SECRET_PATH = HERMES_HOME / "google_client_secret.json"
|
|
|
|
SCOPES = [
|
|
"https://www.googleapis.com/auth/calendar",
|
|
"https://www.googleapis.com/auth/gmail.readonly",
|
|
"https://www.googleapis.com/auth/gmail.send",
|
|
"https://www.googleapis.com/auth/gmail.modify",
|
|
"https://www.googleapis.com/auth/drive",
|
|
"https://www.googleapis.com/auth/contacts.readonly",
|
|
"https://www.googleapis.com/auth/spreadsheets",
|
|
"https://www.googleapis.com/auth/documents",
|
|
]
|
|
|
|
|
|
def get_credentials():
|
|
"""Load and refresh credentials."""
|
|
from google.oauth2.credentials import Credentials
|
|
from google.auth.transport.requests import Request
|
|
|
|
if not TOKEN_PATH.exists():
|
|
print("[]", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
creds = Credentials.from_authorized_user_file(str(TOKEN_PATH), SCOPES)
|
|
if creds.expired and creds.refresh_token:
|
|
creds.refresh(Request())
|
|
# Save refreshed token
|
|
TOKEN_PATH.write_text(
|
|
json.dumps(
|
|
{**json.loads(creds.to_json()), "type": "authorized_user"},
|
|
indent=2,
|
|
)
|
|
)
|
|
return creds
|
|
|
|
|
|
def get_all_calendar_events(date_id_str=None):
|
|
"""
|
|
Discover all calendars and get today's events from each.
|
|
Returns list of events with 'calendar' field added.
|
|
"""
|
|
from googleapiclient.discovery import build
|
|
|
|
if not date_id_str:
|
|
date_id_str = datetime.now().strftime("%Y%m%d")
|
|
|
|
dt = datetime.strptime(date_id_str, "%Y%m%d")
|
|
# Use Toronto timezone
|
|
from zoneinfo import ZoneInfo
|
|
tz = ZoneInfo("America/Toronto")
|
|
|
|
# Start of day in Toronto time
|
|
start_dt = datetime(dt.year, dt.month, dt.day, tzinfo=tz)
|
|
end_dt = start_dt + timedelta(days=1)
|
|
|
|
time_min = start_dt.isoformat()
|
|
time_max = end_dt.isoformat()
|
|
|
|
creds = get_credentials()
|
|
service = build("calendar", "v3", credentials=creds)
|
|
|
|
# 1. Discover all calendars
|
|
cal_list = service.calendarList().list().execute()
|
|
calendars = cal_list.get("items", [])
|
|
|
|
all_events = []
|
|
for cal in calendars:
|
|
cal_id = cal["id"]
|
|
cal_name = cal.get("summary", cal_id)
|
|
cal_selected = cal.get("selected", True) # user has this calendar visible
|
|
|
|
if not cal_selected:
|
|
continue # skip hidden calendars
|
|
|
|
try:
|
|
result = service.events().list(
|
|
calendarId=cal_id,
|
|
timeMin=time_min,
|
|
timeMax=time_max,
|
|
singleEvents=True,
|
|
orderBy="startTime",
|
|
maxResults=50,
|
|
).execute()
|
|
except Exception:
|
|
continue # skip inaccessible calendars
|
|
|
|
for ev in result.get("items", []):
|
|
ev["calendar"] = cal_name # add calendar name
|
|
all_events.append({
|
|
"id": ev["id"],
|
|
"summary": ev.get("summary", "(no title)"),
|
|
"start": ev.get("start", {}).get("dateTime", ev.get("start", {}).get("date", "")),
|
|
"end": ev.get("end", {}).get("dateTime", ev.get("end", {}).get("date", "")),
|
|
"location": ev.get("location", ""),
|
|
"description": ev.get("description", ""),
|
|
"calendar": cal_name,
|
|
})
|
|
|
|
return all_events
|
|
|
|
|
|
def main():
|
|
date_id_str = sys.argv[1] if len(sys.argv) > 1 else None
|
|
events = get_all_calendar_events(date_id_str)
|
|
print(json.dumps(events, indent=2, ensure_ascii=False))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|