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

Автоматизация и доставка ​

Демона нет. Планирование делегировано планировщику хоста; инструмент предоставляет генераторы и режим --print, чтобы ничего не записывалось без согласия. Один и тот же конвейер отчёта работает из cron, systemd, Task Scheduler или CI-задачи.

См. также: Отчёты и диффы, Источники данных и квоты, Обход и аудит, MCP и навыки агентов.

Установка или предпросмотр расписания ​

bash
lv24-seo schedule install --kind weekly --cron "0 6 * * 1" --config ./seo.toml --print
lv24-seo schedule install --kind daily --cron "0 6 * * *" --config ./seo.toml
lv24-seo schedule print --cron "0 6 * * 1" --target systemd --json

По умолчанию weekly — это 0 6 * * 1, а daily — 0 6 * * *. Генерируемая команда — report weekly --send --json; вид daily добавляет --days 1 --lag-days 0. Абсолютный путь --config и каталог конфигурации как рабочий каталог встраиваются, поэтому нестандартное имя файла, путь с пробелами или несколько конфигов в одной папке продолжают работать.

У schedule install нет флага --target: он определяет бэкенд хоста. schedule print принимает --target cron|systemd|windows и рендерит, не трогая хост. Команды schedule status нет; смотрите планировщик хоста через crontab -l, systemctl --user list-timers или Task Scheduler.

Артефакты ​

ЦельАртефактРасположениеПоведение часового пояса
cronУправляемый блок crontab с CRON_TZ=<zone> и префиксом cdCrontab текущего пользователяCRON_TZ поддерживается Vixie/ISC cron; другие демоны используют время хоста
systemdПользовательские юниты lv24-seo-<kind>.service и .timer~/.config/systemd/user (с учётом XDG)OnCalendar включает настроенную IANA-зону
windowsXML Task Scheduler в UTF-16%APPDATA%\lv24-seo\scheduleStartBoundary несёт разрешённое смещение UTC

Блок cron ограничен строками # >>> lv24-seo:<name> >>> и # <<< lv24-seo:<name> <<<. Повторная установка заменяет все управляемые блоки этой задачи. Повреждённый блок без конечного маркера считается продолжающимся до следующего управляемого маркера или EOF, поэтому schedule install чинит файл, а не добавляет дубль.

Только простые формы cron (M H * * * и M H * * DOW) конвертируются в systemd или Windows. Всё остальное отклоняется, а не переносится молча. Неизвестная или недоступная зона отклоняется до установки.

Результат установки всегда несёт список warnings с ограничениями бэкенда: cron может игнорировать CRON_TZ, а XML Windows кодирует фиксированное смещение, а не IANA-зону, поэтому сезонный переход на летнее время требует проверки. В Windows используется tzdata (входит в Windows). Systemd включает таймер через systemctl --user daemon-reload и systemctl --user enable --now.

Рекомендуемая частота ​

ЗадачаКогдаПочему
report weekly --sendПонедельник, 06:00У данных GSC задержка 2–3 дня; понедельник стабилен
crawl --start URLРаз в неделю, после отчётаДержит доказательства обхода рядом с окном отчёта
auth statusРаз в неделюПодтверждает наличие и источник каждого токена
Полевые данные CrUXРаз в неделюСкользящее окно 28 дней; ежедневный опрос ничего не добавляет

Держите одно расписание на период. Доставка at-least-once, поэтому два пересекающихся расписания могут отправить один отчёт дважды даже с журналом.

Email и Telegram ​

Email использует SMTP на порту 587 с STARTTLS и проверяющим TLS-контекстом. Вход никогда не выполняется после сбоя TLS. Сообщение — multipart/alternative: HTML-шаблон продукта и тот же контент простым текстом в 78 колонок, с локализованной темой не длиннее 78 символов.

Шаблон — карточка 600px с inline CSS, без внешних изображений, таблиц стилей, скриптов, трекинговых пикселей и удалённых шрифтов. Неизвестные KPI отображаются локализованным текстом «Нет данных», а не 0. Metrika показывается как сессии, а не клики. Пустой раздел проблем сообщает, что данных о проблемах нет, вместо заявления, что сайт чист. Ограничения и предупреждения дедуплицируются; текст провайдера остаётся на исходном языке и помечается, когда отчёт локализован. В подвале — id свойства, время генерации в UTC и «generated by lv24-seo».

Telegram отправляет тот же локализованный текст в 78 колонок через метод Bot API sendMessage. Ответы HTTP 4xx/5xx, таймауты и некорректные ответы помечаются как failed и никогда не записываются как отправленные.

