## Purpose Подключение обработчиков по тегам классификации письма: уведомление в мессенджер для срочных писем, создание задачи в Vikunja для писем с задачей, создание события в Radicale для писем со встречей. Обработчики запускаются автоматически после классификации и работают идемпотентно. ## ADDED 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`, повторная попытка возможна