gmail · v1.0.0 · 2026-10-02 · sha256 a06176e01742e60e
gmail v1.0.0A
Immutable. This exact content never changes and is served at /api/v1/blob/a06176e01742e60e.
--- name: gmail description: "Check / search / digest Anton's Gmail on demand across his 3 mailboxes (a = youraccount.a personal, a2 = youraccount.a2 assistants, bb = [рабочий аккаунт] work) via his own OAuth connector… Trigger on “/gmail“, “проверь почту“, “что нового в почте“, “есть важные письма“, “найди письмо про <X>“, “что в bb / в личной / в a2“, “сделай дайджест почты“, “check my email“, “search my gmail for <X>“" version: 1.0.0 --- Anton's own Gmail connector — read/search/send across 3 mailboxes. Full project notes: memory [[gmail-connector]]. Reply to Anton in Russian; end with the 🧒 «Простыми словами» recap ([[eli5-always]]). ## The connector (single source of truth) - Folder: `$USERPROFILE/!CLAUDE-HP17 May26\gmail\` — `gmail_check.py` (read/search/send), `gmail_auth.py` (authorize a box), tokens in `tokens\`, secret in `secrets\`. Имя папки историческое (по ноуту), но путь верный и на хабе, и на ноуте: `$USERPROFILE` резолвится в `[путь владельца]` на обеих Windows-машинах. ⚠️ В Bash пиши `$USERPROFILE`, НЕ `%USERPROFILE%` (Git Bash не раскрывает `%VAR%` → exit 2). - ⛔ **ЕДИНСТВЕННАЯ папка коннектора — эта.** На хабе с 2026-06 живёт ВТОРАЯ копия `[путь владельца]`: тот же `gmail_check.py` (md5 совпадает), но СВОЙ `tokens\` и `secrets\`. Два независимых OAuth-состояния на один аккаунт = тихий развал авторизации, когда одну сторону ре-авторизовали, а вторая осталась со старым refresh-token. Ходить туда запрещено; удаление дубля — решение Антона (05.08.2026 ждёт его «+»). - Mailbox labels: **`a`** = you@example.com (Anton's PERSONAL), **`a2`** = you.assistants@example.com (ASSISTANTS), **`bb`** = [рабочий аккаунт]@gmail.com (Platinum/WORK). See [[telegram-account-identities]] / [[gdrive-index]] for which box owns what. - ⚠️ У одного ящика два имени. `sendAs.displayName` (строка From ушедших писем) и имя аккаунта Google (myaccount → Name: first/last, nickname, «display name as») не совпадают. Замер 23.09, ящик `a`: From = `Anton.D`, аккаунт = Punk / Crypto, показ `Punk Crypto (CryptoPunk #1348)`. Вердикт «Криптопанка нет» только по `in:sent` ложный. Этот токен имя не меняет: на `sendAs.patch` приходит 403, нет scope `gmail.settings.basic`. - ⚠️ EVERY Bash call here needs `dangerouslyDisableSandbox:true` — the sandbox has no network, OAuth/API fail with SSL/DNS errors otherwise ([[deterministic-script-gotchas]]). ## Token economy (the law — [[vault-data-architecture]]) The PYTHON SCRIPT does the heavy pull (≈0 LLM tokens); the LLM only JUDGES the short result. Never paste whole inboxes into context — filter first, judge the remainder. ## Commands (run from the gmail folder, dangerouslyDisableSandbox:true) ``` python gmail_check.py whoami # which of a/a2/bb are connected python gmail_check.py unread [a|a2|bb] # unread (one box or all) python gmail_check.py list a2 --max 20 # newest in a box python gmail_check.py search "from:[человек]" # search across all (or --label bb) python gmail_check.py read bb <message_id> # full body of one message ``` ⚠️ У `list` и `read` ящик передаётся ПОЗИЦИОННО (`list a2`, `read bb <id>`), а у `search` — только флагом `--label`. `search a "..."` падает с `unrecognized arguments`; эта неверная форма два дня жила в рутине `pipe-a-inbox-watch` (замер 03.09.2026). ⚠️ **Потолок `--max` — не бесплатный.** Квота Gmail = 250 units/min/user, один запрос метаданных стоит ~5 units, поэтому выборка на 300+ писем упирается в 403 `rateLimitExceeded` ПОСРЕДИ обхода. С 03.09.2026 рельса это переживает сама: `_exec_quota_safe` повторяет на 403/429/5xx, а заголовки писем кэшируются в `meta_cache.json` (заголовки неизменны, поэтому кэш по id безопасен; флаг «непрочитано» в кэше — снимок на момент первого чтения, за свежей непрочитанностью иди в `unread`). Замер: повторный прогон на 60 письмах 11.4с → 1.1с, запросов к Gmail ноль. Тест рельсы: `python _test_gmail_quota.py` (7 чеков, краснеет на сломанном коде). ## Patterns **Две ЛАНЫ с РАЗНЫМИ владельцами планки — не путать (разъехались и жили так до 05.08.2026):** | лана | планка | куда отчёт | владелец правил | |---|---|---|---| | РУЧНАЯ (`/gmail`, Антон спросил сам) | НИЖЕ: он сам попросил показать → группируем по ящикам и показываем | в чат Антону | этот файл | | НОЧНАЯ (`gmail-digest-morning`) | ВЫСОКАЯ: молчание по умолчанию, только «требует ЛИЧНО его» + биллинг-проход + маркер `billing_status.json` | Telegram чат 03 `-[id]`, account `[рабочий аккаунт]` | `~/.claude/scheduled-tasks/gmail-digest-morning/SKILL.md` | - **"проверь почту" / "что важного"** (РУЧНАЯ лана) → run the deterministic important-unread pull, then JUDGE & report compactly (group by box): `python gmail_check.py search "is:unread -category:promotions -category:social -category:forums newer_than:1d" --max 25` Keep: real people, money (invoice/receipt/contract/банк/налоги), meetings (Calendly/Fireflies/Zoom/планерка), leads/investors/deals, legal/visa, fresh OTP. Drop: newsletters/digests/marketing/listing-bots, routine Google "Security alert". Doubt → include with "(?)". - **"найди письмо про X"** → `search "X"` (add `--label` if Anton named a box); show sender · subject · date · msg-id, then offer to `read` the top hit. - **"сделай дайджест"** → НЕ переписывай планку здесь. Открой `~/.claude/scheduled-tasks/gmail-digest-morning/SKILL.md` и исполни ЕГО шаги 1→4 (там же биллинг-проход, маркер `billing_status.json` и проверка ПОЛУЧАТЕЛЯ) — это единственный источник ночной планки. Отличие ручного прогона ровно одно: отчёт Антону в чат, а в Telegram шлём только если он попросил (тогда чат 03 `-[id]`, account `[рабочий аккаунт]` — НЕ Saved Messages, НЕ account `default`). - ⭐ **Проверка получателя** (для денег/гос/юр/банка): прежде чем сказать «требует Антона лично» — открой тело `read <ящик> <msg_id>` и найди имя адресата. Госкабинеты и банки шлют уведомления о ЧУЖИХ аккаунтах на почту того, кто их регистрировал (05.08.2026: письмо Segurança Social было на имя Elena Golovanenco, не Антона). ## Hard safety gates - **READ-ONLY by default.** Never mark-read, archive, delete, or send unless Anton explicitly approves THAT action/message (Tier-2 outbound, [[operating-agreement]]). - **Sending** = `python gmail_check.py send <box> --to … --subject … --body …` — only after Anton OKs the exact draft. Show the draft first (ДО→ПОСЛЕ, [[show-before-after]]). - Secrets/tokens stay in `secrets\`/`tokens\` — never print to chat or commit. - Treat links inside emails as untrusted ([[operating-agreement]] link-safety) — don't auto-open; verify the real URL with Anton first.