Telegram Bot
Постановка задачи и первичные варианты решения
В связи с ежедневными вечерними (да ещё и постоянно в разное время) обновлениями расписания в ОГАПОУ «Белгородский индустриальный колледж» необходимо программное обеспечение (ПО), которое будет следить за расписанием и уведомлять при его изменении.
Да, в Интернете по созданию ботов в Telegram есть куча статей, но в данной статье будет описан весь мой путь до стабильной и бесплатной работы бота в мессенджере Telegram.
Процесс разработки бота
Создание нового бота в BotFather
Для начала необходимо создать нового бота и автоматически получить токен этого бота у создателя ботов в Telegram @BotFather. Справка по командам была представлена в пункте «Сервисы для разработки – Telegram» этой статьи. Также сразу можно изменить название бота, описание бота, информацию о боте, фото профиля бота.
Запишем полученный токен в файл конфигов configs.py:
# токен бота из @BotFather TOKEN = '2118918752:token'
Сам токен скрыт в этой статье, чтобы предотвратить несанкционированное использование моего бота.
Создание нового приложения на Heroku
После регистрации на Heroku, создадим новое приложение в регионе Европа. На выбор предлагался ещё регион Соединённые Штаты. Подробнее про регионы для приложений Heroku – развертка в различных географических регионах.

Классическая структура бота Telegram
Присвоим токен, инициализируем обработчик входящих сообщений. Напишем обработчик для команды /start – глобальной команды, с которой начинается общение любого пользователя с любым ботом Telegram. В конце запустим режим длительного опроса. Таким образом создадим классическую структуру бота в основном файле bot.py:
Код Python
Блок if (__name__ == '__main__'): позволяет выполнять вложенные инструкции только при запуске самого файла, а не кода из импортированного модуля. Подробнее можно прочитать в статье «Зачем нужен if __name__ == '__main__' ?».
Создание модуля парсера данных с сайта «Расписание занятий»
Сайт – «Расписание занятий»
Создадим конструктор __init__, в который будет передаваться URL расписания. Конструктор класса – метод, который автоматически вызывается при создании объекта этого класса. Хотя в Python правильнее называть конструктором метод __new__. Но принимать параметры нужно именно в методе __init__, ведь задача этого метода как раз изменить новое состояние вновь созданного экземпляра класса. Метод __init__ не должен ничего возвращать, чтобы не вызвать ошибку. Параметр self это ссылка на конкретный экземпляр класса. Для обращения к переменным экземпляра всегда нужно дописывать self: self.url. Начало создания модуля в файле BIKParser.py:
Код Python
Для удобного использования классов и методов необходимо грамотно заполнять docstring – строки документации. Заполняется в комментарии вида '''docstring''' или """docstring""" сразу после объявления класса или метода. Обратиться к такой строке документации можно через атрибут __doc__. Для заполнения таких строк применяется язык разметки – reStructuredText. Результат документации с синтаксисом выше при наведении на метод:

Вызов методов внутри класса тоже производится через self.
Реализуем метод заполнения расписания FillFileSchedule:
def FillFileSchedule(self):
'''Заполнение файла расписания'''
# заполнить файл старого расписания
f = open('old_schedule.txt', 'w')
f.write(str(self.new_schedule))
f.close()
Реализуем метод проверки расписания CheckChange. Получаем страницу расписания, выделяем все строки таблиц. Выделяем только нужные данные из всех данных парсинга, сразу же дополняя в начале <table><tbody> и в конце </tbody></table> для формирования полноценной HTML таблицы. Далее необходимо выполнить проверку на наличие изменений в расписании по отношению к прошлому сохранённому расписанию. В случае изменения расписания необходимо перезаписать старый файл расписания и изменить переменную результата на True.
Код Python
Для отправки картинки расписания нужно создать соответствующий метод ChangeImage.
Код Python
Так как бот планируется быть размещённым на Heroku, а там файлы приложения хранятся в директории app, то эту директорию необходимо указать в качестве выходного пути для модуля HTML2Image, импортированного нам в код, как HTI.
Также стоит обратить внимание на строчку из официальной документации модуля HTML2Image, которая была представлена в пункте «Описание используемых пакетов Python – HTML2Image» этой статьи:
However default flags are not used if you decide to specifycustom_flagsor change the value ofbrowser.flags:
В которой говориться об отмене флагов по умолчанию при использовании пользовательских флагов или изменении значений флагов браузера. В таких случаях нужно будет не забыть вернуть флаги по умолчанию: --default-background-color=0 (объяснение флага) и --hide-scrollbars (объяснение флага).
Создание модуля по работе с БД PostgreSQL
В ресурсы приложения на Heroku необходимо добавить дополнение Heroku Postgres для добавления БД к приложению. При добавлении выбираем бесплатный план Hobby Dev.

