Перейти к содержимому

Доставка и эскалация

Push-уведомление доходит мгновенно — но только если телефон под рукой. Доставка (вкладка Доставка в настройках канала) решает вторую задачу: что делать, если сообщение никто не прочитал.

Для каждого канала можно включить дополнительные способы доставки. Работают они как эскалация: сообщение уходит по такому каналу, только если владелец не прочитал его за заданное время.

POST /message ──▶ push всем устройствам (мгновенно)
│
└─ раз в минуту: воркер доставки
├─ сообщение прочитано? → ничего не делаем
└─ прошло >= таймаута метода? → email / SMS / звонок /
Slack / Telegram / webhook /
Discord / Teams / Mattermost /
PagerDuty / Opsgenie
МетодЧто настраиваетсяПриходит
Emailадрес получателя, таймаутписьмо со ссылкой на сообщение и кнопками действий
СМСномер в формате +79001234567, таймауткороткий текст с названием канала и заголовком
Звонокномер, таймаутголосовой звонок с уведомлением
Slackincoming webhook URL, таймаут, мин. приоритетсообщение в канал Slack
Telegrambot token, chat ID, таймаут, мин. приоритетсообщение от бота (с inline-кнопками)
WebhookURL, необязательный секрет, таймаут, мин. приоритетPOST с JSON-телом
Discordwebhook URL, таймаут, мин. приоритеткарточка (embed) со ссылками действий
TeamsURL workflow (Workflows), таймаут, мин. приоритетAdaptive Card с кнопками действий
Mattermostincoming webhook URL, таймаут, мин. приоритетсообщение с цветной полосой и ссылками
PagerDutyIntegration Key, регион, реакция на прочтениеинцидент (Events API v2)
OpsgenieAPI key, регион, реакция на прочтениеалерт (Alerts API)

У каждого метода свой таймаут доставки (readTimeoutSec):

  • 0 — досылка на ближайшем прогоне воркера (то есть примерно в течение минуты);
  • 300 — если сообщение не прочитано 5 минут;
  • максимум — 23 часа (82800).

Отсчёт идёт от момента создания сообщения, а не от момента доставки push. Сообщение считается прочитанным, когда вы открыли его в админке или мобильном приложении, либо когда сработал один из endpoint-ов POST /message/read / POST /application/{id}/message/read, либо когда вы нажали кнопку ✓ Прочитано в письме, в Telegram или в другой интеграции. В Android-приложении то же делает кнопка «Прочитано» прямо в уведомлении и кнопка подтверждения на экране режима будильника.

В момент включения метода сервер запоминает отметку времени. Сообщения, созданные раньше этого момента, по новому методу не досылаются — иначе включение email-а вечером обернулось бы письмом обо всём, что накопилось за сутки.

У всех интеграций (Slack, Telegram, webhook, Discord, Teams, Mattermost, PagerDuty, Opsgenie) есть поле мин. приоритет: сообщения с приоритетом ниже указанного через этот адаптер не уходят. Удобно, чтобы в рабочий чат попадали только важные события, а вся мелочь оставалась в приложении.

У email, SMS и звонка фильтра по приоритету нет — они управляются только переключателем и таймаутом.

Внутри одного прогона сообщения группируются по паре «владелец + адрес»:

  • email — одно сообщение приходит обычным письмом, несколько — одним письмом-дайджестом со списком;
  • СМС — одно СМС на номер: заголовок и краткий список каналов и тем;
  • звонок — один звонок на номер, независимо от числа сообщений.

Интеграции (Slack, Telegram, webhook, Discord, Teams, Mattermost, PagerDuty, Opsgenie) не группируются: одно сообщение — одна отправка.

МетодПоведение при сбое
email и интеграцииИдемпотентны: отметка о доставке ставится после отправки, при сбое следующий прогон повторит
СМС, звонокОтметка ставится до отправки (дубль недопустим), при ошибке снимается и попытка повторяется

Каждое сообщение уходит по каждому методу ровно один раз: факт доставки фиксируется в журнале и повторно не срабатывает.

В письмо и в сообщение Telegram-бота добавляются подписанные ссылки:

  • ✓ Прочитано — отмечает сообщение прочитанным и останавливает досылку по остальным методам;
  • варианты ответа — если сообщение содержит закрытый вопрос (Ask), кнопки соответствуют вариантам ответа.

Те же действия получают и остальные интеграции: в Slack и Teams — кнопками, в Discord и Mattermost — ссылками под текстом, в PagerDuty — ссылками инцидента (links), в Opsgenie — строками описания.

Ссылки подписаны HMAC и действуют ограниченное время (для вопросов — до истечения срока ответа), поэтому авторизация в интерфейсе для них не нужна.

