Питонячий Телеграм-Бот, Yandex-API и диплой на Heroku.
На сегодняшний день, наверное, только ленивый не писал и не делал обзорных статей с инструкциями как запилить собственного телеграм-бота. Это уже такой своеобразный мейнстрим на сегодня, особенно среди питонистов.
В данном посте я хотел бы затронуть не столько процесс написания самого бота, сколько описать в целом процесс интеграции кода на питоне с телеграм-сервисом, сторонним API по геокодированию от Yandex и развертыванию этого всего на удаленном бесплатном хостинге Heroku.
Итак, попалось мне как-то одно тестовое задание, где требовалось написать телеграм-бота с возможностью обрабатывать запросы пользователя в виде текста с частичным упоминанием географических адресов. В ответ пользователь должен получить полный структурированный адрес географического объекта. На этом задание конечно не исчерпывалось, но остальное я опишу возможно в другой раз 🙂
Чтобы написать бота, я использовал питонячую библиотеку python-telegram-bot . Там все достаточно понятно. Есть официальная документация и масса примеров в сети. Если коротко описать процесс создания самого бота, то для этого необходимо:
- Создать и зарегистрировать своего бота.
Для этого в сервисе телеграм необходимо отправить Боту-Отцу ( @BotFather ) команду /newbot .
Дать боту имя (name) и имя пользователя (username).
В ответ Папа-Бот вышлет уникальный API-ключ (TOKEN) для вашего бота. Данный ключ является идентификатором для доступа и управления ботом. Его нужно сохранить и держать в тайне, иначе бота могут угнать! (Не размещать в коде в публичном доступе на открытых ресурсах, например).
- Собственно, написать код (логику работы нашего бота), используя любую подходящую библиотечку для телеграм:) В коде мы используем полученный ранее API ключ, для взаимодействия с сервером телеграм.
Структура каталога нашего бота в общем случае должна содержать следующие файлы: __init.py__ — необходим, чтобы Python рассматривал каталог, как содержащий пакеты для корректного импорта.
main.py – сам код (логика) бота.
config.py – указываем наши токены, которые потом импортируются в mian.py .
Вот так, если упрощенно представить на схеме, все и происходит:
- Запустить главный файл main.py на исполнение, и писать запросы нашему боту в телеграм-сервисе.
Создав бота, пришло время дать ему дополнительный функционал. Поскольку цель состоит в том, чтобы бот взаимодействовал с API геокодера, отвечая на запросы пользователя полным адресом географического объекта. Для данной цели подходит API от Yandex. Для бесплатного использования необходимо зарегистрироваться. При регистрации указать некоммерческое использование и после этого нам будет доступно 25000 запросов к API в сутки.
В ответ мы получим также API ключ, который необходимо использовать в коде бота для взаимодействия с Yandex геокодером.
Yandex геокодер использует прямое и обратное геокодирование. То есть, умеет отвечать полным адресом на текстовый запрос или координаты, и наоборот — отправлять координаты по указанному адресу. В коде бота достаточно описать функцию обработчик для запросов пользователя, которая будет формировать запрос, затем парсить ответ от Yandex и выводить ответ пользователю в телеграм-клиент. На официальной странице API Yandex геокодера все достаточно понятно описано.
И вот когда бот написан и отлажен, пришло время дать ему онлайн-доступность. Потому, что до сих пор он работал только при условии запущенного main.py файла на локальном компьютере с доступом в интернет. Для бесплатного хостинга подобных проектов подходит сервис Heroku и созданный в нем активный аккаунт. Кроме того нам понадобится Heroku Command Line Interface (CLI) , который можно скачать тут, а также Git.
Добавим в директорию нашего бота еще пару файлов Procfile и requirements.txt :
Procfile – текстовый, но без расширения. Для этого необходимо использовать сохранение через Notepad++ (опция All-typs (.))
Содержание файла:
requirements.txt – обычный текстовый.
Содержание файла:
Это необходимые компоненты для виртуального окружения на сервере. Конечно, всего этого нам сейчас и не нужно, но пусть будет:) Главное, чтобы необходимые нам библиотеки и фреймворки были нужной версии. После того, как оба файла добавлены в каталог с ботом, необходимо запушить все на гитхаб в отдельный репозиторий, предварительно удалив из кода API токены (помним про безопасность).
Теперь у нас всё готово для загрузки бота на Heroku.
Вернем токены обратно в наш код и приступим к диплою. Из терминала (командной строки — в моем случае это Git bash):
Нажимаем любую клавишу, переходим в браузер, подтверждаем вход.
Имя будет создано автоматически. Можно также это сделать из-под браузера.
Создание и хостинг телеграм бота. От А до Я
Привет, хабрчане! Какой бы заезженной не была тема создания телеграм бота на python3, я не нашёл инструкций, где показан путь от первой строчки кода до деплоинга бота (по крайней мере все методы, что я видел, немного устарели). В этой статье я хочу показать процесс создания бота от написания BotFather-у до деплоинга бота на Heroku.
Статья получилась длинной, советую пробежаться глазами по содержанию и кликнуть по интересующему вас пункту.
P.S. Пишите если нужна статья по созданию более сложного бота, т.е. с вебхуками, БД с настройками юзеров и т.д.
BotFather
Для начала нам надо зарегистрировать нашего бота в Telegram. Для этого:
В поиске вбиваем @BotFather и переходим в диалог с Отцом Ботов.
Пишем /newbot. Указываем имя бота (то, что отображается в диалогах). Указываем его логин, по которому его можно булет найти.
P.S. Оно должно заканчиваться на Bot/bot
Вот. Нам дали API ключ и ссылку на бота. Желательно сохранить API ключ и перейти в диалог с ботом, чтобы потом не копаться в переписке с BotFather
Дальше добавим ему пару команд: пропишем /setcommands и одним сообщением, т.к. /setcommands не добавляет команды, а задаёт их с нуля, пошлём ему команды.
all — спарсить заголовки с вкладки «ВСЁ ПОДРЯД»
top — спарсить заголовки с вкладки «ЛУЧШЕЕ»
На этом работа с BotFather закончилась, перейдём к следующей части.
Установка и настройка pipenv. Первый запуск.
Для начала создадим файл, в котором будет основной код бота bot.py. Если бот большой, то сразу создавайте файлы, куда вы вынесете функции, классы и т.д, иначе читаемость кода стремится к нулю. Я добавлю parser.py
Установим pipenv, если его конечно ещё нет.
Установим pipenv в папку проекта.
Установим интересующие нас библиотеки. Я буду работать с PyTelegramBotAPI. Также для парсинга добавим BeautifulSoup4.
Начинаем писать код!
Открываем bot.py, импортируем библиотеки и создаём главные переменные.
Запустим бота. Посмотри наличие ошибок.
Если ошибок не появилось, то продолжим.
Хэндлеры. Отвечаем на команды и сообщения
Пришло время научить бота отвечать нам. Возможно даже сделать его ответы полезными.
Основы взаимодействия. Ответ на команды
Для взаимодействия с пользователем, т.е. для ответа на его команды и сообщения используются хэндлеры.
Начнём с самого простого: ответим на команды /start и /go
Сейчас разберёмся что это и как это работает. Передаём в message_handler параметр commands равный массиву со строками — командами, на которые он будет отвечать описанным ниже образом. (На все эти команды он ответит одинаково). Далее используем send_message, в него записываем id чата (его можно достать из message.chat.id), в который отправить сообщение и, собственно, само сообщение. Нельзя забыть написать bot.polling() в конце кода, иначе бот сразу же выключиться. Почему так мы узнаем позже.
Теперь можно запустить бота и написать ему /start или /go и он ответит.
P.S. Сообщение может быть не только строкой, а, в принципе, чем угодно.
Это json объект, хранящий информацию об отправителе, чате, и самом сообщении.
Основы взаимодействия. Ответ на текстовые сообщения.
Теперь обработаем текстовые сообщения бота. Самое важное что нам нужно знать это то, что текст сообщения храниться в message.text и то, что, чтобы обрабатывать текст в message_handler нужно передавать content_types=[‘text’].
Добавим вот такой код.
Тут мы довабили пару переменных: вынесли текст сообщения (в нижнем регистре, чтобы не было лишних проблем с теми кто пишет капсом, заборчиком и т.д.) в переменную text, вынесли message.chat.id в отдельную переменную, чтобы каждый раз не обращаться к message. Также мы построили небольшое ветвление, для ответа на определённые сообщения, а также ответ на случай непонятного боту сообщения.
Основы взаимодействия. Ответ на картинки, документы, аудио и прочие.
Для ответа на картинки, стикеры, документы, аудио и т.д. нужно всего лишь поменять content_types=[‘text’].
Рассмотрим пример с картинкой, добавив этот код.
Все типы контента:
text, audio, document, photo, sticker, video, video_note, voice, location, contact, new_chat_members, left_chat_member, new_chat_title, new_chat_photo, delete_chat_photo, group_chat_created, supergroup_chat_created, channel_chat_created, migrate_to_chat_id, migrate_from_chat_id, pinned_message
Строим цепочку ответов.
Пришло время закончить с элементарными действиями и начать что-то серьёзное. Попробуем построить цепочку ответов. Для этого нам понадобиться register_next_step_handler(). Создадим простой пример, на котором и разберёмся как работает register_next_step_handler().
И так, в первой функции добавился bot.register_next_step_handler(msg, askAge), в него мы передаём сообщение, которые хотим послать, и следующий щаг, к которому перейти после ответа пользователя.
Во второй функции всё поинтересней, здесь идёт проверка ввёл ли пользователь число, и, если нет, то функция рекурсивно вызывает сама себя, с сообщением «Возраст должен быть числом, введите ещё раз.». Если пользователь ввёл всё верно, то он получает ответ.
Но, есть тут проблема. Можно повторно вызвать команду /go или /start, и начнётся бардак.
Пофиксить это несложно, добавим переменную для проверки состояния выполнения скрипта.
С построением простых цепочек мы разобрались, пойдём дальше.
Добавляем парсер в цепочку.
Для начала нужен сам парсер. Обратим внимание на то, что во вкладках «Лучшее» и «Всё подряд» есть дополнительные фильтры: сутки, неделя, месяц и ≥10, ≥25, ≥50, ≥100 соответственно.
Парсер конечно можно написать и в 1 функцию, но я разобью на 2, так будет проще читать код.
По итогу парсер возвращает нам строку с заголовками статей, основываясь на наших запросах.
Пробуем, используя полученные знания, написать бота связанного с парсером. Я решил создать отдельный класс (это скорее всего неправильный метод, но это уже относится к питону, а не к основной теме статьи), и в объекте этого класса хранить изменяемые данные.
Теория. Методы взаимодействия с ботом.
Мы используем long polling для получения данных о сообщениях от бота.
Есть же вариант использовать в корне другой метод — вебхуки. Так бот сам будет отправлять нам данные о получении сообщения и т.д. Но этот метод сложнее в настройке, и, для простого показательного бота я решил его не использовать.
Также в дополнительных материалах будут ссылки на всё, что использовалось и о чём говорилось.
Маркапы. Добавляем клавиатуры для быстрого ответа.
Наконец основной код дописан. Теперь можно передохнуть и написать маркапы. Я думаю вы неоднократно видели их, но всё же, приложу скриншот. [SCREENSHOT]
Я выведу маркапы в отдельный файл — markups.py.
В написании маркапов нет ничего сложного. Нужно лишь создать маркап, указать пару параметров, создать пару кнопок и добавить их в маркап, далее просто указываем reply_markup=markup в send_message .
В параметры маркапа указываем ширину строки и изменение размеров кнопок, иначе они огромны.
Применим полученные знания к нашему боту.
Ура! С кодом впринципе разобрались. Теперь самое важное — деплоинг бота не хероку.
Деплоим бота на Heroku.
Для начала надо зарегистрироваться на Хероку и на Гитхабе.
Теперь создаём репозиторий на гитхабе. (нажмите плюсик слева от вашего аватара)
Сейчас нам нужен Procfile (Procfile.windows для windows). Создаём его и записываем в него bot: python3 bot.py
Теперь удаляем TOKEN из bot.py, здесь он не нужен, ведь мы будем загружать этот файл на гитхаб. Через тот же терминале, что использовали для запуска бота, заливаем файлы на гитхаб. (Предворительно удалите папку __pycache__).
Гит просит логин и пароль, спокойно вводим и преступаем к деплоингу бота на хероку. Пишем всё в том же терминале.
Теперь возвращаем TOKEN в bot.py, здесь он нужен, ведь мы будем загружать этот файл на хероку.
Чтобы выключить бота
И, не забываем перед залитием на гитхаб и удалить TOKEN из нашего bot.py. Ведь нам не нужно, чтобы кто-то им пользовался. Можно конечно воспользоваться .gitignore и вынести токены в отдельный фай.
Поздравляю!
Работа окончена, бот работает удалённо.
Ссылки
Заключение
Если кому-то было интересно, то цель написания статьи выполнена. Если кому-то хочется увидеть статью про более сложного бота (с вебхуками, подключенной БД с настройками пользователей и т.д.) — пишите.
Деплой бота на сервере Heroku
Может кто—нибудь дать инструкцию, как развернуть бота на сервере heroku (написанном на python )? Спасибо заранее.
- Первым делом создайте Git репозиторий в папке с ботом: git init
- Войдите в аккаунт Heroku : heroku login
- Создайте приложение: heroku create appname —region eu
Создайте файл Procfile с содержимым: bot: python main.py
- Если бот на веб-хуках, замените bot на web . Но на бесплатном тарифе, бот будет засыпать каждые 30 минут если не будет подключений, по этому на Heroku free лучше держать ботов на лонг-поллинге. Но проблема с засыпанием веб приложения решается с помощью http://kaffeine.herokuapp.com/.
Теперь нужно упаковать все зависимости проекта: pip freeze > requirements.txt
- Теперь наша папка выглядит приблизительно так: Procfile main.py requirements.txt
Коммитим изменения: git add . затем git commit -m ‘commit’
Пушим на Heroku : git push heroku master
Запускаем бота: heroku ps:scale bot=1
Смотрим логи: heroku logs -t
В папке с проектом должны быть такие 3 файла:
Proc
requirements.txt
+ другие модули (сгенерировать этот файл можно так: pip freeze > requirements.txt )
runtime.txt
или другая версия python
Из папки, где находится проект, запускаете такие команды:
Дополнительно рекомендую установить токен как переменную: heroku config:set TOKEN=7593654323:AAlkjHjhDZiWUD7Th4Dl_uPlkjpdorpoHGhgdsPVjQ (также можно установить переменные и на сайте Хероку) и обращаться к ней из программы
Создаем телеграм-бот с вебхуками на Heroku
Поделюсь своим опытом разработки телеграм-бота для большого количества пользователей: разберу свои ошибки и шаги для их решения.
Одной из моих рабочих задач, как программиста, была автоматизация проведения викторины. Конечно, уже существуют специализированные бесплатные приложения, заточенные под эти задачи, но нужно было такое, в котором не было бы ничего лишнего, оно было всегда под рукой и такое привычное, чтобы не нужно было с ним разбираться. Выбор пал на телеграм-бота и для того чтобы он справлялся с большей нагрузкой было принято решение использовать асинхронную библиотеку aiogram.
Начнём с создания эхо бота на aiogram, тут нет ничего сложного, возьмём пример из документации:
Однако преимущество aiogram над python-telegram-bot и pyTelegramBotAPI в том, что он асинхронный, а значит, может обрабатывать несколько запросов почти единовременно. Стандартная база данных sqlite отлично подходит для несложных проектов и уже входит в стандартную библиотеку питона, поэтому для начала я решил использовать её.
Через несколько часов работы приложение было написано, и мы с коллегами решили протестировать на себе его работоспособность. Бот запускался с использование технологии long polling, и запускался он на локальном компьютере. Для небольшого количества человек этого вполне достаточно: 3-4 человека в секунду бот выдерживает без особых проблем.
Но, к сожалению, или к счастью, во время проведения викторины боту посыпалось бОльшее количество запросов, на которое мы не рассчитывали, в связи с чем посыпались ошибки не обрабатываемые ошибки, связанные с одновременным постоянным запросом новых сообщений у сервера и обработкой уже полученных.
Решением этой проблемы стал переход на вебхуки. И для обеспечения бесперебойной работы разместим его на удалённом сервере. Отличным решением для этого является heroku: здесь можно управлять запуском приложения как с компьютера, так и с мобильного приложения, отслеживать логи и, что является наиболее важным для нас, настраивать вебхуки.
Алгоритм для реализации эхо бота в данном случае занимает больше времени, но он достаточно прост: