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:
+38
@@ -0,0 +1,38 @@
|
||||
# email-attachments Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Скачивание вложений не помечает письмо прочитанным
|
||||
|
||||
Скачивание вложений MUST NOT выставлять IMAP-флаг `\Seen` (письмо не должно
|
||||
становиться «прочитанным» в почтовом ящике).
|
||||
|
||||
Способ: `himalaya attachment download` ставит `\Seen` (использует `BODY[]`), и
|
||||
флага `--preview` у него нет. Поэтому `get_attachments()` MUST использовать
|
||||
сырой IMAP-запрос `BODY.PEEK[]` через stdlib `imaplib` (не ставит `\Seen` на
|
||||
Microsoft Exchange, проверено) и распаковку MIME через stdlib `email`.
|
||||
|
||||
#### Scenario: Скачивание вложения у непрочитанного письма
|
||||
- **GIVEN** письмо в INBOX с флагами `()` (непрочитанное)
|
||||
- **WHEN** `get_attachments()` скачивает его вложения
|
||||
- **THEN** файлы вложений сохранены в `attachments/`, а флаги письма на IMAP
|
||||
остаются `()` (флаг `\Seen` не выставлен)
|
||||
|
||||
#### Scenario: Фолбэк при сбое сырого IMAP
|
||||
- **WHEN** `fetch_attachments_imaplib()` не может получить письмо (ошибка IMAP)
|
||||
- **THEN** вложения скачиваются через `himalaya attachment download`, после чего
|
||||
флаг `\Seen` снимается через `himalaya flag remove` (письмо временно
|
||||
помечается, но восстанавливается) ИЛИ операция помечается как недоступная —
|
||||
письмо НЕ остаётся прочитанным навсегда
|
||||
|
||||
### Requirement: Пароль IMAP для скачивания вложений
|
||||
|
||||
`fetch_attachments_imaplib()` MUST брать учётные данные IMAP (host, port, login,
|
||||
пароль) из конфига himalaya (`~/.config/himalaya/config.toml`, секция
|
||||
`[accounts.<default>]`, `backend.*`, пароль — `backend.auth.raw`) и MUST NOT
|
||||
логировать или выводить пароль.
|
||||
|
||||
#### Scenario: Доступ к конфигу
|
||||
- **WHEN** `get_attachments()` запускается для письма
|
||||
- **THEN** подключение к IMAP выполняется с учётными данными из конфига
|
||||
himalaya, пароль никуда не выводится
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# email-storage-format Specification
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Архивация не помечает письмо прочитанным
|
||||
|
||||
Чтение тела письма при архивации MUST NOT выставлять IMAP-флаг `\Seen`.
|
||||
`mail_archive.py` для получения тела использует `himalaya message read` с
|
||||
флагом `--preview` (документировано: читает БЕЗ установки `\Seen`).
|
||||
|
||||
#### Scenario: Архивация непрочитанного письма
|
||||
- **GIVEN** письмо в INBOX с флагами `()` (непрочитанное)
|
||||
- **WHEN** `mail_archive.py` архивирует письмо (пишет `email.md`)
|
||||
- **THEN** в frontmatter `email.md` флаг `Seen` отсутствует И на IMAP флаги
|
||||
письма остаются `()`
|
||||
|
||||
#### Scenario: Флаги в frontmatter соответствуют IMAP
|
||||
- **GIVEN** письмо заархивировано после фикса
|
||||
- **WHEN** флаги письма на IMAP меняются (пользователь прочитал/ответил)
|
||||
- **THEN** frontmatter `email.md` после следующей синхронизации отражает флаги
|
||||
IMAP (Seen появляется только если письмо реально прочитано пользователем)
|
||||
Reference in New Issue
Block a user