Существуют только каналы email и Telegram. Нет адаптеров Resend или Brevo и нет --channel github.

Настройте доставку в seo.toml в разделах [report.email] и [report.telegram]. Пароль SMTP и токен бота Telegram приходят из auth set --provider smtp|telegram, а не из файла конфигурации. --send обязателен; сгенерированное расписание уже его содержит.

Идемпотентность ​

Доставка отчёта идемпотентна через журнал sends, уникальный по (kind, property, period, channel, content_hash). Хеш покрывает заголовок и разделы плюс нормализованную локаль рендера; generated_at и диагностика исключены. Английский не включает локаль в хеш, поэтому обновление не переотправляет уже доставленный английский период.

only_on_change пропускает отправку, если хеш контента уже доставлялся (already_sent). Материальный фильтр пропускает её, если ни одно изменение не достигает report.thresholds (no_material_change). Фильтр работает до хеша локали, поэтому чистое переключение языка может быть подавлено, когда в периоде нет материального изменения.

Доставка — at-least-once на канал. Неоднозначный результат транспорта помечается failed и не записывается, поэтому следующий запуск может доставить дубль. SMTP и Bot API не дают exactly-once после падения процесса. База сериализует попытки доставки и может блокировать других писателей до таймаута транспорта.

Ворота записи ​

Запись к провайдерам отделена от доставки отчёта. CLI использует сухой прогон и явное применение:

  1. Запустите команду записи с --allow-write и установленным LV24_SEO_WRITE_SECRET. Без секрета ворота отказывают с WRITE_DISABLED.
  2. Проверьте план, plan_hash и confirm_token. Токен — это HMAC над действием, целью, хешем плана и сроком действия, валидный 300 секунд.
  3. Примените с теми же флагами и --confirm <token>. Изменённый план, неверный секрет, повреждённый или просроченный токен дают CONFIRM_REQUIRED.

Ворота поддерживают идемпотентную заявку pending-to-sent. При инициализированной рабочей области CLI подключает SQLite-журнал, поэтому сбой провайдера освобождает заявку и повтор разрешён; завершённая или выполняющаяся заявка считается дублем и возвращает skipped. Без каталога данных применение всё равно выполняется, но конверт предупреждает, что защита от повтора отключена на пять минут жизни токена. Dry run не открывает журнал и не создаёт базу. Падение может оставить pending-заявку без TTL в 0.5.2; она считается выполняющейся, пока оператор её не освободит.

В 0.5.2 реально отправляет только IndexNow. Отправка карты сайта GSC и переобход Yandex отказывают с WRITE_DISABLED, а не делают вид, что запись произошла. Инструменты записи MCP требуют allow_write=true; их обработчики в этой сборке тоже не подключены и возвращают WRITE_DISABLED.

GitHub Actions ​

В репозитории продукта есть рецепт недельного workflow: examples/github-actions/seo-weekly.yml. Скопируйте его в репозиторий сайта, а не в репозиторий инструмента.

yaml
on:
  schedule:
    - cron: "0 6 * * 1"
  workflow_dispatch:
permissions:
  contents: read

Рецепт использует стандартный раннер ubuntu-latest, одну задачу, таймаут 15 минут, группу concurrency и короткое хранение артефактов. В публичном репозитории со стандартными раннерами это стоит $0. Приватный репозиторий расходует включённые или оплачиваемые минуты Actions и хранилище; большие раннеры, матрицы и платные дополнения выходят за рамки пути с нулевыми расходами.

Задача восстанавливает data/seo.sqlite3 (плюс -wal и -shm) через actions/cache, создаёт каталог данных и запускает:

bash
uvx --from lv24-seo lv24-seo report weekly --send --json

Учётные данные провайдеров и доставки берутся из секретов репозитория. Несекретные идентификаторы остаются в закоммиченном seo.toml. Cron GitHub всегда в UTC, поэтому целевое локальное время нужно пересчитать явно.

Часовой пояс ​

report.timezone — это IANA-зона (по умолчанию Europe/Minsk). Она управляет окнами отчёта и stats, окнами MCP по умолчанию и локальными границами дня. Данные провайдеров остаются привязанными к датам и не конвертируются.

Бэкенды расписаний применяют зону там, где могут, и сообщают предупреждение, где не могут. Неверная зона отклоняется при валидации конфигурации с CONFIG_INVALID; она никогда не подменяется молча. Проверяйте задачи Windows после перехода на летнее время и убедитесь, что установленный cron-демон поддерживает CRON_TZ, прежде чем на него полагаться.

Source: _source/engine-scheduling.md, _source/engine-cli.md, _source/engine-config.md.