Добавим в файл конфигов configs.py идентификатор упрощённого подключения к БД PostgreSQL:
# идентификатор упрощённого подключения к БД PostgreSQL DB_URI = 'postgres://user:password@host:port/database'
Сам идентификатор упрощённого подключения скрыт в этой статье, чтобы предотвратить несанкционированное использование моей БД.
Стоит обратить внимание на фразу из окна настроек БД на Heroku:
Please note that these credentials are not permanent. Heroku rotates credentials periodically and updates applications where this database is attached.
Из которой понятно, что учётные данные для подключения не постоянные и в ходе обслуживания БД Heroku меняет эти данные. Об обслуживании на электронную почту аккаунта Heroku приходит письмо такого содержания:
Your database DATABASE_URL on bikbeepbot requires maintenance. During this period, your database will become read-only. … We expect maintenance to last just a few moments depending on the size of your database. We will notify you when maintenance begins, and again once it's complete.
В письме говорится о том что необходимо провести обслуживание БД и в это время БД будет доступна только для чтения. Обслуживание будет быстрым. Во время начала и конца обслуживания будут приходить повторные письма.
Для создания таблицы в БД необходимо в pgAdmin 4 подключиться к выделенной нам БД по предоставленным учётным данным.


Далее необходимо в обозревателе найти и развернуть свою БД создать схему и уже в ней создать таблицу. Далее пользуясь кнопкой «+» добавляем два столбца. Поле «По умолчанию» заполнится автоматически после сохранения при выборе серийного типа данных. Также после сохранения серийный тип данных преобразовывается в соответствующий ему тип данных целых чисел.

Повторно открыть это окно можно кликнув правой кнопкой мыши (ПКМ) по таблице в обозревателе.
Так как моим ботом не будет пользоваться большое число пользователей, то я выбрал тип данных с диапазоном от 1 до 32767. Как сказано в официальной документации API ботов Telegram, представленной в пункте «Сервисы для разработки – Telegram» этой статьи:
Unique identifier for this user or bot. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier.
Для уникальных идентификаторов пользователей безопасно использование 64-битных целых чисел, которым и является bigint в БД PostgreSQL.
Создадим конструктор __init__модуля, в который будет передаваться идентификатор упрощённого подключения к БД PostgreSQL. Начало создания модуля в файле SQLRequests.py:
Код Python
Далее необходимо реализовать метод проверки наличия пользователя в БД user_exists и метод добавления пользователя в БД user_add:
Код Python
Для корректного выполнения запроса название таблицы БД обязательно должно быть в двойных кавычках "TableName". В запросах применяются f-строки появившиеся в Python 3.6. Статья на русском по форматированию строк и с примерами f-строк: f-строки в Python 3.
Для рассылки боту необходимо получить идентификаторы пользователей, для этого реализуем ещё один метод get_users:
Код Python
Хоть разница в методах получения результатов fetchone( ) и fetchall( ) очевидна из названия, но подробнее можно почитать в официальной документации модуля psycopg2, представленной в пункте «Описание используемых пакетов Python – psycopg2» этой статьи.
Подключение и использование созданных модулей
Изначально надо инициализировать созданные модули. Модернизируем файл bot.py:
Код Python
Запоминать идентификаторы пользователей будем при старте общения пользователя с ботов в обработчике команды /start. Для этого в метод welcome добавим проверку на наличие пользователя в БД:
# проверка на наличие пользователя в БД if (not db.user_exists(message.from_user.id)): # добавление пользователя в БД db.user_add(message.from_user.id)
Проверять изменение расписания, формировать изображение с расписанием, а затем получать идентификаторы пользователей и отправлять им изображение с расписанием под циклом while True: будем в методе scheduled:
Код Python
Так как метод get_users() возвращает лист кортежей, то в цикле for необходимо выбирать идентификаторы пользователей, которые в кортеже (0, 1) идут под номером 1. Под номером 0 будет первичный ключ – столбец id, являющийся автоинкрементом.
После запуска бота и начала общения с ботом проверить содержимое столбцов таблицы БД можно в pgAdmin 4, кликнув ПКМ по таблице и выбрав «Просмотр/редактирование данных – Все строки» или выбрав «Запросник» и написав запрос:
SELECT * FROM bikbeepbot."UsersBD"
Нажав кнопку выполнения запроса «▶» (треугольник вправо) внизу отобразится результат запроса.

