В данной статье расскажу, как я настроил автоматизацию обычных повседневных задач. Эти задачи варьируются от предварительной записи в прачечную до уведомлений о тренировках в местном тренажерном зале CrossFit.
Главное внимание уделим общей настройке, процессу разработки и добавления задач, при этом не будем углубляться в специфику автоматизации каждой задачи. В основе настройки: Raspberry Pi для выполнения скриптов задач, Pushover для отправки нативных уведомлений на телефон и Puppeteer для взаимодействия с веб-сайтами и извлечения с них данных.
Типы задач для автоматизации
В данном случае все автоматизируемые задачи делятся на 2 общие категории: запланированные и непрерывные.
Обе категории задач предполагают одинаковый алгоритм действий: вы заходите на сайт (возможно, с авторизацией), извлекаете нужную информацию и выполняете какое-либо действие, например нажимаете на кнопку.
К запланированным относятся все задачи, которые выполняются через определенные интервалы времени. Например, каждое утро я просматриваю содержание тренировки в CrossFit, выпадающей на текущий день, или каждый воскресный вечер бронирую место в прачечной на следующую неделю. Это те самые задачи, которые легко забываются, но которые надо делать. Кроме того, в большинстве случаев для их выполнения приходится взаимодействовать с медленными и громоздкими сайтам.
Непрерывные задачи более сложные, но при этом скучные. Они подразумевают постоянное проведение определенной проверки и выполнение действия по ее результатам. Именно этим мне пришлось заниматься, когда я сдавал на права. Возможность записаться на экзамен по вождению предоставлялась только через 3 месяца. Однако время от времени люди отменяли свои записи и появлялись новые даты, доступные для бронирования. Мне приходилось несколько раз в день мониторить сайт, чтобы выяснить, освободилось ли место на ближайшие пару недель.
Общая настройка
Для каждой задачи я написал скрипт, который выполняет всю необходимую логику. Как правило, пишу их, используя Node.js (простой JavaScript или TypeScript) и библиотеку Puppeteer. Этот фреймворк для автоматизации взаимодействий пользователя с веб-сайтами отлично подходит для решения поставленной задачи.
Фактическая процедура написания каждого скрипта зависит от сайта, но общий принцип всегда один и тот же. Применяю инструменты разработчика в Chrome/Brave, вручную взаимодействую со страницей и выполняю задачу, подлежащую автоматизации. При каждом взаимодействии отслеживаю происходящее в DOM
и переношу это в скрипт, задействуя соответствующие методы Puppeteer. В большинстве случаев сделать это не намного сложнее, чем найти нужный HTML
-элемент для взаимодействия или просмотреть DOM
в поисках определенной строки текста. Углубленное изучение принципов работы с Puppeteer для извлечения данных с сайтов выходит за рамки данной статьи.
Разработка скриптов для каждой задачи выполняется на MacBook, а запуск их в продакшн осуществляется с помощью старенького Raspberry Pi 3 2016 года. По современным меркам он уже немного устарел, но его вычислительной мощности хватает для выполнения требуемых задач. Готовый скрипт я переношу на Pi либо через scp
, либо получаю доступ к Pi напрямую через SSH
. После этого связываюсь с репозиторием GitHub, куда был отправлен скрипт с MacBook.
Настройка автоматизации скриптов
Если скрипт соответствует запланированной задаче, тогда я планирую его с помощью cron
: выполняю $ crontab -e
, редактирую файл, в который добавляется новый скрипт, и указываю время и день выполнения.
# Пример файла crontab для скрипта
# бронирования места в прачечной. В данном случае
# скрипт запускается в 21:05 каждое воскресенье посредством cron.
# m h dom mon dow command
5 21 * * 7 node /home/pi/bookLaundry.js
С непрерывными задачами дело обстоит немного сложнее. Как правило, они выполняются каждые две минуты и требуют отслеживания состояния между запусками.
Например, чтобы проверить, освободилась ли новая дата записи, необходимо знать, какие варианты были доступны при предыдущем запуске. Вместо того чтобы управлять состоянием в каком-либо постоянном хранилище, например в файле или базе данных, я обычно пишу задачи так, чтобы управление состоянием происходило во время выполнения.
Рассмотрим наглядный пример. Ниже представлена упрощенная версия скрипта, проверяющего наличие новых доступных записей на экзамен по вождению:
// `getAvailableAppointments()` возвращает
// массив записей.
let appointments = getAvailableAppointments();
while (true) {
delay(300); // Ожидает 5 минут
const currentAppointments = getAvailableAppointments();
// `setDiff(A, B)` возвращает разность множеств A-B
const newAppointments = setDiff(currentAppointments, appointments);
if (newAppointments.length > 0) {
notifyAboutNewAppointments(newAppointments);
}
appointments = currentAppointments;
}
Сохранение необходимого состояния между запусками посредством переменной appointments
сильно упрощает большую часть требуемой логики. Нужно лишь получить текущее состояние и проверить разницу по сравнению с предыдущим.
Как насчет обработки ошибок? Чаще всего заключаю всю логику в try-catch
и отправляю сообщение об ошибке в виде уведомления. Не самый изящный способ, зато простой и действенный.
Запускаю скрипты для непрерывных задач командой screen
. $ screen -S <name-of-session>
создает сеанс виртуального терминала, где выполняется скрипт. Выполнение одного из этих скриптов в виртуальном терминале с помощью screen
позволяет отключить SSH
-соединение с Pi и при этом продолжать работу запущенных процессов.
Чтобы остановить выполнение скрипта, нужно подключиться к Pi по SSH
, выполнить команду $ screen -R <name-of-session>
для присоединения к сеансу и отключить его сочетанием клавиш Ctrl+Z
.
Отправка уведомлений на телефон с помощью Pushover
Ключевой аспект всех скриптов заключается в том, что они должны уведомлять о результатах выполнения своих задач. Например, скрипт, проверяющий наличие новых доступных дат записи на экзамен по вождению, должен уведомлять при появлении новых дат. Аналогичным образом скрипт, ответственный за бронирование места в прачечной, должен уведомлять, получилось ли у него это сделать. Кроме того, уведомления должны поступать в случае сбоя в работе скриптов по каким-либо причинам.
Какое-то время я рассматривал возможность использования электронной почты для доставки уведомлений: либо посредством curl
, запускающего сервер SMTP
для личного адреса Gmail
, либо путем установки mailutils
и применения соответствующей команды email
.
Однако после тщательного изучения вопроса решил воспользоваться Pushover (предоставляется 30-дневный бесплатный пробный период и единовременная плата в 5$ при продолжении работы с сервисом).
С настройкой все просто. Устанавливаем приложение Pushover на устройство iPhone или Android, которое служит клиентом, получающим отправляемые уведомления. После создания аккаунта на сайте Pushover регистрируем приложение.
Создав приложение, выбираем имя и иконку, которые будут отображаться на экране телефона при получении уведомлений. После этого отправляем уведомления на телефон посредством простого запроса POST
:
const sendNotice = async (msg, prio = 0) => {
const data = {
message: msg,
token: PUSHOVER_TOKEN,
user: PUSHOVER_USER,
html: 1,
priority: prio,
};
const url = "https://api.pushover.net/1/messages.json"
await axios.post(url, data);
};
На самом базовом уровне потребуются только 3 параметра: токен приложения PUSHOVER_TOKEN
, токен пользователя PUSHOVER_USER
и отправляемое сообщение. В данном случае я включил дополнительные необязательные параметры: отправка сообщений в формате HTML
и приоритет priority
, определяющий способ отображения сообщения на клиенте.
Теперь, когда потребуется отправить информацию, например об успешном или неудачном выполнении задачи, можно в любом скрипте использовать метод sendNotice
. А поскольку сообщение отправляется в формате HTML
, то можно включить активные гиперссылки и форматирование, а именно размер и цвет текста.
Рассмотрим пример. Запускаем скрипт для получения содержания сегодняшней тренировки в зале Crossfit, и на телефон приходят следующие уведомления:
Длительное нажатие на каждое уведомление открывает все сообщение целиком, что очень удобно. Как видно, я назвал приложение AutoButler
и добавил иконку жилета для эффекта персонализации.
Заключение
Вы можете улучшить разные аспекты, чтобы добиться более надежной и изящной настройки. Представленный подход соответствует моим требованиям и предполагает минимальное сопровождение. Что касается общей стоимости, то она составляет 45$ (40$ за Raspberry Pi, 5$ за Pushover). Эксплуатационные расходы близки к нулю, так как Raspberry Pi потребляет очень мало электричества.
При необходимости скорректировать расписание запуска скрипта приходится подключаться к Raspberry по SSH
и выполнять команду crontab -e
. Однако такая необходимость возникает нечасто, а если и возникает, то вопрос решается за пару минут. Весь процесс автоматизации новой задачи, от написания скрипта до передачи его в Raspberry Pi и “развертывания”, занимает около 30 минут (“плюс-минус” в зависимости от сложности базового сайта). Учитывая все выше сказанное, текущая настройка является для меня оптимальным вариантом.
Читайте также:
- VS Code Remote-SSH для удаленной разработки
- 20 продвинутых проектов для освоения сложных концепций программирования
- Мониторинг сайта: просто, но эффективно
Читайте нас в Telegram, VK и Дзен
Перевод статьи Marko Cotra: How I Use a Raspberry Pi, Pushover, and Puppeteer to Streamline Mundane Tasks