# Google API REST Calls via Python stdlib (no pip, no requests, no google-api-python-client)

## Problem
On restricted headless systems like ZimaOS/CasaOS:
- No `pip` installed (or `ensurepip` fails due to root-owned paths).
- No `requests`, `httpx`, or `google-api-python-client` available.
- Cannot install anything via `pip install`.

## Solution
Use Python's built-in `urllib.request` + `json` (or `urllib.parse`) to call Google's REST APIs directly.

## Authentication
All Google APIs v3 accept a plain `Authorization: Bearer <access_token>` header. If the access_token is expired, refresh it via the OAuth2 endpoint:

```python
import urllib.request, urllib.parse, json

def refresh_access_token(client_id, client_secret, refresh_token):
    data = urllib.parse.urlencode({
        'client_id': client_id,
        'client_secret': client_secret,
        'refresh_token': refresh_token,
        'grant_type': 'refresh_token'
    }).encode()
    req = urllib.request.Request(
        'https://oauth2.googleapis.com/token',
        data=data, headers={'Content-Type': 'application/x-www-form-urlencoded'}
    )
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())['access_token']
```

## Calendar API Examples

### List Calendars
```python
req = urllib.request.Request(
    'https://www.googleapis.com/calendar/v3/users/me/calendarList',
    headers={'Authorization': f'Bearer {token}'}
)
with urllib.request.urlopen(req) as r:
    data = json.loads(r.read())
    for c in data.get('items', []):
        print(c['summary'], c['id'])
```

### List Events (next N days)
```python
from datetime import datetime, timezone, timedelta

now = datetime.now(timezone.utc).isoformat()
max_date = (datetime.now(timezone.utc) + timedelta(days=N)).isoformat()

url = (
    'https://www.googleapis.com/calendar/v3/calendars/'
    f'{urllib.parse.quote(cal_id)}/events'
    f'?timeMin={urllib.parse.quote(now)}'
    f'&timeMax={urllib.parse.quote(max_date)}'
    '&orderBy=startTime&singleEvents=true'
)
req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'})
with urllib.request.urlopen(req) as r:
    data = json.loads(r.read())
    for ev in data.get('items', []):
        print(ev['summary'], ev['start'].get('dateTime', ev['start'].get('date')))
```

### Create Event
```python
event = {
    'summary': 'Meeting',
    'start': {'dateTime': '2026-05-10T10:00:00+02:00', 'timeZone': 'Europe/Vienna'},
    'end':   {'dateTime': '2026-05-10T11:00:00+02:00', 'timeZone': 'Europe/Vienna'}
}
url = f'https://www.googleapis.com/calendar/v3/calendars/{urllib.parse.quote(cal_id)}/events'
req = urllib.request.Request(
    url,
    data=json.dumps(event).encode(),
    headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'},
    method='POST'
)
with urllib.request.urlopen(req) as r:
    print(json.loads(r.read())['id'])
```

### Delete Event
```python
url = (
    'https://www.googleapis.com/calendar/v3/calendars/'
    f'{urllib.parse.quote(cal_id)}/events/{urllib.parse.quote(event_id)}'
)
req = urllib.request.Request(url, method='DELETE', headers={'Authorization': f'Bearer {token}'})
with urllib.request.urlopen(req) as r:
    print(r.status)
```

## Key gotchas
- **Never** embed credentials in command output or logs.
- Use `urllib.parse.quote()` on any calendar ID or URL segment that contains `@`, `%` etc.
- Google returns 401 on expired token; refresh and retry in one function wrapper.
- Rate limits: ~100 requests per 100 seconds per user. Practically never hit for personal scripts.
- SSL context: on very old machines, you may need to accept TLS; test first before adding `ssl._create_unverified_context()`.

## Scope
Same approach works for:
- Gmail API (read labels, list messages via `messages.list`)
- Google Tasks API
- Google Drive API v3 (file metadata, upload via multipart)
- Any other Google API that supports OAuth 2.0 Bearer tokens.

## Why not use curl?
You *can* use `curl` for one-off calls, but for structured parsing (JSON output, date math, error handling) a Python script is more maintainable and testable.