Чат-интеграции получают сообщение карточкой с цветом по приоритету (красный — 9–10, оранжевый — 7–8, жёлтый — 4–6, синий — 1–3), заголовком, текстом и подписью Notifly · канал · P<приоритет>.

СервисКуда слатьКак выглядят действия
Discordwebhook канала: https://discord.com/api/webhooks/<id>/<token>markdown-ссылки под текстом
TeamsURL workflow «Post to a channel when a webhook request is received» (Workflows / Power Automate)кнопки Action.OpenUrl Adaptive Card
Mattermostincoming webhook: https://<сервер>/hooks/<key>markdown-ссылки под текстом
  • Discord. Настройки канала Discord → Интеграции → Вебхуки → Новый вебхук → Копировать URL. Принимаются только хосты Discord (discord.com, discordapp.com). Упоминания @everyone/@here из текста сообщения не срабатывают (allowed_mentions пуст).
  • Microsoft Teams. В канале Teams: … → Workflows → шаблон Post to a channel when a webhook request is received → выберите команду и канал → скопируйте URL. Сообщение приходит Adaptive Card (версия 1.4). Старые Office 365 connectors (*.webhook.office.com) Microsoft выводит из эксплуатации — такие URL отклоняются при сохранении с подсказкой перейти на Workflows.
  • Mattermost. Integrations → Incoming Webhooks → Add Incoming Webhook. Формат Slack-совместимый (attachment). Массовые упоминания @channel, @all, @here из текста нейтрализуются.

Incident-интеграции создают инцидент/алерт, а не просто пишут в чат.

Приоритет NotiflyPagerDuty severityOpsgenie priority
9–10criticalP1
7–8errorP2
4–6warningP3
1–3infoP4
0infoP5
  • PagerDuty — Events API v2. В сервисе PagerDuty: Integrations → Add integration → Events API V2, скопируйте Integration Key (32 символа). Для аккаунтов в EU-регионе выберите регион eu (events.eu.pagerduty.com). dedup_key = notifly-<id сообщения>: повторная отправка того же сообщения не создаст второй инцидент. Кнопки действий приходят в links инцидента, текст — в custom_details.message.
  • Opsgenie — Alerts API (POST /v2/alerts, заголовок Authorization: GenieKey …). В команде Opsgenie: Integrations → Add integration → API, скопируйте API key; регион us (api.opsgenie.com) или eu (api.eu.opsgenie.com). alias = notifly-<id сообщения> — Opsgenie дедуплицирует открытые алерты по нему. Ссылки действий — в описании и в details.

Поле «Когда сообщение прочитано» (onRead) замыкает цикл: когда сообщение прочитано в Notifly — в приложении, админке или по ссылке ✓ Прочитано прямо из инцидента, — воркер доставки в течение минуты отправляет в PagerDuty acknowledge или resolve, в Opsgenie — acknowledge или close (по тому же dedup_key/alias). По умолчанию ("") инцидент не трогается.

Реакция выполняется не более одного раза на сообщение: неудачная попытка (после встроенных повторов) не повторяется бесконечно. Обратного направления нет: acknowledge в PagerDuty не отмечает сообщение прочитанным в Notifly.

Generic-webhook получает POST с JSON:

{
"title": "CPU high",
"message": "load average 12.4",
"priority": 8,
"channel": "Prod",
"appId": 12345,
"ts": 1782561600000
}

Если задан секрет, тело подписывается HMAC-SHA256 и подпись передаётся в заголовке:

X-Notifly-Signature: sha256=<hex>

Проверка на стороне получателя — в SDK она уже написана.

Python:

import notifly
raw = request.get_data()
if not notifly.verify_signature(SECRET, raw, request.headers.get("X-Notifly-Signature")):
abort(401)

Go — подпакет webhook даёт готовый обработчик, который сам проверяет подпись, разбирает тело и отвечает нужными кодами:

import "github.com/truboter/notifly.ru/sdk/go/webhook"
http.Handle("/notifly/alerts", webhook.DeliveryHandler(secret,
func(ctx context.Context, p *webhook.DeliveryPayload) error {
log.Printf("не прочитано в %s: %s", p.ChannelName, p.Title)
return nil // вернуть ошибку = попросить Notifly повторить
},
&webhook.Options{Dedup: webhook.NewDedup(time.Hour)}))

Без SDK это семь строк на любом языке: hmac_sha256(secret, raw_body) в hex, с префиксом sha256=, сравнить в постоянном времени.

