# Google Workspace OAuth — End-to-End Setup (LFCS)

## Context
Project: `hermes-lfcs` (created 2026-05-11)
OAuth client: `Desktop client 1` (client ID `890850960489-5hejo1gd3apvpn1nu1183ohk4u9rlnev`)
OAuth client secret saved: `/root/.hermes/google_client_secret.json`
Token saved: `/root/.hermes/google_token.json`

## APIs to enable (Google Cloud Console → hermes-lfcs project)
- `drive.googleapis.com`
- `sheets.googleapis.com`
- `gmail.googleapis.com`
- `docs.googleapis.com`
- `calendar-json.googleapis.com` (Calendar API)
- `contacts.googleapis.com` (People API)

## Required scopes by operation

| Operation | Scopes needed |
|-----------|---------------|
| Search/read Drive | `drive.readonly` |
| Create Drive files | `drive.file` |
| Create/edit Docs | `documents` |
| Send Gmail | `gmail.send` |
| Read Gmail | `gmail.readonly` |
| Sheets read/write | `spreadsheets` |
| Calendar | `calendar` |

**Minimum for site report workflow:** `drive.file` + `documents` + `gmail.send` (+ `gmail.readonly` for searching sent mail)

## The project/token mismatch problem

Symptom: ` Caller does not have required permission to use project opsman-lfcs-27665`

Cause: Access token is still bound to an **old** OAuth client/project, not `hermes-lfcs`. Simply downloading a new `client_secret.json` does NOT fix this.

Fix: Re-authenticate:
```bash
python /root/.hermes/skills/productivity/google-workspace/scripts/setup.py --auth-url
# User pastes URL → allows → pastes redirect URL
python /root/.hermes/skills/productivity/google-workspace/scripts/setup.py --auth-code "CODE_FROM_REDIRECT"
```

## google_api.py script limitations

The `google_api.py` script (gws CLI wrapper) has TWO separate failure modes:

1. **"project opsman-lfcs-27665" error** — gws binary routes through wrong project. The underlying API call often SUCCEEDS despite this error. Check actual operation result, not the error output.

2. **drive.readonly scope insufficient for file creation** — `google_api.py drive search` works fine with `drive.readonly`, but creating files requires `drive.file`. The script doesn't tell you this.

Workaround: Use direct Python API calls when `google_api.py` fails:
```python
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials

creds = Credentials.from_authorized_user_file('/root/.hermes/google_token.json', [
    'https://www.googleapis.com/auth/drive.file',
    'https://www.googleapis.com/auth/documents'
])
drive = build('drive', 'v3', credentials=creds)
doc = drive.files().create(
    body={'name': 'Report Name', 'mimeType': 'application/vnd.google-apps.document'},
    fields='id, webViewLink'
).execute()
```

## Gmail send via direct API
```python
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from email.mime.text import MIMEText
import base64

creds = Credentials.from_authorized_user_file('/root/.hermes/google_token.json', [
    'https://www.googleapis.com/auth/gmail.send'
])
service = build('gmail', 'v1', credentials=creds)
msg = MIMEText(body_text)
msg['to'] = 'admin@lfcs.com.au'
msg['subject'] = 'Subject'
raw = base64.urlsafe_b64encode(msg.as_bytes()).decode()
result = service.users().messages().send(userId='me', body={'raw': raw}).execute()
```

## Re-auth needed when
- New scopes added to the OAuth client in Google Cloud Console
- Token expires (should auto-refresh; if not, re-auth)
- Switching between read-only and write operations
- API returns `invalid_scope: Bad Request` or `Insufficient Permission` despite working auth previously

**Do NOT edit scopes in google_token.json to fix scope issues** — the OAuth grant is server-side. Only a full re-auth (`--auth-url` → paste → `--auth-code`) fixes it. The `opsman-lfcs-27665` error is a different problem (wrong project绑定).

## Photo upload — use embed_photos.py
```bash
python /root/.hermes/embed_photos.py
```
Uploads all 13 Liverpool photos to Drive, generates public links. Links go in the doc as plain text — inline image embedding via Docs API is not worth the effort (thumbnail rendering, broken images, API limitations). Email the Drive folder link instead.

## File creation then email — full workflow

```python
# 1. Create doc
drive = build('drive', 'v3', credentials=creds)
doc = drive.files().create(
    body={'name': 'Report Name', 'mimeType': 'application/vnd.google-apps.document'},
    fields='id, webViewLink'
).execute()
doc_id = doc['id']

# 2. Add content
docs = build('docs', 'v1', credentials=creds)
docs.documents().batchUpdate(
    documentId=doc_id,
    body={'requests': [{'insertText': {'location': {'index': 1}, 'text': content}}]}
).execute()

# 3. Email link
gmail = build('gmail', 'v1', credentials=creds)
msg = MIMEText(f'Report link: {doc["webViewLink"]}')
msg['to'] = 'admin@lfcs.com.au'
msg['subject'] = 'Report Name'
gmail.users().messages().send(userId='me', body={'raw': base64.urlsafe_b64encode(msg.as_bytes()).decode()}).execute()
```