Как написать бот для телеграма на Bash?

Stepan Mikulov

Administrator
Команда форума
Для начала, получим репозиторий со скриптами:

git clone https://github.com/rshekhovtsov/msms.git

Переходим в папку msms и далее работаем в ней.

Если telegram заблокирован, используйте прокси. Самый простой и надежный вариант — torsocks:

sudo apt install tor
sudo apt install torsocks

В качестве примера настроим в три шага мониторинг стартовой страницы google.com

ШАГ 1. Создаем бота в telegram и получаем id пользователя​


  • В строке поиска контактов в telegram ищем @botfather:

    mf8rtxrovcs-4r8ax2pn7fuxusc.png

  • Запускаем его кнопкой Start, вводим команду /newbot и отвечаем на вопросы. Нужно иметь ввиду, что name — это имя бота, которое будет отображаться пользователям, а username — уникален и должен завершаться на «bot»:

    gcq5fxjht5xytwso83degwhmslm.png


    Среди прочего, бот выдаст секретный токен для HTTP API, который нужно скопировать и сохранить в файл telegram-api-key.txt в папке msms.
  • Набираем в строке поиска telegram имя нашего бота, и запускаем его.
  • В качестве завершающего штриха, добавим себя в список получателей оповещений мониторинга:

    sudo chmod +x ./recipients-setup.sh
    torsocks ./recipients-setup.sh

    Скрипт выведет список последних обращений к боту, там должна быть одна строка с нашими id и именем в telegram. Берём этот id и сохраняем его в файле services/google-recipients.txt. Формат файла: каждая строка — один id. Пример:

    123456789
    987654321

Чтобы добавить нового получателя, нужно попросить его стартовать бота в telegram, запустить recipients-setup.sh и добавить id в файл.

ШАГ 2. Настраиваем мониторинг​


Описание сервиса происходит путём создания ini-файла в папке serviсes. Нужно задать пять параметров:

  1. MSMS_SERVICE_NAME: наименование сервиса — будет использоваться в оповещениях и журнале мониторинга.
  2. MSMS_SERVICE_ENDPOINT: endpoint сервиса, на который мы будем обращаться curl'ом.
  3. MSMS_CURL_PARAMS: дополнительные параметры curl, см. пример ниже.
  4. MSMS_EXPECTED: ожидаемый ответ сервиса. Используется, если ответ короткий.
  5. MSMS_EXPECTED_FILE: имя файла с ожидаемым ответом сервиса. Если указан, перезаписывает MSMS_EXPECTED.
  6. MSMS_RECIPIENTS: файл со списком получателей оповещений.

Запрос на google.com возвращает фиксированный html c редиректом, будем использовать его как ожидаемый ответ сервера:

curl google.com > services/google-response.html

Создадим файл services/google.ini:

MSMS_SERVICE_NAME='google front page'

# service endpoint
MSMS_SERVICE_ENDPOINT='google.com'

# curl parameters
MSMS_CURL_PARAMS='-s --connect-timeout 3 -m 7'

# expected service response
MSMS_EXPECTED_FILE='google-response.html'

# recipients list file
MSMS_RECIPIENTS='google-recipients.txt'

В MSMS_CURL_PARAMS можно задать всё, что умеет curl, в том числе:

  1. Отключить сообщения curl, чтобы не засорять консоль и лог: -s
  2. Задать таймаут соединения с проверяемым сервисом (в секундах): --connect-timeout 3
  3. Задать таймаут получения ответа: -m 7
  4. Отключить проверку сертификата для SSL (например, если используется самоподписанный сертификат): --insecure
  5. Указать тип http-запроса: -X POST
  6. Указать заголовки: -H "Content-Type: application/json"
  7. Указать тело запроса в виде строки или файла. Пример для файла: -d @request.json

Мы отключили оповещения и задали таймауты 3 сек. на соединение и 7 сек. на получение ответа сервиса.

Внимание: указывайте значения параметров в одинарных кавычках, как в примере. К сожалению, bash в этом смысле довольно хрупок, и случайно залетевшая бабочка не там поставленная кавычка может привести к гибели вселенной сложно диагностируемым ошибкам.

Мы настроили мониторинг. Проверим, что все ОК:

sudo chmod +x ./monitoring.sh
torsocks ./monitoring.sh

Скрипт должен вывести сообщение вида:

2020-01-10 12:14:31
health-check "google front page": OK
 

Stepan Mikulov

Administrator
Команда форума

ШАГ 3. Настраиваем расписание​


Настроим расписание мониторинга в cron:

sudo crontab -e

Добавим строку для ежеминутной проверки google.com:

*/1 * * * * torsocks <ПУТЬ К ПАПКЕ РЕПОЗИТОРИЯ>/monitoring.sh >> <ПУТЬ К ПАПКЕ РЕПОЗИТОРИЯ>/monitoring.log 2>&1

Добавим каждый день в 11.00 оповещение, подтверждающее работоспособность самого мониторинга. Для этого передадим в скрипт параметр DAILY:

0 11 * * * torsocks <ПУТЬ К ПАПКЕ РЕПОЗИТОРИЯ>/monitoring.sh DAILY >> <ПУТЬ К ПАПКЕ РЕПОЗИТОРИЯ>/monitoring.log 2>&1


2>&1 — стандартный прием, перенаправляющий ошибки в основной поток вывода. В итоге, они тоже попадут в журнал мониторинга.

Сохраним изменения и подхватим их командой:

sudo service cron reload

Подробнее о настройке cron можно почитать, например, здесь.

Таким образом, каждую минуту будет запускаться скрипт мониторинга, который будет через curl обращаться на google.com. Если полученный ответ отличается от ожидаемого, скрипт разошлёт по списку получателей оповещение в telegram. Журнал проверок ведётся в файле monitoring.log

