67 lines
4.5 KiB
Markdown
67 lines
4.5 KiB
Markdown
# email-handlers Specification
|
||
|
||
## Purpose
|
||
Подключение обработчиков по тегам классификации письма: уведомление в мессенджер
|
||
для срочных писем, создание задачи в Radicale (VTODO, календарь «Задачи») для
|
||
писем с задачей, создание события в Radicale (VEVENT, календарь «Рабочий») для
|
||
писем со встречей. Обработчики запускаются автоматически после классификации и
|
||
работают идемпотентно. (Vikunja выведена из эксплуатации 2026-09-13 — change
|
||
`remove-vikunja-use-radicale-tasks`.)
|
||
|
||
## Requirements
|
||
|
||
### Requirement: Уведомление в мессенджер для срочных писем
|
||
|
||
Письмо с тегом `urgent` MUST вызывать отправку уведомления в мессенджер
|
||
(Telegram) с отправителем, темой и первыми строками текста.
|
||
|
||
#### Scenario: Срочное письмо
|
||
- **WHEN** `email_classifier.py` пометил письмо тегом `urgent`
|
||
- **THEN** `email_handlers.py` отправляет в Telegram уведомление с from/subject/превью
|
||
|
||
### Requirement: Создание задачи в Radicale (VTODO) для писем с задачей
|
||
|
||
Письмо с тегом `task` MUST создавать задачу в Radicale (CalDAV, календарь
|
||
«Задачи») как VTODO с темой письма в SUMMARY и ссылкой на письмо в DESCRIPTION.
|
||
|
||
#### Scenario: Письмо с задачей
|
||
- **WHEN** `email_classifier.py` пометил письмо тегом `task`
|
||
- **THEN** в Radicale (календарь Задачи) создаётся VTODO: SUMMARY=тема письма, DESCRIPTION=ссылка на `email.md`
|
||
|
||
### Requirement: Создание события в Radicale для писем со встречей
|
||
|
||
Письмо с тегом `meeting` MUST создавать событие в календаре Radicale (Рабочий)
|
||
с темой письма как SUMMARY и извлечённой датой/временем, если они указаны.
|
||
|
||
#### Scenario: Письмо со встречей
|
||
- **WHEN** `email_classifier.py` пометил письмо тегом `meeting` и в классификации есть дата/время
|
||
- **THEN** в Radicale (календарь Рабочий) создаётся VEVENT с SUMMARY=тема письма
|
||
|
||
### Requirement: Идемпотентность обработчиков
|
||
|
||
Обработчик MUST запускаться для каждого письма один раз; повторный запуск на
|
||
уже обработанном письме MUST NOT создавать дубликат задачи/события/уведомления.
|
||
|
||
#### Scenario: Повторный запуск обработчиков
|
||
- **WHEN** `email_handlers.py` запущен повторно на письме, для которого уже созданы задача/событие
|
||
- **THEN** дубликаты не создаются (трекинг обработанных в state)
|
||
|
||
### Requirement: Информационные письма не создают обработчиков
|
||
|
||
Письмо с тегом `info` MUST NOT вызывать уведомления, задач или событий; оно
|
||
только помечается тегом в frontmatter.
|
||
|
||
#### Scenario: Информационное письмо
|
||
- **WHEN** `email_classifier.py` пометил письмо тегом `info`
|
||
- **THEN** `email_handlers.py` не создаёт ни уведомления, ни задачи, ни события
|
||
|
||
### Requirement: Уведомление о недоступности обработчика
|
||
|
||
Если обработчик не может выполниться (Radicale недоступен, нет учётных данных),
|
||
MUST быть записана ошибка в лог, и письмо MUST остаться помеченным тегом для
|
||
повторной попытки (не теряться).
|
||
|
||
#### Scenario: Radicale недоступен
|
||
- **WHEN** `email_handlers.py` пытается создать задачу/событие, но Radicale недоступен
|
||
- **THEN** ошибка пишется в лог, письмо остаётся с тегом `task`/`meeting`, повторная попытка возможна
|