Настройки канала → вкладка Доставка. Включите нужный метод переключателем, укажите адрес и таймаут. Кнопка Проверить рядом с методом сохраняет настройки и сразу шлёт туда тестовое уведомление.

Подсказка по Telegram: создайте бота через @BotFather (/newbot) — он выдаст bot token; узнайте Chat ID (для личных сообщений — у @userinfobot, для группы это ID вида -100…); обязательно нажмите Start у своего бота или добавьте его в группу, иначе Telegram запретит отправку.

Настройки доставки — часть объекта канала, поле deliveryConfig в POST /application и PUT /application/{id}:

Окно терминала
curl -X PUT "$NOTIFLY_URL/application/12345" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{
"name": "Prod",
"deliveryConfig": {
"email": { "enabled": true, "to": "oncall@example.com", "readTimeoutSec": 300 },
"telegram": { "enabled": true, "botToken": "123:ABC", "chatId": "-1001234567890",
"readTimeoutSec": 900, "minPriority": 7 },
"webhook": { "enabled": true, "url": "https://example.com/hook",
"secret": "s3cret", "readTimeoutSec": 0, "minPriority": 5 },
"pagerduty": { "enabled": true, "routingKey": "R0123456789abcdef0123456789abcde",
"region": "eu", "onRead": "resolve",
"readTimeoutSec": 600, "minPriority": 8 }
}
}'

Поля адаптеров:

ПолеКто используетЗначение
enabledвсевключён ли метод
toemail, sms, callадрес или номер получателя (+79001234567)
urlslack, webhook, discord, teams, mattermostURL вебхука / произвольный URL
botToken, chatIdtelegramпараметры бота
secretwebhookключ подписи HMAC-SHA256 (опционально)
readTimeoutSecвсетаймаут досылки, 0…82800
routingKeypagerdutyIntegration Key (Events API v2)
apiKeyopsgenieключ API-интеграции
regionpagerduty, opsgenieus (по умолчанию) или eu
onReadpagerduty, opsgenie"", acknowledge или resolve
minPriorityвсе интеграциине слать сообщения с приоритетом ниже
Окно терминала
curl -X POST "$NOTIFLY_URL/application/12345/delivery/test" \
-H "Content-Type: application/json" \
-H "X-Notifly-Key: <client-token>" \
-d '{"adapter": "telegram"}'

Без тела проверяются все включённые методы канала. Ответ — результат по каждому:

{"results": {"telegram": {"ok": false, "error": "telegram: 403 bot was blocked by the user"}}}

Если ни один метод не включён (или указанный адаптер выключен), вернётся 400 no enabled adapter to test. Проверка идёт в обход эскалации: ждать таймаута не нужно, уведомление уходит сразу.

Досылка не расходует дневную квоту: событие уже списано при создании сообщения (message). Сколько бы методов доставки ни было включено, счётчик событий от этого не растёт — см. Квоты и тарифы.

  • URL-адреса Slack, webhook, Discord, Teams и Mattermost проверяются защитой от SSRF: приватные, loopback и link-local адреса на момент отправки блокируются.
  • Секреты — URL вебхуков Slack, Discord, Teams и Mattermost (в них зашит токен), botToken, secret, routingKey, apiKey — в ответах API заменяются маской ********; маска при сохранении означает «не менять». Они хранятся в настройках канала и доступны только владельцу канала: получателю делегированного доступа вкладка «Доставка» недоступна.
  • Управление доставкой требует API-токена владельца канала; app-токен канала сюда не подходит.

Эскалацию отменяют — насовсем, без утреннего «догона» — два механизма:

  • Тихие часы владельца канала: пока окно тишины активно, методы для сообщений ниже порога срочности не отправляются; вместо них приходит одна сводка.
  • Окна обслуживания: сообщения в каналах охвата не эскалируются вовсе, независимо от приоритета.

Подавленный метод записывается в журнал доставки как «подавлен» и больше не повторяется.

Проверьте по порядку:

  1. Сообщение не прочитано? Прочтение — в админке, в приложении или кнопкой «Прочитано» в уведомлении — отменяет эскалацию.
  2. Прошёл ли таймаут метода (см. Таймаут). Сообщения, созданные до включения метода, не досылаются.
  3. Приоритет не ниже минимального (minPriority) — у интеграций, где он задан (см. Минимальный приоритет).
  4. Не действуют ли тихие часы или окно обслуживания — они подавляют эскалацию насовсем.
  5. Работает ли сам метод — запустите тест доставки: он отправит уведомление сразу и вернёт ошибку адаптера, если что-то не так. SMS и звонок срабатывают, только если интеграция включена на стороне сервера.

Если не приходит само push-уведомление на устройство, см. «Не приходят уведомления».