Если нужно добавить еще один сервис, мы просто создаем новый ini-файл для него в папке services и, при необходимости, формируем отдельный список получателей. Всё остальное отработает автоматически.

Если проверяемый сервис стал недоступен, оповещение будет приходить ежеминутно. Если не получается быстро восстановить сервис, можно временно отключить уведомления в свойствах бота в telegram.

Теперь давайте подробно разберем дополнительные возможности и реализацию скриптов.

Шаблоны сообщений и эмоциональное вовлечение​


Имя бота и аватарка задаются через @botfather.
Шаблоны сообщений находятся в папке templates:

  • curl-fail.txt: сообщение, отправляемое в случае, когда curl вернул ненулевой код ошибки. Обычно говорит о невозможности достучаться до сервиса.
  • daily.txt: ежедневное сообщение, подтверждающее, что мониторинг сервиса работает.
  • service-fail.txt: сообщение, отправляемое в случае, когда ответ сервиса отличается от ожидаемого.

Разберем возможности кастомизации на примере встроенных шаблонов сообщений.
В шаблонах используются эмодзи. К сожалению, habr их не отображает.
Для подбора эмодзи удобно использовать поиск на emojipedia.org:


Подходящий символ просто копируете и вставляете в текст шаблона (это обычный unicode).


Скрипт мониторинга​


monitoring.sh делает простой auto-discovery — берет все ini-файлы из папки services и для каждого выполняет основной скрипт с логикой проверки и рассылки оповещений:

#!/bin/bash
cd $(dirname "$0")/services

for service_ini in $(ls *.ini); do
bash ../msms.sh "$1" "$service_ini"
done

Для формирования ежесуточного сообщения о статусе мониторинга скрипту можно передать параметр DAILY.

Обратите внимание, что при старте скрипта текущая папка меняется на services. Это позволяет в ini-файлах указывать пути к файлам относительно services.

Скрипт проверки и рассылки оповещений​


msms.sh содержит основную логику проверки сервиса и рассылки оповещений.

Работа с telegram:

# telegram endpoint
TG_API_URL="https://api.telegram.org/bot$(cat ../telegram-api-key.txt)/sendMessage"

#################################################################
# send message to telegram
# parameter: message text
#################################################################
function send_message {
for chat_id in $(cat ../$MSMS_RECIPIENTS); do
curl -s -X POST --connect-timeout 10 $TG_API_URL -d chat_id=$chat_id -d parse_mode="Markdown" -d text="$1"
echo
done
}


Мы формируем URL для доступа к REST API telegram, используя сохраненный в файле секретный ключ.

Функция send_message использует curl для отправки сообщений на этот REST API, забирая id получателей из файла, указанного нами в ini. В отправляемых данных мы указываем, что используем разметку сообщений: parse_mode="Markdown".

Выведем текущие дату-время и загрузим ini-файл.

echo $(date '+%Y-%m-%d %H:%M:%S')

# load variables from .ini file:
. $2


Магическая строка . $2 выполняет переданный на вход вторым параметром ini-файл как обычный скрипт, занося заданные в нём значения в переменные окружения.

Загрузим ожидаемый ответ из файла, если задан параметр MSMS_EXPECTED_FILE:

if [ -n "$MSMS_EXPECTED_FILE" ]; then
MSMS_EXPECTED="$(cat "$MSMS_EXPECTED_FILE")"
fi


Выполним проверку сервиса с отправкой оповещений, если нужно:

RESPONSE="$(eval curl $MSMS_CURL_PARAMS \"$MSMS_SERVICE_ENDPOINT\")"
EXIT_CODE=$?
if [[ $EXIT_CODE != 0 ]]; then
echo health-check \"$MSMS_SERVICE_NAME\" FAILED: CURL EXIT WITH $EXIT_CODE
MESSAGE="$(cat ../templates/curl-fail.txt)"
MESSAGE=$(eval echo $MESSAGE)
send_message "$MESSAGE"
elif [[ "$RESPONSE" != "$MSMS_EXPECTED" ]]; then
echo health-check \"$MSMS_SERVICE_NAME\" FAILED: "$RESPONSE"
MESSAGE="$(cat ../templates/service-fail.txt)"
MESSAGE=$(eval echo $MESSAGE)
send_message "$MESSAGE"
else
echo health-check \"$MSMS_SERVICE_NAME\": OK
fi


Сначала присваиваем переменной RESPONSE результат выполнения команды curl для данного сервиса.

Выражение EXIT_CODE=$? кладет в переменную результат выполнения последней команды, т.е. curl. При необходимости отправки оповещения, считывается шаблон из соответствующего файла и осуществляется рассылка на получателей с помощью send_message.

Последний блок обрабатывает параметр DAILY:

if test "$1" = "DAILY"; then
echo health-check \"$MSMS_SERVICE_NAME\" DAILY
MESSAGE="$(cat ../templates/daily.txt)"
MESSAGE=$(eval echo $MESSAGE)
send_message "$MESSAGE"
fi

Он отправляется сообщение, подтверждающее работоспособность самого мониторинга.

Получение списка id пользователей​


recipients-setup.sh обращается к API telegram для получения последних сообщений, адресованных боту:

curl -s https://api.telegram.org/bot$(cat telegram-api-key.txt)/getUpdates \
| python recipients-setup.py

Здесь используется магия python для красивого вывода списка. Это необязательно, можно просто взять нужный id из json, который выведет команда:

torsocks curl -s https://api.telegram.org/bot$(cat telegram-api-key.txt)/getUpdates
 
Сверху