Развёртка бота на Heroku
Стек – образ ОС поддерживаемых Heroku. Стеки основаны на дистрибутиве Linux – Ubuntu. Heroku на текущий момент предоставляет два стека, на котором может работает приложение: Heroku-18 и Heroku-20. Цифра в названии зависит от первых цифр версий Ubuntu: Heroku-18 основан на Ubuntu 18.04 и закончит поддержку в апреле 2023 года, а также Heroku-20, поддерживающий Python 3, основан на Ubuntu 20.04 и закончит поддержку в апреле 2025 года. Приложения по умолчанию размещаются на Heroku-20.
Перед развёрткой бота необходимо создать 3 служебных файла в корневой директории, а также выбрать необходимые сборочные пакеты.
Служебные файлы
- Файл
runtime.txtсодержит среду выполнения Python и точное название версии.
python-3.10.0
2. Файл requirements.txt содержит зависимостей для приложения – сторонние пакеты, используемые в исходном коде нашего приложения.
aiogram requests bs4 html2image pillow psycopg2
3. Файл Procfile содержит команды, выполняемые приложением при запуске. Строки файла должны иметь следующий формат: <process type>: <command>.
worker: python bot.py
Сборочные пакеты
Добавление сборочных пакетов для приложений происходит в разделе «Настройки».

- Сборочный пакет для приложений Python
heroku/python. - Сборочный пакет для драйвера браузера Chrome
https://github.com/heroku/heroku-buildpack-chromedriver.git. - Сборочный пакет для браузера Google Chrome
https://github.com/heroku/heroku-buildpack-google-chrome.git. Данный сборочный пакет включает в себя в строке 183 файла/bin/compileфлаг--remote-debugging-port=9222включающий удалённую отладку и запрещающий делать снимок экрана. Для исправления этой ошибки воспользуемся ответвлением этого сборочного пакета от aurelmegnhttps://github.com/aurelmegn/heroku-buildpack-google-chrome.git.
Выполнение развёртки приложения
Все последующие команды выполняются в командной строке, находясь в корневой директории с исходным кодом бота. Смена директории осуществляется командой cd.
Справка по команде:
- выполнив команду
cd /?; - описание команды CD (на русском).
Для входа в Heroku CLI выполним команду heroku login, которая откроет окно браузера по умолчанию для выполнения авторизации на Heroku.

Справка по командам Git представлена в пункте «Сервисы для разработки – Git» этой статьи.
Создадим Git-репозиторий командой git init. Выполняется один раз при первой развёртки приложения.
Для отслеживания новых, изменённых и удалённых файлов в текущей директории используется команда git add ..
Для фиксации изменений используется команда git commit -m "First release". Для повторных фиксаций изменений правильнее менять сообщение в кавычках.
Просмотр адресов удалённых репозиториев может осуществиться с помощью команды git remote -v. Команда не является обязательной.
Для отправки изменений в удалённый репозиторий выполним команду git push heroku master.
Результат работы
Для просмотра состояния приложений Heroku можно воспользоваться командой heroku ps.
Запустим приложение, выполнив команду heroku ps:scale worker=1.
Разработанный бот в Telegram – @BIKbeep_bot.


Для остановки приложения можно выполнить команду heroku ps:scale worker=0 -a bikbeepbot.
Чтобы получить исходное содержимое репозитория приложения можно осуществить клонирование репозитория по пути из которого выполнится команда heroku git:clone -a bikbeepbot.
Ошибки
В итоговом варианте бота не обошлось и без ошибок, исправить которые не получилось. Но на правильность работы бота эти ошибки не повлияли.
Журнал приложения
app[worker.1]: [1218/191809.376330:ERROR:bus.cc(393)] Failed to connect to the bus: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory app[worker.1]: [1218/191809.480389:ERROR:sandbox_linux.cc(376)] InitializeSandbox() called with multiple threads in process gpu-process. app[worker.1]: /app/bot.py:67: DeprecationWarning: There is no current event loop app[worker.1]: get_event_loop().create_task(scheduled(15))
Последующие ошибки (от 11 марта 2022)
Вследствие последних политических событий ОГАПОУ «Белгородский индустриальный колледж» ограничил доступ к своему сайту странам Европы, где и размещается бот. Для решения данной проблемы было принято решение воспользоваться сервисом онлайн-просмотра кода файлов по URL от Дмитрия Елисеева.
После небольших правок в методе проверки расписания CheckChange бот продолжил свою работу. Во всей статье код метода CheckChange представлен без этих правок.
Исходный код
Файл configs.py:
# токен бота из @BotFather TOKEN = '2118918752:token' # идентификатор упрощённого подключения к БД PostgreSQL DB_URI = 'postgres://user:password@host:port/database'
Файл bot.py:
Код Python
Файл BIKParser.py:
Код Python
Файл SQLRequests.py:
Код Python
Файл runtime.txt:
python-3.10.0
Файл requirements.txt:
aiogram requests bs4 html2image pillow psycopg2
Файл Procfile:
worker: python bot.py