mirror of
https://gitverse.ru/kpa39l/email-assistant.git
synced 2026-09-29 09:15:09 +00:00
Session close 2026-09-15: gitverse source of truth, gitea mirror, token locations in AGENT.md
This commit is contained in:
+118
-1
@@ -303,4 +303,121 @@ VTIMEZONE Europe/Moscow + RRULE:FREQ=WEEKLY;BYDAY=TU, DTSTART 11:00.
|
||||
|
||||
**Cron:** mail-archive-every-5min (5f2305b2bbf8) приостанавливался на время
|
||||
отладки → **ВОЗОБНОВЛЁН** (next_run 08:05, state scheduled).
|
||||
вычитка openspec-файлов чейнджа.
|
||||
вычитка openspec-файлов чейнджа.
|
||||
|
||||
## 2026-09-15 — IMAP realtime sync (планирование) + Telegram-уведомления в ЛС
|
||||
|
||||
### 1. Диагностика авторизации: rate-limit, а не TLS-fingerprinting
|
||||
|
||||
**Симптом:** python/openssl/imaplib не логинятся на mail.corpoffice.tech:143
|
||||
(`NO AUTHENTICATE failed`), himalaya (rustls) логинится.
|
||||
|
||||
**Что перепробовано:**
|
||||
1. Raw-socket LOGIN для e.storozhenko / e.storozhenko@vinogorod.ru /
|
||||
VINOGOROD\e.storozhenko — NO.
|
||||
2. `ssl.wrap_socket` — `AttributeError` (убрано в py3.12) → `SSLContext.wrap_socket`.
|
||||
3. himalaya — логинится (эталон).
|
||||
4. Python с тем же base64 AUTHENTICATE PLAIN, что himalaya — NO.
|
||||
5. Сравнение TLS-отпечатков openssl vs rustls (ClientHello, ciphers) — различаются
|
||||
→ гипотеза **JA3 fingerprinting**.
|
||||
6. Порт 993 (IMAPS) через imaplib — тоже NO.
|
||||
7. **Опровержение:** чтение прод-кода `mail_archive.py` (~528) — он логинится
|
||||
простым `LOGIN login password` (не AUTH PLAIN). После паузы 45с — успех.
|
||||
Вывод: **rate-limit Exchange** (сервер молчит timeout после ~10 быстрых
|
||||
подключений), НЕ fingerprinting, НЕ бан IP.
|
||||
|
||||
**Урок:** не спешить с «экзотическими» диагнозами (JA3); сначала прочитать
|
||||
существующий прод-код — там уже рабочее решение.
|
||||
|
||||
### 2. `scripts/imap_client.py` — отдельная функция авторизации + наблюдаемость
|
||||
|
||||
- `imap_connect()`: socket → STARTTLS → LOGIN (простая форма, как в проде),
|
||||
re-try с экспоненциальной паузой 5→60с, креды из config/himalaya-config.toml.
|
||||
- JSON-лог в `/opt/hermes/email/logs/imap_client.log`: conn_ok/conn_error/
|
||||
auth_ok/auth_failed/session_started/session_ended (пароль никогда).
|
||||
- `imap_metrics()`: auth_success_rate, conn_ok/err, sessions_active.
|
||||
- `imap_session` (contextmanager) — гарантирует session_ended.
|
||||
- CLI: `python3 imap_client.py` (self-test), `--metrics`.
|
||||
- `mail_archive.py`: блок соединения заменён на `imap_connect()`;
|
||||
`sys.path.insert(0, str(Path(__file__).parent))` перед импортом imap_client
|
||||
(важно для cron: иначе импорт падает из-за cwd).
|
||||
- Проверено: `HOME=/home/estorozhenko python3 imap_client.py` →
|
||||
`OK: connected+LOGIN as e.storozhenko @ mail.corpoffice.tech`.
|
||||
ВАЖНО: запускать с `HOME=/home/estorozhenko` (конфиг himalaya там).
|
||||
|
||||
### 3. Telegram-уведомления о важных письмах — в ЛС (kpa39l)
|
||||
|
||||
- Создан `/opt/hermes/email-assistant/.env`: `TELEGRAM_CHAT_ID=281328953`
|
||||
(private chat kpa39l, бот @dedinit_controller_bot id 7765665742).
|
||||
Файл в .gitignore (строка 9) — не попадёт в git.
|
||||
- Порядок загрузки .env в email_handlers.py: проект `.env` → radicale/.env →
|
||||
/opt/vesti/.env; `override=False` → проект не перезаписывается.
|
||||
- Живой тест: письмо 3216 («RE: Платежи Аврора») помечено
|
||||
`classification: urgent` → отправлено в ЛС (msg_id=60), пользователь
|
||||
подтвердил получение. `handled_urgent: true` записан.
|
||||
|
||||
### 4. БАГ: крон не отправлял уведомления (python3 без httpx)
|
||||
|
||||
**Симптом:** 10 urgent-писем накопились без уведомлений.
|
||||
|
||||
**Причина:** `scripts/mail-classify-handlers.sh` вызывал `python3` (системный),
|
||||
у которого нет httpx → `tg_call` бросал RuntimeError, скрипт глушил ошибку
|
||||
`|| echo "[handlers] ошибка"`.
|
||||
|
||||
**Фикс:** `PY=/opt/vesti/.venv/bin/python` (fallback python3) + `--limit 2`
|
||||
(дозированно, анти-спам backlog). Проверено: `bash -n` OK.
|
||||
|
||||
### 5. Формат даты в уведомлениях
|
||||
|
||||
- `format_date_for_tg()`: `2026-09-02 10:49+03:00` → `02.09.2026 10:49`
|
||||
(ДД.ММ.ГГГГ ЧЧ:ММ; RFC3339 и Z тоже; незнакомое — as-is).
|
||||
- `_unquote_yaml()` в parse_email_md: снятие YAML-кавычек (`"..."` → значение).
|
||||
- Строка `📅 <дата>` в handle_urgent под заголовком.
|
||||
|
||||
### 6. Репозиторий: gitverse = источник истины + gitea mirror
|
||||
|
||||
**Решение пользователя (2026-09-15):** gitverse.ru — источник истины для всех
|
||||
репозиториев; gitea.nixg.ru — зеркало.
|
||||
|
||||
**Токен:** единый источник — `/opt/hermes/.hermes/secrets/git-tokens.env`
|
||||
(chmod 600): `GITVERSE_LOGIN=kpa39l` (НЕ estorozhenko!), `GITVERSE_PAT`,
|
||||
`GITVERSE_API=https://api.gitverse.ru`, `GITEA_API=http://127.0.0.1:3000/api/v1`,
|
||||
`GITEA_TOKEN`. Загрузка: `set -a && source ... && set +a`.
|
||||
|
||||
**Создание репо на gitverse (private, auto_init:false!):**
|
||||
```bash
|
||||
curl -X POST "$GITVERSE_API/user/repos" \
|
||||
-H "Authorization: Bearer $GITVERSE_PAT" \
|
||||
-H "Accept: application/vnd.gitverse.object+json;version=1" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name":"email-assistant","private":true,"auto_init":false}' # → 201
|
||||
```
|
||||
|
||||
**Push в gitverse:** remote `gitverse` = `https://kpa39l:$GITVERSE_PAT@gitverse.ru/kpa39l/email-assistant.git`; `git push gitverse master`.
|
||||
|
||||
**Pull mirror в gitea (12h):** migrate API:
|
||||
```bash
|
||||
curl -X POST "$GITEA_API/repos/migrate" -H "Authorization: token $GITEA_TOKEN" \
|
||||
-d '{"clone_addr":"https://oauth2:$GITVERSE_PAT@gitverse.ru/$GITVERSE_LOGIN/email-assistant.git",
|
||||
"repo_name":"email-assistant","repo_owner":"estorozhenko","service":"git",
|
||||
"mirror":true,"mirror_interval":"12h"}' # → 201
|
||||
```
|
||||
Проверка: `git ls-remote https://oauth2:$PAT@gitverse.ru/kpa39l/email-assistant.git HEAD` ==
|
||||
`docker exec gitea git --git-dir=/data/git/repositories/estorozhenko/email-assistant.git rev-parse HEAD`;
|
||||
mirror-sync `POST $GITEA_API/repos/estorozhenko/email-assistant/mirror-sync` → 200.
|
||||
|
||||
**Питфолы:**
|
||||
- База gitverse API — `api.gitverse.ru`, НЕ `gitverse.ru/api/v1` (там HTML SPA).
|
||||
- Gitea migrate только с `https://oauth2:<PAT>@...` (ssh:// → 422).
|
||||
- `auto_init:false` обязательно (иначе первый push упадёт).
|
||||
- `GITVERSE_API` уже содержит `https://` — не добавлять его повторно.
|
||||
- После пуша токен в remote URL — оставить (git config, не в коммитах); НЕ вычищать,
|
||||
иначе следующий push потребует креды.
|
||||
|
||||
### Открытые хвосты (2026-09-15)
|
||||
|
||||
- imap_stream.py (Фаза 1): скелет IDLE-цикла; aioimaplib не установлен —
|
||||
выбор: ставить его или свой клиент поверх imap_connect() (raw socket).
|
||||
- Backlog 10 urgent-писем (июль-сентябрь): отправляются по 2/прогон крона в ЛС;
|
||||
часть просрочена — решить, пропускать ли (пометить handled).
|
||||
- Live-тесты Exchange: пауза ≥45с между подключениями (rate-limit).
|
||||
Reference in New Issue
Block a user