Declaration of VAR

and some other stuff

Бот для Telegram

2015-10-24 19:28:59 +0300

2015-10-24 19:28:59 +0300 | Comments

В Telegram с 24 июня 2015 появились боты, а точнее Telegram Bot API. То есть, теперь мою прошлую задачу получения статуса сервера можно решить гораздо удобнее.

This article in english 🇺🇸.

Бот не является “пользовательским аккаунтом”, ему не нужен номер телефона, и самое главное - не нужно ставить клиентское приложение для Telegram на сервере или где вы хотите его использовать. Бот для Telegram - это ну как бы и есть API - веб-запросы к серверам Telegram. Механизм обработки запросов и отправки ответов лежит на вас, как на владельце бота.

Официальная документация по ботам здесь: https://core.telegram.org/bots

Создание бота

Нужно написать самому главному боту - BotFather:

…и уважительно попросить создать нового бота:

После этого бот будет создан. Обратите внимание на токен - по нему осуществляется доступ к боту, так что светить этот токен нельзя, иначе кто угодно сможет получить доступ к сообщениям бота (перехватывать запросы и отправлять свои ответы).

Сейчас, когда у вас уже создан бот и получен токен, я могу на примере продемонстрировать, что конкретно имел в виду под словами “это ну как бы и есть API”. Откройте браузер и перейдите по такому адресу:

https://api.telegram.org/botТУТВАШТОКЕН/getMe

В браузере отобразится примерно такое:

