Автоматизация и доставка
Демона нет. Планирование делегировано планировщику хоста; инструмент предоставляет генераторы и режим --print, чтобы ничего не записывалось без согласия. Один и тот же конвейер отчёта работает из cron, systemd, Task Scheduler или CI-задачи.
См. также: Отчёты и диффы, Источники данных и квоты, Обход и аудит, MCP и навыки агентов.
Установка или предпросмотр расписания
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> и префиксом cd | Crontab текущего пользователя | CRON_TZ поддерживается Vixie/ISC cron; другие демоны используют время хоста |
systemd | Пользовательские юниты lv24-seo-<kind>.service и .timer | ~/.config/systemd/user (с учётом XDG) | OnCalendar включает настроенную IANA-зону |
windows | XML Task Scheduler в UTF-16 | %APPDATA%\lv24-seo\schedule | StartBoundary несёт разрешённое смещение 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 использует сухой прогон и явное применение:
- Запустите команду записи с
--allow-writeи установленнымLV24_SEO_WRITE_SECRET. Без секрета ворота отказывают сWRITE_DISABLED. - Проверьте план,
plan_hashиconfirm_token. Токен — это HMAC над действием, целью, хешем плана и сроком действия, валидный 300 секунд. - Примените с теми же флагами и
--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. Скопируйте его в репозиторий сайта, а не в репозиторий инструмента.
on:
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:
permissions:
contents: readРецепт использует стандартный раннер ubuntu-latest, одну задачу, таймаут 15 минут, группу concurrency и короткое хранение артефактов. В публичном репозитории со стандартными раннерами это стоит $0. Приватный репозиторий расходует включённые или оплачиваемые минуты Actions и хранилище; большие раннеры, матрицы и платные дополнения выходят за рамки пути с нулевыми расходами.
Задача восстанавливает data/seo.sqlite3 (плюс -wal и -shm) через actions/cache, создаёт каталог данных и запускает:
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.