Мониторинг изменений страницы (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 | Подстрока по регулярному выражению из текста ответа | regex; если пуст — ведёт себя как hash |
css | Текст первого элемента по CSS-селектору | обязателен — валидный CSS-селектор |
xpath | Текст первого узла по XPath-выражению | обязателен — валидное XPath-выражение |
Логика проверки одинакова для всех режимов: извлекаем значение → сравниваем с
сохранённым lastValue → если отличается, отправляем alert и запоминаем новое
значение. Первая проверка просто запоминает текущее значение и алерт не шлёт.
Несколько правил в одном мониторе
Заголовок раздела «Несколько правил в одном мониторе»Вместо одиночного 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.
Подбор и проверка селектора
Заголовок раздела «Подбор и проверка селектора»Прежде чем создавать монитор, удобно подобрать и проверить правило «на лету» — для этого есть два эндпоинта, не требующие создания монитора.
Проверить правило — 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: <client-token>" \ -d '{ "url": "https://example.com/product/42", "useBrowser": false, "mode": "css", "selector": ".product-price" }'# → {"ok":true,"value":"1 299 ₽"}Ответ: ok (булево), value (извлечённое значение, усечено до 200 символов),
error (текст ошибки, если правило не сработало) и note (диагностика, например
о деградации браузерного режима до GET).
Подобрать селектор нейросетью — POST /content-monitor/suggest-selector
Заголовок раздела «Подобрать селектор нейросетью — POST /content-monitor/suggest-selector»Notifly загружает страницу один раз, сжимает HTML и одним запросом к LLM подбирает
режим и селектор под ваше описание. Результат всегда проверяется на реальном
содержимом страницы, поэтому в ответе гарантированно валидный sampleValue:
curl -X POST "$NOTIFLY_URL/content-monitor/suggest-selector" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "url": "https://example.com/product/42", "useBrowser": false, "prompt": "следить за ценой товара" }'В ответе: mode, selector, label, sampleValue (текущее значение на странице),
reason (почему так), confidence (high/medium/low), alternatives (другие
кандидаты) и fallback (true, если LLM недоступен и селектор подобран эвристикой).
Поле prompt — до 500 символов. Запрос вида «следить за всей страницей» сразу
вернёт режим hash без обращения к LLM.
Создание монитора
Заголовок раздела «Создание монитора»Через админку
Заголовок раздела «Через админку»- Откройте app.notifly.ru → Мониторы → Контент.
- Укажите URL, выберите режим (или нажмите «Подобрать селектор» и опишите словами,
за чем следить), при необходимости включите
useBrowser. - Кнопкой «Проверить» убедитесь, что извлекается ровно то значение, которое нужно.
- Задайте период, текст alert и приоритет — и сохраните.
Через 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: <client-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 изменилась!", "alertPriority": 7 }'Множественный режим (несколько правил на одной странице):
curl -X POST "$NOTIFLY_URL/content-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-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": "Изменилось содержимое карточки товара.", "alertPriority": 5 }'Мониторинг поля в JSON API:
curl -X POST "$NOTIFLY_URL/content-monitor" \ -H "Content-Type: application/json" \ -H "X-Notifly-Key: <client-token>" \ -d '{ "appid": 12345, "name": "Курс из API", "url": "https://api.example.com/rates", "mode": "json", "selector": "$.usd.value", "intervalSec": 600, "alertMessage": "Курс USD изменился." }'Поля запроса
Заголовок раздела «Поля запроса»| Поле | Тип | Обязательно | Описание / ограничения |
|---|---|---|---|
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 | нет | Трактовать текст alert как Markdown |
alertPriority | int | нет | Приоритет уведомления, 0–10 |
* Нужно задать либо rules (множественный режим), либо mode+selector
(одиночный). Если rules непуст — mode/selector игнорируются.
Жизненный цикл и статусы
Заголовок раздела «Жизненный цикл и статусы»Новый монитор создаётся со статусом pending и проверяется почти сразу
(nextCheckAt = «сейчас»). После изменения «значимых» полей (URL, режим, селектор,
правила, useBrowser, период, таймаут) проверка перепланируется на «сейчас»,
косметические правки (название, заголовок/текст alert, приоритет) расписание не сдвигают.
POST /content-monitor/:id/pause— приостановить (проверки не выполняются;nextCheckAtотодвигается далеко в будущее).POST /content-monitor/:id/resume— возобновить (статус сноваpending, проверка планируется на «сейчас»).
В записи монитора хранятся lastValue (последнее извлечённое значение),
lastError (последняя ошибка загрузки/извлечения), lastCheckAt, nextCheckAt
и alertedAt.
Ограничения
Заголовок раздела «Ограничения»- Период проверки: 60–86400 секунд (от 1 минуты до 24 часов).
- Таймаут загрузки: по умолчанию 15 с, максимум 60 с.
- До 20 правил на один монитор.
- Приоритет alert: 0–10; длина
name— 1–200,alertMessage— 1–2000 символов. - Запросы идут из облака, поэтому целевой URL должен быть доступен из публичного интернета.
- Браузерный режим (
useBrowser) работает, только если на сервере настроен соответствующий контейнер.
REST API
Заголовок раздела «REST API»Все эндпоинты требуют клиентский токен (или MCP-код с правом записи для изменяющих операций); список доступен и в режиме чтения. Подробнее о токенах — в Первый вход.
| Метод | Путь | Описание |
|---|---|---|
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 | Возобновить проверки |
См. также: Активные мониторы, HTTP-мониторы, Workflow-мониторы, Browser-workflow.