# ✨ Отправлять готовый текст через apprise и ntfy - **Тип:** feature - **Категория:** Очередь - **Зачем:** Пользователь веба узнаёт о готовности только опросом с открытого экрана. - **Теги:** goal:ready-notification Двигает все пять пунктов «Завершения» цели: готовый текст и отказ доходят до пользователя веба без открытого приложения, адрес канала свой у каждого, а отказ канала задачу не роняет. Сегодня `completeJob` и `failJob` отвечают только источнику `telegram`; источник `api` не получает ничего. Здесь появляется второй способ доставки, и выбор между ними обязан остаться в одной точке — той же, что сейчас. ## Затрагивает - `internal/contract` — интерфейс отправителя уведомления; - `internal/service`, `completeJob` и `failJob` — выбор канала по источнику задачи; - новый адаптер поверх apprise либо прямого HTTP к ntfy; - адрес канала у учётной записи: колонка и её миграция, экран настройки; - секция конфигурации: адрес сервера ntfy, способ вызова apprise; - `docs/architecture.md` — новая внешняя зависимость и чем она отказывает; - `docs/security.md` — текст расшифровки уходит на внешний сервис; - `docs/database.md` — настройки числами. ## Критерии приёмки - Задача из веба, дошедшая до `done`, отправляет текст в канал владельца. Оракул — тест с подставным отправителем: вызов ровно один, с текстом задачи и адресом владельца. - Задача из Telegram по-прежнему отвечает ботом и вторым каналом не дублируется. Оракул — тот же тест на источнике `telegram`: подставной отправитель ntfy не вызван. - Недоступный канал уведомлений не мешает задаче завершиться: состояние `done` и текст в приложении остаются. Оракул — тест с отправителем, возвращающим ошибку: задача в `done`, текст на месте, в логе одна запись уровня `WARN`. - Отказ задачи доходит тем же каналом и тем же человекочитаемым текстом, что видит пользователь Telegram. Оракул — тест на ветке `failJob`. ## Рамки Web Push с VAPID и своим хранением подписок не делаем. Своего сервера ntfy не поднимаем — адрес приходит конфигом. Текст расшифровки уходит на внешний сервис, и это сдвиг периметра: строка в `docs/security.md` обязательна.