mirror of
https://gitverse.ru/kpa39l/email-assistant.git
synced 2026-09-29 09:15:09 +00:00
Session close 2026-09-15 (вечер): imap_stream Фаза 1 реализован, backlog urgent закрыт (фильтр актуальности), live-тесты OK
This commit is contained in:
@@ -2,6 +2,83 @@
|
||||
|
||||
Воспроизводимость: хронология, команды, решения, ошибки и как чинили.
|
||||
|
||||
## 2026-09-15 (вечер, сессия @session:default/20260915_174925_ec1ca4)
|
||||
|
||||
### imap_stream.py — Фаза 1 (IDLE-цикл) реализован
|
||||
|
||||
**Цель:** постоянный IMAP-поток (задачи 3–5 change imap-realtime-sync). aioimaplib
|
||||
не ставится → свой клиент на сыром socket поверх `imap_client.imap_connect`.
|
||||
|
||||
**Архитектура `scripts/imap_stream.py`:**
|
||||
- `ImapStream` — обёртка над сокетом: теги, `_cmd`, `_cmd_ok`, чтение до тега.
|
||||
- IDLE-цикл: `idle_start` → `idle_wait(25с)` → `idle_done` → перевыпуск.
|
||||
- Reconnect: при ошибке `CONNECT_PAUSE=45с` (rate-limit Exchange), повтор.
|
||||
- `reconcile_new(folder)` — поиск UID > last_uid, архивация через mail_archive,
|
||||
запись в `mailbox_state` + событие `mailbox_events`.
|
||||
- `reconcile_full(folder)` — полное сравнение UID/флагов (каждый 5-й цикл).
|
||||
- SQLite: `/opt/hermes/email/state/mailbox.db`, таблицы `mailbox_state` (uid,
|
||||
folder, message_id, in_reply_to, refs, flags, has_attachment, archive_path,
|
||||
last_seen, deleted) и `mailbox_events` (event/ts/folder/uid/detail).
|
||||
- CLI: `--check` (авторизация), `--test-idle` (IDLE 30с), `--status`, `--metrics`,
|
||||
без аргументов — демон.
|
||||
|
||||
**Питфолы Exchange, найденные live-тестами (пауза ≥45с между коннектами):**
|
||||
1. `_load_credentials` в imap_client.py читал `Path.home()` — в сессии Hermes
|
||||
HOME≠реальный. Фикс: `HERMES_REAL_HOME` (env) → `/home/estorozhenko`. Cron
|
||||
уже делает это в mail-archive.sh.
|
||||
2. `_cmd_ok`: `resp.split(b"\r\n")[-1]` давал пустой элемент (хвост `\r\n`) —
|
||||
команда считалась failed даже при OK. Фикс: разбор строк до тега.
|
||||
3. Колонка `references` — зарезервированное слово SQLite → `refs`.
|
||||
4. **UID-батчинг сломан:** `UID SEARCH UID 1:1000` пуст (UID — глобальный номер
|
||||
~14200+, не порядковый); первый пустой батч обрывал цикл, все письма ложно
|
||||
помечались deleted. Фикс: `UID SEARCH ALL` / `UID SEARCH UID <min>:*`
|
||||
(проверено: 365 UID).
|
||||
5. Exchange отвечает на IDLE `+ IDLE accepted, awaiting DONE command.` —
|
||||
НЕ `+ idling`. Фикс: матч `+ IDLE` или `+ idling`.
|
||||
6. **Exchange рвёт IDLE-соединение ~60с** (не 30 мин) — `IDLE_TIMEOUT=25с`
|
||||
(перевыпуск до серверного лимита). Демон переживает обрыв: reconnect 45с.
|
||||
|
||||
**Live-тесты (все с паузой ≥45с):**
|
||||
- `--check`: connect+LOGIN+SELECT 365 писем → OK.
|
||||
- `--test-idle`: SELECT → reconcile (0 новых, deleted=0, flags=18) → IDLE 30с →
|
||||
0 событий → DONE → EXIT=0.
|
||||
- Демон 150с: stream_started → SELECT 365 → обрыв IDLE (~60с) →
|
||||
reconnect_pause 45с → SELECT 365. Цикл переподключения работает.
|
||||
|
||||
**Git:** 5a81c86 запушен в gitverse (defcf53..5a81c86). Файлы: scripts/imap_stream.py
|
||||
(новый, ~690 строк), scripts/email_handlers.py (+фильтр актуальности), scripts/imap_client.py
|
||||
(+HERMES_REAL_HOME).
|
||||
|
||||
### Backlog urgent-pисем: корень найден + фильтр актуальности
|
||||
|
||||
**Проблема:** 10 urgent-писем не уходили в Telegram. Причина: при наличии
|
||||
python-dotenv `email_handlers.py` шёл в ветку `if load_dotenv` — грузил только
|
||||
`BASE_DIR/.env` и `radicale/.env`, НО НЕ `/opt/vesti/.env` → `VESTI_BOT_TOKEN`
|
||||
пуст → все обработки падали. Строка `✗ [urgent] ...: Нет токена Telegram`.
|
||||
|
||||
**Фикс (email_handlers.py):** `/opt/vesti/.env` грузится всегда (до ветвления).
|
||||
|
||||
**Запрос пользователя (mid-turn):** «перестань слать неактуальные срочные
|
||||
уведомления; встроить проверку на актуальность — сравнивать дату письма и
|
||||
текущую». Реализовано:
|
||||
- `URGENT_MAX_AGE_DAYS = 3` — письма старше 3 дней не шлются.
|
||||
- `is_urgent_recent(headers)` — парсит дату из frontmatter, сравнивает с now.
|
||||
- Старое письмо: `✓ [urgent] ...: пропущено (актуальность истекла)`, помечается
|
||||
`handled_urgent` (идемпотентность, не перебирается).
|
||||
|
||||
**Результат backlog:** 5 сентябрьских доставлены в ЛС (msg_id 62–64 и далее),
|
||||
5 старых (июль/авг) — пропущены фильтром корректно. Январьское «Сервер» — на
|
||||
деле `classification: info` (не urgent, ошибка подсчёта). 14229 помечен вручную
|
||||
(уже дважды уведомлён — предотвращён дубль).
|
||||
|
||||
**Известный отдельный баг (не urgent):** `✗ [task] ...: PUT 400: Bad Request`
|
||||
(Radicale VTODO-задача) — не разобран, открыт на следующую сессию.
|
||||
|
||||
### Cron-заметка
|
||||
|
||||
`mail-classify-handlers` (в venv /opt/vesti/.venv/bin/python, --limit 2/прогон)
|
||||
работает; после фикса токена обрабатывает по 2 письма за прогон идемпотентно.
|
||||
|
||||
## 2026-09-11
|
||||
|
||||
### Фаза 1.7: динамическое обнаружение подпапок INBOX
|
||||
|
||||
Reference in New Issue
Block a user