Мониторинг изменений страницы (Content monitor)
Контент-монитор следит не за доступностью ресурса, а за содержимым: Notifly раз в N секунд загружает заданный URL, извлекает из ответа интересующее значение и сравнивает его с предыдущим. Как только значение изменилось — приходит alert. Это активный монитор (звонит сам Notifly), но он отвечает на вопрос «что изменилось на странице», а не «жив ли сайт».
Типичные задачи:
- Цена — отслеживать цену товара на странице магазина или у конкурента.
- Статус / наличие — «в наличии» ↔ «нет в наличии», статус заказа, статус сервиса.
- Контент конкурента — появилась новая запись в блоге, изменился прайс, обновился changelog.
- JSON API — значение поля в публичном JSON-ответе (курс, остаток, версия).
Режимы извлечения
Заголовок раздела «Режимы извлечения»Что именно сравнивать, задаёт режим (mode). Поддерживаются пять режимов:
mode | Что извлекается | Поле selector |
|---|---|---|
hash | SHA-256 всего тела ответа — alert при любом изменении страницы | не нужен |
json | Значение по JSONPath из JSON-ответа | обязателен — JSONPath, например $.price или $.items.0.status |
text | Первое совпадение регулярного выражения (синтаксис Go) в тексте ответа | regex; если пуст — ведёт себя как hash |
css | Текст первого элемента по CSS-селектору | обязателен — валидный CSS-селектор |
xpath | Текст первого узла по XPath-выражению (можно и атрибут: //a/@href) | обязателен — валидное XPath-выражение |
Логика проверки одинакова для всех режимов: извлекаем значение → сравниваем со
снимком предыдущей проверки → если отличается, отправляем alert и запоминаем новое
значение. Первая проверка просто запоминает текущее значение и алерт не шлёт.
Пробелы в тексте элементов (css/xpath) схлопываются, так что переносы строк
в вёрстке ложных изменений не дают.
Если значение извлечь не удалось (селектор ничего не нашёл, элемент пустой,
JSONPath отсутствует, regex не совпал) — это не «изменение», а ошибка проверки:
монитор уходит в down (см. Уведомления).
hash реагирует на любой изменившийся байт — включая счётчики, токены и время в
разметке. Для «живых» страниц надёжнее следить за конкретным фрагментом через
css/xpath.
Несколько правил в одном мониторе
Заголовок раздела «Несколько правил в одном мониторе»Вместо одиночного mode+selector можно задать массив правил (rules) —
до 20 штук на один монитор. Каждое правило — это { "mode", "selector", "label" },
где label — необязательное человекочитаемое имя (оно же попадает в текст алерта).
Alert срабатывает, если изменилось хотя бы одно правило. Это удобно, когда на
одной странице нужно следить сразу за несколькими значениями (например, цена +
наличие + рейтинг).
"rules": [ { "mode": "css", "selector": ".product-price", "label": "Цена" }, { "mode": "css", "selector": ".availability", "label": "Наличие" }, { "mode": "text", "selector": "Скидка\\s+\\d+%", "label": "Скидка" }]Если rules задан, поля верхнего уровня mode/selector игнорируются. Если rules
пуст — работает одиночный режим (mode+selector), который остаётся для обратной
совместимости.
Снимок хранится по каждому правилу отдельно: перестановка правил ложных алертов не вызывает, а новое или изменённое правило на первой проверке только запоминает своё значение.
JavaScript-страницы: useBrowser
Заголовок раздела «JavaScript-страницы: useBrowser»По умолчанию страница загружается обычным HTTP-GET. Если нужное значение
подгружается скриптами уже в браузере (SPA, ленивая подгрузка цены и т.п.),
включите флаг useBrowser: true — тогда страница рендерится в headless
Chromium, и значения извлекаются из готового DOM после выполнения JS (text —
по видимому тексту страницы, hash — по отрендеренному HTML).
Если проверка в браузерном режиме упала, Notifly снимает скриншот страницы
(когда на сервере настроено хранилище скриншотов) и прикладывает его к алерту об
ошибке: ключ приходит в extras сообщения под именем notifly::screenshot,
картинка отдаётся по GET /browser-shot/<key> и показывается превью в карточке
сообщения в админке. Алерты об изменениях по умолчанию уходят без скриншота.
Кадры, не попавшие в алерт, не хранятся; доступ — только у владельца монитора;
хранятся в пределах квоты хранилища тарифа и
удаляются вместе с монитором.
Уведомления
Заголовок раздела «Уведомления»| Событие | Когда | Текст |
|---|---|---|
| Изменение | Значение хотя бы одного правила отличается от снимка предыдущей проверки — при каждом изменении | alertMessage + список изменений «было → стало» (значения усечены до 100 символов) |
| Ошибка проверки | Страница не загрузилась (сеть, таймаут, HTTP-код ≥ 400) или правило не извлекло значение; один раз на падение | Ошибка проверки: <причина> |
| Проверка восстановлена | Первая успешная проверка после down — только если об ошибке был отправлен alert | ✅ Проверка восстановлена + какая была ошибка |
Все три уходят с заголовком alertTitle и приоритетом alertPriority. Текст
изменения собирается в Markdown, поэтому включайте alertMessageMarkdown: true
(админка делает это сама). Пример алерта:
Цена товара #42 изменилась!
🔔 Обнаружено изменение
**Цена**
было: ~~1 299 ₽~~
стало: **1 199 ₽**Если у правила нет label, в тексте будет «правило N».
Статусы монитора: pending (создан или возобновлён), up, down (последняя
проверка — ошибка) и paused. Порога «N неудач подряд» у контент-монитора нет:
в down уводит первая же ошибка.
- Без спама. Об ошибке монитор сообщает один раз: момент алерта хранится в
alertedAt, и пока монитор вdown, повторных сообщений нет. Re-arm: если черезPUT /content-monitor/:idизменить параметры проверки (URL, режим, селектор, правила,useBrowser, период, таймаут), отметка сбрасывается и проверка запускается сразу — если ошибка сохраняется, придёт свежий alert. - Таймаут сбрасывает снимок. После ошибки-таймаута первая успешная проверка запоминает значения заново и об изменении не сообщает. При остальных ошибках снимок сохраняется, и изменение, случившееся за время сбоя, будет замечено.
- Сбои инфраструктуры Notifly (например, недоступен браузерный сервис) — не вина сайта: алерта нет, статус не меняется, квота возвращается, проверка повторяется через 2 минуты.
- Окна обслуживания: в окне проверки идут и пишутся в историю, но алерт об ошибке откладывается до конца окна, а найденные изменения не сообщаются и не принимаются за новый снимок — после окна страница сравнивается со снимком до работ. Изменение, пережившее окно, будет замечено; временная заглушка «ведутся работы» — нет.
- Тихие часы подавляют эскалацию во внешние каналы для
сообщений с приоритетом ниже
breakthroughPriority(по умолчанию 8). - Статус-страница канала: переход в
down(ошибка проверки, не изменение) открывает авто-инцидент, восстановление — закрывает. - Каждая проверка — одно событие дневной квоты; при исчерпании проверки откладываются до следующих суток.
Подбор и проверка селектора
Заголовок раздела «Подбор и проверка селектора»Прежде чем создавать монитор, удобно подобрать и проверить правило «на лету» — для этого есть два эндпоинта, не требующие создания монитора. Оба загружают страницу так же, как живой монитор, и применяют правило тем же кодом. Загруженная страница кэшируется на 90 секунд: повторные тесты по тому же URL её не перезагружают.
Проверить правило — POST /content-monitor/test-rule
Заголовок раздела «Проверить правило — POST /content-monitor/test-rule»Загружает страницу и применяет одно правило, возвращая извлечённое значение или понятную ошибку:
curl -X POST "$NOTIFLY_URL/content-monitor/test-rule" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "url": "https://example.com/product/42", "useBrowser": false, "mode": "css", "selector": ".product-price" }'# → {"ok":true,"value":"1 299 ₽"}Ответ (всегда 200): ok (булево), value (извлечённое значение, усечено до
200 символов; для hash — короткий отпечаток страницы), error (текст ошибки,
если страница не загрузилась или правило не сработало) и note (диагностика,
например о деградации браузерного режима до GET).
Подобрать селектор нейросетью — POST /content-monitor/suggest-selector
Заголовок раздела «Подобрать селектор нейросетью — POST /content-monitor/suggest-selector»Notifly загружает страницу, и нейросеть по вашему описанию определяет, что
отслеживать, и находит текущее значение на странице; по этому значению сервер
сам строит устойчивый CSS/XPath-селектор, а если не вышло — нейросеть синтезирует
селектор по фрагменту страницы (с проверкой и повторами). Для JSON-ответа
подбирается режим json. Результат всегда проверяется на реальном содержимом
страницы, поэтому sampleValue в ответе — настоящее текущее значение:
curl -X POST "$NOTIFLY_URL/content-monitor/suggest-selector" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "url": "https://example.com/product/42", "useBrowser": false, "prompt": "следить за ценой товара" }'В ответе: mode, selector, label, sampleValue (текущее значение на странице),
reason (почему так), confidence (high/medium/low, если есть оценка),
alternatives (другие кандидаты с их sampleValue), fallback (true, если
селектор подобран эвристикой — заголовок h1/title или хэш страницы — без
нейросети или после её неудачи) и note. Поле prompt — до 500 символов. Запрос
вида «следить за всей страницей» сразу вернёт режим hash без обращения к нейросети.
Если страница не загрузилась, ответ — 502.
Создание монитора
Заголовок раздела «Создание монитора»Через админку
Заголовок раздела «Через админку»- Откройте app.notifly.ru → Мониторы → Создать → «Контент страницы изменился» (или вкладка Контент → «Добавить монитор контента»).
- Укажите URL, при необходимости включите «Браузерный режим». В блоке «Подбор селектора ИИ» опишите словами, за чем следить, нажмите «Сгенерировать селектор» и «Добавить в правила» — или задайте правила вручную.
- Кнопкой «Проверить» у правила убедитесь, что извлекается ровно то значение, которое нужно.
- Задайте период, таймаут, канал и текст алерта (поддерживается Markdown) — и сохраните.
Заголовок и приоритет алерта форма не настраивает (приоритет 5) — задайте их через REST API или MCP. Учтите, что сохранение монитора из формы вернёт приоритет к 5.
Через REST API — POST /content-monitor
Заголовок раздела «Через REST API — POST /content-monitor»Одиночный режим (следим за одним CSS-значением):
curl -X POST "$NOTIFLY_URL/content-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "appid": 12345, "name": "Цена товара #42", "url": "https://example.com/product/42", "mode": "css", "selector": ".product-price", "useBrowser": false, "intervalSec": 3600, "timeoutSec": 15, "alertMessage": "Цена товара #42 изменилась!", "alertMessageMarkdown": true, "alertPriority": 7 }'Множественный режим (несколько правил на одной странице):
curl -X POST "$NOTIFLY_URL/content-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "appid": 12345, "name": "Карточка товара", "url": "https://example.com/product/42", "rules": [ { "mode": "css", "selector": ".product-price", "label": "Цена" }, { "mode": "css", "selector": ".availability", "label": "Наличие" } ], "intervalSec": 3600, "alertMessage": "Изменилось содержимое карточки товара.", "alertMessageMarkdown": true, "alertPriority": 5 }'Мониторинг поля в JSON API:
curl -X POST "$NOTIFLY_URL/content-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <api-token>" \ -d '{ "appid": 12345, "name": "Курс из API", "url": "https://api.example.com/rates", "mode": "json", "selector": "$.usd.value", "intervalSec": 600, "alertMessage": "Курс USD изменился.", "alertMessageMarkdown": true }'Поля запроса
Заголовок раздела «Поля запроса»| Поле | Тип | Обязательно | Описание / ограничения |
|---|---|---|---|
appid | uint | да | ID канала, куда слать alert |
name | string | да | Название монитора, 1–200 символов |
url | string | да | Валидный http(s)-URL на публичный адрес |
mode | string | да* | Одиночный режим: hash / json / text / css / xpath |
selector | string | зависит | JSONPath для json, regex для text, CSS для css, XPath для xpath; для json/css/xpath обязателен |
rules | array | да* | Массив правил {mode, selector, label}, до 20; альтернатива mode+selector |
useBrowser | bool | нет | true — рендеринг через headless Chromium |
intervalSec | uint | да | Период проверки, 60–86400 секунд |
timeoutSec | uint | нет | Таймаут загрузки, по умолчанию 15, максимум 60 |
alertMessage | string | да | Текст уведомления об изменении, 1–2000 символов |
alertTitle | string | нет | Заголовок всех уведомлений монитора |
alertMessageMarkdown | bool | нет | Отображать уведомления как Markdown (рекомендуется true) |
alertPriority | int | нет | Приоритет уведомлений, 0–10 |
* Нужно задать либо rules (множественный режим), либо mode+selector
(одиночный). Если rules непуст — mode/selector игнорируются. CSS-селекторы,
XPath-выражения и regex проверяются на синтаксис при сохранении (400 при ошибке).
Жизненный цикл
Заголовок раздела «Жизненный цикл»Новый монитор создаётся со статусом pending и проверяется почти сразу
(nextCheckAt = «сейчас»). После изменения параметров проверки (URL, режим,
селектор, правила, useBrowser, период, таймаут) проверка перепланируется на
«сейчас»; правка названия, заголовка/текста alert и приоритета расписание не сдвигает.
PUT /content-monitor/:id принимает тот же набор полей целиком (кроме appid).
POST /content-monitor/:id/pause— приостановить (проверки не выполняются;nextCheckAtотодвигается далеко в будущее).POST /content-monitor/:id/resume— возобновить (статус сноваpending, проверка планируется на «сейчас»).
В записи монитора хранятся lastValue (снимок последней успешной проверки —
JSON-объект со значениями правил), lastError (последняя ошибка загрузки/извлечения),
lastCheckAt, nextCheckAt и alertedAt. История проверок с извлечёнными
значениями и найденными изменениями — GET /monitor-history/content_monitor/:id
(см. Активные мониторы) и вкладка «История» в
окне редактирования монитора.
Ограничения
Заголовок раздела «Ограничения»- Период проверки: 60–86400 секунд (от 1 минуты до 24 часов).
- Таймаут загрузки: по умолчанию 15 с, максимум 60 с.
- До 20 правил на один монитор.
- Приоритет alert: 0–10; длина
name— 1–200,alertMessage— 1–2000 символов. - Страница загружается
GET-запросом без своих заголовков и тела; читается до 1 МБ ответа. HTTP-код ≥ 400 — ошибка проверки. - Запросы идут из облака: URL должен быть доступен из публичного интернета, адреса приватных сетей, loopback и link-local отвергаются.
- Браузерный режим (
useBrowser) работает, только если на сервере настроен браузерный сервис.
REST API
Заголовок раздела «REST API»Авторизация — заголовок X-Notifly-Key с API-токеном (или
токеном устройства). Создание, изменение, удаление, пауза и возобновление требуют
у API-токена доступа write; для списка, test-rule и suggest-selector
достаточно read. Токен, ограниченный каналами, видит и меняет только мониторы
этих каналов. Share-токены каналов к управлению мониторами не допускаются (403).
| Метод | Путь | Описание |
|---|---|---|
GET | /content-monitor | Список контент-мониторов пользователя |
POST | /content-monitor | Создать монитор |
POST | /content-monitor/suggest-selector | Подобрать режим + селектор нейросетью по описанию |
POST | /content-monitor/test-rule | Применить одно правило к странице (dry-run), вернуть значение или ошибку |
PUT | /content-monitor/:id | Обновить настройки (нельзя сменить appid) |
DELETE | /content-monitor/:id | Удалить монитор (вместе со скриншотами) |
POST | /content-monitor/:id/pause | Приостановить проверки |
POST | /content-monitor/:id/resume | Возобновить проверки |
GET | /browser-shot/<key> | Скриншот из extras алерта (только владельцу монитора) |
Через MCP контент-мониторы тоже можно создавать и проверять правила словами — например, «следи за ценой на странице https://example.com/product/42 раз в час».
См. также: Активные мониторы, HTTP-мониторы, Workflow-мониторы, Browser-workflow.