{"ok":true,"result":{"id":ТУТБУДЕТID,"first_name":"someTestBot","username":"someTestBot"}}`

Для большей наглядности, вот скриншот браузера:

Вот что такое бот в Telegram. Вы отправляете веб-запрос и получаете на него JSON-ответ.

Разумеется, нам нужно автоматизировать как отправку запросов, так и разбор JSON-ответов, потому нужна программная реализация бота, которая будет работать на каком-нибудь сервере (можно и на домашнем компе, но тогда, очевидно, бот будет доступен только когда будет включен компьютер). И для этой цели можно выбрать абсолютно любой язык программирования, поддерживающий отправку веб-запросов, ведь, как я уже сказал и продемонстировал - работа с ботом это всего лишь отправка веб-запросов и разбор ответов.

Реализация бота

Я выбрал язык Python. Выбор далеко не случаен. Дело в том, что для Python уже есть готовая библиотека, существенно облегчающая реализацию бота - это pyTelegramBotAPI. Собственно, в ней сделано уже всё, что требуется для отправки веб-запросов и получения ответов, вам остаётся только описать команды, которые ваш бот будет уметь выполнять. Однако, какое-то понимание всё же потребуется, потому настоятельно рекомендую изучить документацию по API: https://core.telegram.org/bots/api

Итак, вам нужно поставить Python и какую-нибудь IDE (хотя, конечно, можно и в Блокноте). Вот что у меня:

Итак, у вас установлен Python и pip, ставим pyTelegramBotAPI:

pip install pyTelegramBotAPI

Теперь создаём файл someTestBot.py, в котором и будет описан функционал бота. Я определил две обязательных команды /start и /help, а также команду отправки статуса сервера /server (забегая вперёд, она предназначена для запуска в Linux, так что при тестировании на Windows её вызов зафейлится):

import telebot # подключение библиотеки pyTelegramBotAPI
import logging # библиотека журнала

# для запуска скриптов
from subprocess import call

# настройки для журнала
logger = logging.getLogger('log')
logger.setLevel(logging.INFO)
fh = logging.FileHandler('someTestBot.log')
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s | %(levelname)-7s | %(message)s")
fh.setFormatter(formatter)
logger.addHandler(fh)

# создание бота с его токеном API
bot = telebot.TeleBot("ТУТВАШТОКЕНОТBOTFATHER")

# текст справки
help_string = []
help_string.append("Это простой *тестовый бот*, созданный в обучающих целях.\n\n")
help_string.append("/start - выводит приветствие;\n")
help_string.append("/help - отображает эту справку;\n")
help_string.append("/server - присылает статус сервера.")

# --- команды

@bot.message_handler(commands=['start'])
def send_start(message):
    # отправка простого сообщения
    bot.send_message(message.chat.id, "Привет, я тестовый бот! Отправьте мне /help для вывод справки.")

@bot.message_handler(commands=['help'])
def send_help(message):
    # отправка сообщения с поддержкой разметки Markdown
    bot.send_message(message.chat.id, "".join(help_string), parse_mode="Markdown")

@bot.message_handler(commands=['server'])
def send_server(message):
    try:
        # по этому пути на сервере лежит скрипт сбора информации по статусу сервера
        call(["/root/scrps/status.sh"])
        # читает файл с результатами выполнения скрипта
        status = open("/root/scrps/status.txt", "rb").read()
        bot.send_message(message.chat.id, status, parse_mode="Markdown")
    except Exception as e:
        logger.exception(str(e))
        bot.send_message(message.chat.id, "Ошибка при получении статуса сервера. Подробности в журнале.")

# запуск приёма сообщений
bot.polling()

Скрипт сбора информации о статусе сервера status.sh я взял из прошлой статьи и немного изменил:

#!/bin/bash

scrps_path=/root/scrps

info_web="*Web-servers*
----------------------
$(service apache2 status | sed -r "s/[*]+/-/g")
$(service nginx status | sed -r "s/[*]+/-/g")

"

info_mysql="*MySQL*
----------------------
$(mysqladmin ping)
$(mysqladmin status)

"

info_cpu="*CPU*
----------------------
$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1" of 100 percents"}')

"

info_ram="*RAM*
----------------------
free: $(free -m | grep Mem | awk '{print $4}') MB of $(free -m | grep Mem | awk '{print $2}') MB total

"

info_space="*HDD*
----------------------
$(df -h --output=source,size,used,avail | head -n2)
"

text=$(printf "$info_web$info_mysql$info_cpu$info_ram$info_space")
printf '%s\n' "$text" > ${scrps_path}/status.txt

Его надо поместить на сервере по пути /root/scrps/status.sh, либо каком другом, но тогда надо не забыть изменить его и в исходниках бота.

На этом реализация бота закончена.

Возвращаемся к BotFather, указываем ему команды, которые знает бот:

Размещение и запуск бота на сервере

Ну, во-первых, вы уже можете запустить бота у себя на компьютере - нужно открыть командную строку в том каталоге, где лежит someTestBot.py, и выполнить python someTestBot.py. Если это Windows, появится “чёрное окно”, которое будет означать, что бот уже работает. Найдите в Telegram пользователя @someTestBot (у вас своё имя, очевидно), и можно уже с ним переписываться:

Как я и говорил, команду /server в Windows он обработать не смог, и пойманное исключение записалось в журнал.

Кстати, обратите внимание - приложение показывает подсказки о всех командах бота, которые мы добавили через BotFather:

Теперь задача, как сделать так, чтобы бот работал постоянно. Идём на сервер с Linux, создаём каталог /usr/local/bin/someTestBot/, кладём в него наш someTestBot.py. Кстати, на сервере тоже должен быть установлен pyTelegramBotAPI (разумеется, Python и pip тоже).

Я расскажу о двух способах, как запустить скрипт в качестве сервиса: Supervisor и systemd.

Supervisor

Устанавливаем Supervisor:

sudo apt-get install supervisor

Создаём конфиг для процесса (/etc/supervisor/conf.d/someTestBot.conf):

[program:someTestBot]
directory=/usr/local/bin/someTestBot
command=python someTestBot.py
autostart=true
autorestart=true

С таким конфигом, надо полагать, бот будет “выполняться” с root правами, что некруто. Скорее всего, можно задать, с какими правами это будет работать. Ну да ладно, нам же надо скорее в продакшн, так что запускаем:

supervisorctl update
supervisorctl restart someTestBot

systemd

А можно ничего и не устанавливать, если в системе используется systemd.

Создаём конфиг:

vi /etc/systemd/system/telegram-bot-sometestbot.service

С таким содержанием:

[Unit]
Description=Telegram bot someTestBot

[Service]
WorkingDirectory=/usr/local/bin/someTestBot
ExecStart=/usr/bin/python3 /usr/local/bin/someTestBot/someTestBot.py
Restart=always
RestartSec=10
SyslogIdentifier=python-someTestBot
User=someTestBot

[Install]
WantedBy=multi-user.target

В User=someTestBot можно и root указать, но я рекомендовал бы создать под это отдельного юзера. Правда, тогда чтобы работали скрипты, надо будет дать этому пользователю доступ к mysqladmin.

Включаем нашу только что созданную службу:

systemctl enable telegram-bot-sometestbot.service
systemctl start telegram-bot-sometestbot.service

Ну всё, бот работает, проверяем:

Тестового бота @someTestBot, используемого для статьи, я удалил. Если кто-то в будущем займёт этот юзернейм, то это не я.

Что ещё можно сделать? Ну, вот эта строка - bot.polling() - в общем-то, означает бесконечно выполняющийся цикл запросов к серверам Telegram, что не очень правильно в плане потребления ресурсов на обеих сторонах. Лучше сделать так, чтобы приложение бота ожидало сообщения от Telegram. Это можно сделать, реализовав webhook. Когда доберусь до этого, дополню статью.



[02.12.2018] Update: Telegram бот через вебхук

Наконец-то запилил инструкцию на webhook. Решил не добавлять здесь, а запостить новую статью.