mirror of
https://gitverse.ru/kpa39l/email-assistant.git
synced 2026-09-29 09:15:09 +00:00
fix: архивация/вложения больше не помечают письма прочитанными (Seen)
Причина: himalaya message read и attachment download используют IMAP BODY[], который по RFC 3501 выставляет \Seen на сервере (Microsoft Exchange). Пользователь: письма в ящике после скачивания становятся прочитанными. Фикс: - чтение тела: himalaya message read --preview (не ставит Seen) - вложения: fetch_attachments_imaplib() — сырой IMAP stdlib (socket+ssl), UID FETCH (BODY.PEEK[]), папки в modified UTF-7, литералы до 1.5МБ, MIME-encoded words, фолбэк himalaya + flag remove seen Проверено живьём: UID 14200 (INBOX, docx 1.1МБ) флаги ()->() — Seen не выставлен. Openspec: change no-mark-seen-on-archive заархивирован (2026-09-14-no-mark-seen-on-archive), 7/7 validate OK
This commit is contained in:
@@ -247,4 +247,60 @@ VTIMEZONE Europe/Moscow + RRULE:FREQ=WEEKLY;BYDAY=TU, DTSTART 11:00.
|
||||
**Открыто на следующий заход:** живой прогон обработчиков (--limit 1 на каком-то
|
||||
письме с task/meeting), проверка доставки urgent в Telegram, cron
|
||||
(классификатор → обработчики после mail-archive), обновление STATUS.md/TODO.md,
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-14 — СРОЧНЫЙ ПАТЧ: письма НЕ помечаются «прочитанными» (no-mark-seen-on-archive)
|
||||
|
||||
**Симптом (от пользователя, срочно):** при скачивании письма в почтовом ящике
|
||||
становятся «прочитанными» (`\Seen`). Нужно, чтобы архивация/скачивание вложений
|
||||
НЕ трогали флаг.
|
||||
|
||||
**Диагностика:**
|
||||
1. `himalaya message read --help` — по умолчанию ставит Seen; есть `--preview`
|
||||
(читает без Seen).
|
||||
2. `himalaya attachment download --help` — **НЕТ** флага против Seen.
|
||||
3. `himalaya message export --full` — ищет по envelope id (sequence number),
|
||||
а не по IMAP UID (UID ≠ envelope id) → для бэкфилла по UID непригоден.
|
||||
4. **Корень (100% подтверждён):** himalaya `message read` и `attachment download`
|
||||
шлют IMAP `BODY[]` (не `BODY.PEEK[]`). По RFC 3501 `BODY[]` автоматически
|
||||
выставляет `\Seen`. Сервер — **Microsoft Exchange** (mail.corpoffice.tech:143,
|
||||
STARTTLS), который это поведение соблюдает железно.
|
||||
5. Живой тест: непрочитанное письмо UID 320 → raw `FETCH 320 BODY.PEEK[]`
|
||||
прочитал 16430 байт, флаги остались `()` (Seen НЕ выставлен).
|
||||
|
||||
**Решение (2 правки в `scripts/mail_archive.py`):**
|
||||
- **Чтение тела** (`get_email_content`): заменить `himalaya message read`
|
||||
на `himalaya message read --preview` (документированный флаг, не ставит Seen).
|
||||
- **Вложения** (`get_attachments` → новый `fetch_attachments_imaplib()`):
|
||||
сырой IMAP на stdlib (socket + ssl), `UID FETCH <uid> (BODY.PEEK[])` —
|
||||
не ставит Seen. Пароль из `~/.config/himalaya/config.toml` (секция auth.raw).
|
||||
|
||||
**Подводные камни, которые вскрылись при реализации:**
|
||||
1. **imaplib не подходит**: `uid('fetch', ...)` возвращал 0 байт на этом
|
||||
Exchange-сервере (странный парсинг литеральных ответов). Решение — сырой
|
||||
socket + свой парсер.
|
||||
2. **Модифицированный UTF-7**: кириллические имена папок (`INBOX/Организация
|
||||
работы`) через raw socket надо отправлять в IMAP modified UTF-7, иначе
|
||||
`SELECT` не находит папку. Написал `_imap_utf7_encode()` (ASCII как есть,
|
||||
`&` → `&-`, не-ASCII сегменты → base64-UTF16BE).
|
||||
3. **Литералы >64 КБ**: сервер отвечает `BODY[] {1534256}` — нужно читать
|
||||
чанками по 64 КБ, пока не наберёшь полный литерал (N байт после `{N}\r\n`).
|
||||
Первая версия падала «literal truncated: got 49152, expected 1534256».
|
||||
4. **MIME-encoded words в имени файла**: `part.get_filename()` возвращал
|
||||
`=?koi8-r?B?...?=` — обязательно `email.header.decode_header()`.
|
||||
5. **Himalaya-фолбэк**: если сырой IMAP упал — himalaya `attachment download`
|
||||
(ставит Seen) + сразу `himalaya flag remove <uid> seen --folder <folder>`
|
||||
(синтаксис: ID и флаги в одном списке).
|
||||
|
||||
**Верификация (живой тест, 2026-09-14):**
|
||||
- UID 14200, INBOX, флаги ДО `()` (непрочитанное), вложение «Переместить
|
||||
стол.docx» (1 117 244 байт): `fetch_attachments_imaplib` → True, файл скачан,
|
||||
флаги ПОСЛЕ `()` — **Seen НЕ выставлен**.
|
||||
- UID 52, папка `INBOX/Организация работы` (кириллица): 4 docx скачаны,
|
||||
флаги не тронуты (mUTF-7 работает).
|
||||
- UID 320 (без вложений): BODY.PEEK[] не меняет флаги.
|
||||
|
||||
**Cron:** mail-archive-every-5min (5f2305b2bbf8) приостанавливался на время
|
||||
отладки → **ВОЗОБНОВЛЁН** (next_run 08:05, state scheduled).
|
||||
вычитка openspec-файлов чейнджа.
|
||||
Reference in New Issue
Block a user