Как создать бота для телеграмма на python
Перейти к содержимому

Как создать бота для телеграмма на python

  • автор:

Как создать бота для телеграмма на python

Для начала давайте определимся с тем что такое бот. Бот представляет из себя интерфейс telegram с помощью которого может вызываться любая программа. То есть это прослойка между чатом в вашем телефоне и кодом на компьютере или сервере, помогающая отправить и получить данные в чате.

С ботом можно начать диалог как и с обычным человеком, или добавить его в группу. Разница в том, что бот понимает только ограниченный набор заранее заданных команд, которые передаются в программу. В зависимости от логики программы, бот может отвечать в чат или выполнять любые другие действия, доступные языку программирования на котором написан код. То есть можно, например, получить данные из базы данных, найти нужный файл, сохранить изображение и еще много чего.

Схематично это взаимодействие выглядит примерно так:

Папа ботов

Для того, чтобы создать своего бота, необходимо обратиться с этой просьбой к отцу всех ботов:) На полном серьезе.

Ищем бота с ником @botfather и начинаем с ним диалог:

Пишем ему сообщение /help и получаем перечень всех команд для botfather.

Здесь мы не будем подробно останавливаться на описании каждой команды. Сейчас нас интересует только создание нового бота. Это команда /newbot. Отправим её botfather и посмотрим что будет.

При получении этого сообщения botfather начинает процесс создания и просит вас задать имя для вашего нового бота, а затем и username. Разница в том, что имя может быть любым, тогда как username обязательно должно оканчиваться на «bot» (для того чтобы человек сразу понимал, что общается с ботом, а не с другим человеком). Для упрощения мы зададим одинаковые имя и username нашему боту.

После того, как название выбрано, botfather присылает нам токен бота.

Токен – это уникальный идентификатор нашего бота. Точка связи между ботом и кодом на сервере/компьютере. Именно по нему наш код будет обращаться к боту и попадать туда куда нужно, а бот в свою очередь идентифицировать то, что обращаются именно к нему.

На этом наше общение с botfather окончено.

Пишем функционал бота

Переходим ко второй части создания бота – написанию кода. В этой статье мы будем рассматривать в качестве основы python, который для начла нужно установить, если он у вас еще не установлен. Сделать это можно на официальном сайте python просто следуя инструкциям установщика.

После того как питон установлен, нам нужно установить библиотеку telepot, обеспечивающую взаимодействие python с telegram. Сделать это можно за 5 секунд. Открываем командную строку и пишем там $ pip install telepot.

В случае успешной установки мы увидим, Successfully installed. Вот как это выглядит у меня после всего «птичьего текста» процесса установки.

Описание всего функционала библиотеки доступно здесь. Написано легко и понятно. Но на английском.

Теперь у нас есть python и библиотека telepot, позволяющая взаимодействовать с telegram. Время написать простой код.

import telepot from pprint import pprint bot = telepot.Bot('***** Сюда надо вставить токен, полученный у botfather *****') response = bot.getUpdates() pprint(response)

Здесь мы подключили библиотеки telepot и pprint, добавили токен, полученный нами ранее у botfather, послали запрос боту на то чтобы он отправил нам последние сообщения (bot.getUpdates()) и вывели результат на экран. Полученное сообщение выглядит так:

, 'date': 1465283242, 'from': , 'message_id': 10772, 'text': 'Hello'>, 'update_id': 100000000>

Важный нюанс! Бот не человек, сам инициировать беседу не может, поэтому до того как выполнять код, вам необходимо проявить инициативу:) Для этого найдем в telegram нашего бота @rtportal_tp_bot и напишем ему что-нибудь.

Как мы видим, сообщение представляет собой массив, представленный в виде словаря python. Сообщение содержит в себе идентификатор чата (id), то, с кем ведется чат (first_name), тип чата, дату сообщения, от кого получено сообщение, номер сообщение и собственно сам текст. Вообще наполнение полученного сообщения постоянно обновляется, поэтому вполне вероятно, что когда вы будете читать этот текст, в ответе будет приходить гораздо больше параметров. Актуальная версия описания всегда доступна в описании api на официальном сайте telegram.

Теперь, когда мы знаем как выглядит сообщение, можно написать простого бота-калькулятора. Код будет выглядеть следующим образом:

import telepot from telepot.loop import MessageLoop TOKEN = '***** Сюда надо вставить токен, полученный у botfather *****' bot = telepot.Bot(TOKEN) def handle(msg): """ Process request like '3+2' """ content_type, chat_type, chat_id = telepot.glance(msg) text = msg["text"] try: answer = eval(text) except: answer = "Can't calculate :(" bot.sendMessage(chat_id, "answer: <>".format(answer)) MessageLoop(bot, handle).run_as_thread() # Keep the program running. while True: n = input('To stop enter "stop":') if n.strip() == 'stop': break

Здесь мы подключили библиотеку telepot, добавили информацию о токене бота, прочитали полученное сообщение и, если оно представляет собой математическую операцию, выполняем его функцией eval. В противном случае выводим сообщение о том, что калькуляция не возможна.

Для того, чтобы программа выполнялась, в конце добавлен цикл, выполняющий код до момента написания слова stop‘.

Запускаем код и проверяем результат в telegram. Все работает!

Нюансы

Есть несколько важных нюансов, которые нужно понимать при работе с ботами telegram:

  1. Бот сам не может являться инициатором беседы в чате. Это можете сделать только вы. До момента как вы напишете что-нибудь боту, он считается выключенным.
  2. Так как код python (и любой другой) запускается на компьютере или сервере, он будет выполняться только если компьютер/сервер доступен. То есть в случае если компьютер с кодом «заснул», отвалилась сессия или код завис — бот работать не будет. То же самое относится к серверу.
  3. Если компьютер/сервер с кодом бота перезагрузился, нужно запускать код заново, а так же инициировать беседу с ботом в telegram (см.п.1) иначе бот работать не будет.
  4. Есть два метода получения сообщений. Это getUpdates и webhooks. В этой статье описан метод getUpdates как наиболее простой для старта. Отличие getUpdates от webhooks в том что в первом случае ваш компьютер/сервер через заданные промежутки времени проверяет наличие новых сообщений, а во втором telegram сам отправляет новые сообщение при их наличии к вам на сервер. Webhooks меньше грузит и ваш сервер и сервера telegram, однако требует дополнительной настройки и наличия ssl сертификата.

В этой статье мы рассмотрели как самостоятельно написать код для бота, однако, стоить отметить, что в telegram уже есть всевозможные боты с типовым функционалом и если вам надо сделать что-то простое, то можно воспользоваться ими. Так же есть конструкторы ботов, позволяющие создавать логику без написания кода. Например, @chatfuelbot. Но если вам нужен нетиповой функционал, то все же лучше написать код самостоятельно.

Из ограничений готовых ботов и платформ-конструкторов можно отметить то, что в результате их популярности и большого количества пользователей, такие боты могут работать не стабильно в зависимости от нагруженности серверов, на которых размещены боты. Это может выражаться как в увеличенном времени реакции бота, так и в полном отсутствии реакции в части случаев.

Зачем все это:

Ключевой вопрос: «как можно применить бота?». Можно, например, научить бота python читать логи на предмет ошибок и выдавать сообщение если в процессе что-то пошло не так. Можно обрабатывать aml события и блокировать счета мошенников в реальном времени (привет sas-форум). Можно создать библиотеку знаний по нужной области рисков и отправлять нормативные документы по запросу.

Применить бот в работе рисковика можно. Все зависит от вашей фантазии и наличия открытых портов для api telegram в вашем банке 🙂

О том как научить вашего бота следить за событиями Google Calendar можно прочитать в этой статье.

Полезные ссылки:

Создание telegram-ботов на Python

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

Для готового бота подобрать ресурсы сложнее, и если вы не уверены, задайте вопрос нашей поддержке через форму подбора тарифа. Чем подробнее вы опишете ваш проект, тем точнее специалисты смогут подобрать подходящий тариф.

Операционная система подойдет любая, но если это ваш первый проект, то лучше выбрать Ubuntu: она проще в администрировании, чем CentOS.

Подключитесь к боксу по SSH и обновите ОС — с момента создания образа на нашей площадке могло выйти несколько обновлений для операционной системы и других пакетов, в которых могли быть закрыты обнаруженные уязвимости.

Для Ubuntu обновление проводится в 2 команды:

root@box-10000:~# apt-get update root@box-10000:~# apt-get upgrade

Их можно совместить в одну:

root@box-10000:~# apt-get update && apt-get upgrade

Когда все основные компоненты системы будут обновлены, можно приступать к подготовке бокса к размещению телеграм-бота.

Создание пользователя для бота

Запускать бота под пользователем root небезопасно. Сделайте отдельного пользователя для этих целей.

Запустите команду adduser, передав ей аргументом имя будущего пользователя.

root@box-10000:~# adduser bot

Далее команда предложит придумать пароль, повторить его ещё раз и заполнить информацию о новом пользователе. Все поля, кроме пароля, не обязательны, можно жать Enter для подстановки значения по умолчанию.

Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully Changing the user information for username Enter the new value, or press ENTER for the default Full Name []: Room Number []: Work Phone []: Home Phone []: Other []: Is the information correct? [Y/n]

Стать новым пользователем можно командой su:

root@box-10000:~# su - bot bot@box-10000:~$

Переключиться обратно к root-пользователю можно с помощью команды logout, либо сочетанием клавиш Ctrl+D, которое сразу выполнит эту команду:

bot@box-10000:~$ logout root@box-10000:~#

Подготовка окружения

Проверьте версию Python, на котором будет запускаться бот:

root@box-10000:~# python3 --version Python 3.8.5

Если вы будете размещать готового бота, версия Python должна соответствовать той, на которой бот был разработан.

Для создания нового бота подойдет любая версия.

Если у вас есть готовый бот, написанный на более старой версии языка, установите интерпретатор для него.

Установите менеджер пакетов pip. Он пригодится в дальнейшем для загрузки библиотек.

В Ubuntu установка производится так:

root@box-10000:~# apt install python3-pip

Следующий шаг — создание виртуального окружения. Это нужно для того, чтобы устанавливать разные модули разным проектам и избегать их конфликтов.

Станьте пользователем bot и установите модуль virtualenv командой:

bot@box-10000:~$ pip3 install virtualenv --user

Ключ —user поставит его локально для пользователя bot.

После установки добавьте виртуальное окружение в системные пути пользователя:

bot@box-10000:~$ export PATH=$HOME/.local/bin:$PATH

Создайте новое окружение:

bot@box-10000:~$ virtualenv --system-site-packages python

python — произвольное имя нового виртуального окружения
Ключ —system-site-packages задействует внутри окружения модули, уже установленные в системе.

И активируйте его:

bot@box-10000:~$ source ~/python/bin/activate (python) bot@box-10000:~$

Выйти из окружения можно с помощью команды deactivate:

(python) bot@box-10000:~$ deactivate bot@box-10000:~$

После создания и активации окружения можно устанавливать модули, которые нам потребуются для запуска бота. В первую очередь это pyTelegramBotAPI. Установим его:

(python) bot@box-10000:~$ pip3 install pyTelegramBotAPI

Регистрация бота

Создание любого бота начинается сообщения отцу ботов в телеграме — @BotFather.

Он может управлять всеми существующими ботами, с помощью множества команд. Их список в любой момент можно вызвать командой /help

Для создания нового бота отправьте команду /newbot. После ответов на пару вопросов бот будет создан, а отец ботов пришлёт токен. Его нужно будет указывать в коде для взаимодействия с BotAPI.

Токен для каждого бота уникален. Нельзя, чтобы он попал в открытый доступ. Однако если это произошло, его всегда можно сменить через ботопапу командой /revoke.

Так будет выглядеть диалог создания бота:

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

Окружение подготовлено, бот зарегистрирован, можно начать писать его код.

Создайте директорию для бота, в ней будут все файлы проекта:

bot@box-10000:~$ mkdir tgbot

В директории создайте конфигурационный файл, в котором будут храниться некоторые данные проекта, например, токен:

bot@box-10000:~$ vim tgbot/config.py

Помимо vim можно использовать vi или nano. Далее добавьте в файл токен, в виде такой строки:

TOKEN #bot-launch" name="bot-launch">Запуск бота

В примере выше бот запускается прямым вызовом интерпретатора Python в консоли, но работать он будет, пока активна сессия SSH. Ещё можно запустить его в tmux или screen, но нужно будет следить за тем, чтобы бот всегда работал и поднимать его. Этот процесс можно упростить с помощью системных демонов linux.

Для этого создайте сервис:

root@box-10000:~# vim /etc/systemd/system/bot.service

И добавьте в него такой код:

[Unit] Description=Awesome Bot After=syslog.target After=network.target [Service] Type=simple User=bot WorkingDirectory=/home/bot/tgbot ExecStart=/usr/bin/python3 /home/bot/tgbot/bot.py Restart=always [Install] WantedBy=multi-user.target

После этого выполните 3 команды друг за другом. Первая перезапустит менеджер служб, вторая включит демона, третья — запустит бота:

root@box-10000:~# systemctl daemon-reload root@box-10000:~# systemctl enable bot root@box-10000:~# systemctl start bot

Проверить, что бот работает, можно командой:

root@box-10000:~# systemctl status bot

Её вывод будет примерно таким:

● bot.service - Awesome Bot Loaded: loaded (/etc/systemd/system/bot.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2021-03-29 05:52:50 MSK; 3min 12s ago Main PID: 2087 (python3) Tasks: 4 (limit: 2344) Memory: 19.9M CGroup: /system.slice/bot.service └─2087 /usr/bin/python3 /home/bot/tgbot/bot.py

Самое важное здесь — состояние бота, в строке «Active». Если бот вдруг остановился, его можно снова запустить:

root@box-10000:~# systemctl start bot

Когда вы вносите изменения в код бота, сервис нужно перезапускать:

root@box-10000:~# systemctl restart bot

Чтобы отключить автоматический перезапуск бота, нужно его остановить и отключить демона:

root@box-10000:~# systemctl stop bot root@box-10000:~# systemctl disable bot

Документация

  • Вводная статья на сайте Telegram
  • API telegram-ботов
  • Библиотека pyTelegramBotAPI

Как создать Telegram-бота с помощью библиотеки python-telegram-bot

Делимся инструкцией, как написать бота с помощью библиотеки python-telegram-bot за считанные минуты. На примере гайда от программиста Давида Мастроматтео.

Установка python-telegram-bot

Для создания бота понадобится пакет python-telegram-bot — оболочка для API от Telegram. Написать бота с помощью этой библиотеки очень просто, так как она полностью совместима с Python 3.6+.

Первое, что нужно сделать — установить python-telegram-bot. Вот ссылка на официальную документацию библиотеки .

$ pip install python-telegram-bot –upgrade

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

Теперь можно взяться за создание бота. Для этого даже не нужно писать код. Перейдите в Telegram и найдите канал @BotFather, который отвечает за регистрацию новых ботов. Начните общаться с ботом и введите команду /newbot . Затем BotFather спросит у вас имя и юзернейм.

Курс Мікросервісна архітектура.

Навчитеся будувати архітектуру з нуля, розгортати проєкти на мікросервісах та ухвалювати рішення, коли доцільно переводити проєкт на інший тип архітектури.

bot father 1

bot father 2

У BotFather можно запросить много других интересных вещей. Например, изменить изображение профиля бота.

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

Освоить язык программирования Python вам могут помочь онлайн курсы от наших партнеров Mate Academy и Powercode. Получите качественный уровень знаний от практикующих специалистов.

Программирование бота

Пакет python-telegram-bot состоит из оболочки API Telegram. Этот инструмент доступен через telegram.Bot- классы. Помимо них, есть еще модуль telegram.ext , который значительно упростит работу.

Модуль telegram.ext содержит много классов, но самые важные — telegram.ext.Updater и telegram.ext.Dispatcher . Updater отвечает за выборку новых обновлений от Telegram. Также он передает их в Dispatcher , после чего они обрабатываются с помощью Handler .

Приступим к программированию:

# mastrobot_example.py from telegram.ext import Updater, CommandHandler, MessageHandler, Filters # function to handle the /start command def start(update, context): update.message.reply_text('start command received') # function to handle the /help command def help(update, context): update.message.reply_text('help command received') # function to handle errors occured in the dispatcher def error(update, context): update.message.reply_text('an error occured') # function to handle normal text def text(update, context): text_received = update.message.text update.message.reply_text(f'did you said "" ?') def main(): TOKEN = "insert here your token and don't share it with anyone!" # create the updater, that will automatically create also a dispatcher and a queue to # make them dialoge updater = Updater(TOKEN, use_context=True) dispatcher = updater.dispatcher # add handlers for start and help commands dispatcher.add_handler(CommandHandler("start", start)) dispatcher.add_handler(CommandHandler("help", help)) # add an handler for normal text (not commands) dispatcher.add_handler(MessageHandler(Filters.text, text)) # add an handler for errors dispatcher.add_error_handler(error) # start your shiny new bot updater.start_polling() # run the bot until Ctrl-C updater.idle() if __name__ == '__main__': main()

В функции main создан класс Updater , который автоматически сгенерировал объект Dispatcher , доступный через .dispatcher- свойства класса Updater .

Добавьте несколько обработчиков:

    команда /start вызывает функцию start() , которая отвечает пользователю информативным сообщением;

Сейчас в коде прописаны функции обратного вызова, которые используют полученные данные для отправки сообщений пользователю.

Тестирование

Теперь можно протестировать бота. Запустите его.

Пошлите ему команду /start .

Ура, бот работает!

Но это не конец. Надо создать бота, который сообщает пользователю его ежедневный биоритм. Для этого следует применить команду /start . С ее помощью при запуске чата можно получить данные о дне рождения пользователя. Затем надо создать функцию для обработки новой команды /biorhythm , чтобы отправить ответ пользователю с его личным биоритмом.

Чтобы узнать день рождения пользователя, для начала нужно изменить функцию, обрабатывающую команду /start . Чтобы упростить задачу, попросите пользователя указать год, месяц и день рождения.

Курс Fullstack Web Development.

Fullstack developer вміє працювати як з фронтендом, так і з бекендом сайта, що дозволяє релізовувати будь-яку ідею комплексно і самостійно. Ця професія різноманітна й творча, ти точно не будеш нудьгувати.

# function to handle the /start command def start(update, context): first_name = update.message.chat.first_name update.message.reply_text(f"Hi , nice to meet you!") start_getting_birthday_info(update, context)

В параметре update можно найти полезную информацию о пользователе, например, его имя.

В самом начале скрипта определите новую переменную STATE , которая нужна, чтобы понять, на какой вопрос отвечает пользователь.

STATE = None BIRTH_YEAR = 1 BIRTH_MONTH = 2 BIRTH_DAY = 3

Теперь необходимо реализовать функцию start_getting:_birthday_info() , она вызывается с помощью команды start() . После запуска вы получите информацию о дне рождения от пользователя.

def start_getting_birthday_info(update, context): global STATE STATE = BIRTH_YEAR update.message.reply_text(f"I would need to know your birthday, so tell me what year were you born in. ")

Для переменной STATE устанавливается значение BIRTH_YEAR , чтобы после ответа пользователя было понятно, что вопрос касался года рождения. Затем отправляется сообщение, чтобы узнать год рождения.

Теперь пользователь ответит обычным текстом, поэтому нужно изменить функцию text() .

def text(update, context): global STATE if STATE == BIRTH_YEAR: return received_birth_year(update, context) if STATE == BIRTH_MONTH: return received_birth_month(update, context) if STATE == BIRTH_DAY: return received_birth_day(update, context)

В функции text() необходимо понять, на какой вопрос отвечает пользователь, используя переменную STATE . После чего остается вызвать функцию для обработки каждого ответа.

Эти функции можно записать так:

def received_birth_year(update, context): global STATE try: today = datetime.date.today() year = int(update.message.text) if year > today.year: raise ValueError("invalid value") context.user_data['birth_year'] = year update.message.reply_text(f"ok, now I need to know the month (in numerical form). ") STATE = BIRTH_MONTH except: update.message.reply_text("it's funny but it doesn't seem to be correct. ") def received_birth_month(update, context): global STATE try: today = datetime.date.today() month = int(update.message.text) if month > 12 or month < 1: raise ValueError("invalid value") context.user_data['birth_month'] = month update.message.reply_text(f"great! And now, the day. ") STATE = BIRTH_DAY except: update.message.reply_text("it's funny but it doesn't seem to be correct. ") def received_birth_day(update, context): global STATE try: today = datetime.date.today() dd = int(update.message.text) yyyy = context.user_data['birth_year'] mm = context.user_data['birth_month'] birthday = datetime.date(year=yyyy, month=mm, day=dd) if today - birthday < datetime.timedelta(days=0): raise ValueError("invalid value") context.user_data['birthday'] = birthday STATE = None update.message.reply_text(f'ok, you born on ') except: update.message.reply_text("it's funny but it doesn't seem to be correct. ")

Когда получен год рождения пользователя, остается проверить, допустимое ли это значение. Если да, то оно сохраняется в словаре context.user_data[] . Продолжайте устанавливать значения для переменной STATE и задавать следующие вопросы.

Когда зададите последний вопрос и будете знать день рождения, создайте переменную даты и сохраните ее в context.user_data[] словаре.

Если пользователь вводит недопустимое значение, то получает ответ, что оно неверно. Значение переменной STATE не меняется, поэтому пользователь застревает на этом вопросе, пока не ответит правильно.

Получить новые знания в языке программирования Python вам могут помочь специальные курсы от Hillel и ШАГ . Получите качественный уровень знаний от практикующих специалистов.

Создание команды

Теперь нужно обработать команду /biorhythm .

Добавьте новый обработчик команд в функцию main() .

Напишите функцию расчета биоритма:

# This function is called when the /biorhythm command is issued def biorhythm(update, context): user_biorhythm = calculate_biorhythm( context.user_data['birthday']) update.message.reply_text(f"Phisical: ") update.message.reply_text(f"Emotional: ") update.message.reply_text(f"Intellectual: ") def calculate_biorhythm(birthdate): today = datetime.date.today() delta = today - birthdate days = delta.days phisical = math.sin(2*math.pi*(days/23)) emotional = math.sin(2*math.pi*(days/28)) intellectual = math.sin(2*math.pi*(days/33)) biorhythm = <> biorhythm['phisical'] = int(phisical * 10000)/100 biorhythm['emotional'] = int(emotional * 10000)/100 biorhythm['intellectual'] = int(intellectual * 10000)/100 biorhythm['phisical_critical_day'] = (phisical == 0) biorhythm['emotional_critical_day'] = (emotional == 0) biorhythm['intellectual_critical_day'] = (intellectual == 0) return biorhythm

В примере представлены две разные функции: одна для обработки команды, а другая для расчета биоритма. Таким образом удается разделить ответственность этих функций.

Полный код бота

# mastrobot_example2.py import datetime import math from telegram.ext import Updater, CommandHandler, MessageHandler, Filters STATE = None BIRTH_YEAR = 1 BIRTH_MONTH = 2 BIRTH_DAY = 3 # function to handle the /start command def start(update, context): first_name = update.message.chat.first_name update.message.reply_text(f"Hi , nice to meet you!") start_getting_birthday_info(update, context) def start_getting_birthday_info(update, context): global STATE STATE = BIRTH_YEAR update.message.reply_text( f"I would need to know your birthday, so tell me what year were you born in. ") def received_birth_year(update, context): global STATE try: today = datetime.date.today() year = int(update.message.text) if year > today.year: raise ValueError("invalid value") context.user_data['birth_year'] = year update.message.reply_text( f"ok, now I need to know the month (in numerical form). ") STATE = BIRTH_MONTH except: update.message.reply_text( "it's funny but it doesn't seem to be correct. ") def received_birth_month(update, context): global STATE try: today = datetime.date.today() month = int(update.message.text) if month > 12 or month < 1: raise ValueError("invalid value") context.user_data['birth_month'] = month update.message.reply_text(f"great! And now, the day. ") STATE = BIRTH_DAY except: update.message.reply_text( "it's funny but it doesn't seem to be correct. ") def received_birth_day(update, context): global STATE try: today = datetime.date.today() dd = int(update.message.text) yyyy = context.user_data['birth_year'] mm = context.user_data['birth_month'] birthday = datetime.date(year=yyyy, month=mm, day=dd) if today - birthday < datetime.timedelta(days=0): raise ValueError("invalid value") context.user_data['birthday'] = birthday STATE = None update.message.reply_text(f'ok, you born on ') except: update.message.reply_text( "it's funny but it doesn't seem to be correct. ") # function to handle the /help command def help(update, context): update.message.reply_text('help command received') # function to handle errors occured in the dispatcher def error(update, context): update.message.reply_text('an error occured') # function to handle normal text def text(update, context): global STATE if STATE == BIRTH_YEAR: return received_birth_year(update, context) if STATE == BIRTH_MONTH: return received_birth_month(update, context) if STATE == BIRTH_DAY: return received_birth_day(update, context) # This function is called when the /biorhythm command is issued def biorhythm(update, context): print("ok") user_biorhythm = calculate_biorhythm( context.user_data['birthday']) update.message.reply_text(f"Phisical: ") update.message.reply_text(f"Emotional: ") update.message.reply_text(f"Intellectual: ") def calculate_biorhythm(birthdate): today = datetime.date.today() delta = today - birthdate days = delta.days phisical = math.sin(2*math.pi*(days/23)) emotional = math.sin(2*math.pi*(days/28)) intellectual = math.sin(2*math.pi*(days/33)) biorhythm = <> biorhythm['phisical'] = int(phisical * 10000)/100 biorhythm['emotional'] = int(emotional * 10000)/100 biorhythm['intellectual'] = int(intellectual * 10000)/100 biorhythm['phisical_critical_day'] = (phisical == 0) biorhythm['emotional_critical_day'] = (emotional == 0) biorhythm['intellectual_critical_day'] = (intellectual == 0) return biorhythm def main(): TOKEN = "insert here your token and don't share it with anyone!" # create the updater, that will automatically create also a dispatcher and a queue to # make them dialoge updater = Updater(TOKEN, use_context=True) dispatcher = updater.dispatcher # add handlers for start and help commands dispatcher.add_handler(CommandHandler("start", start)) dispatcher.add_handler(CommandHandler("help", help)) # add an handler for our biorhythm command dispatcher.add_handler(CommandHandler("biorhythm", biorhythm)) # add an handler for normal text (not commands) dispatcher.add_handler(MessageHandler(Filters.text, text)) # add an handler for errors dispatcher.add_error_handler(error) # start your shiny new bot updater.start_polling() # run the bot until Ctrl-C updater.idle() if __name__ == '__main__': main()

Пришло время проверить его:

telegrem bot

Поздравляем! Telegram-бот на Python полностью готов.

Бот, созданный для примера, был сохранен. Его можно протестировать по имени пользователя @mastro35_mastrobot.

За границей Hello World: полный гайд по разработке Telegram ботов с помощью Python и Aiogram 3. Часть 1

Захотев однажды научиться разрабатывать ботов для Telegram на языке программирования Python, я просто зашёл в Яндекс и вбил что-то вроде «telegram бот на python для новичков» и нашёл казалось бы огромное множество гайдов и туториалов. Однако копнув немного глубже стало понятно, что большая часть гайдов заканчивается на прикреплении клавиатур к сообщениям, или ещё хуже, на написании эхо-бота.

Пришлось копаться в документации, шерстить форумы и учиться на примерах кода с GitHub. Этот гайд создан как полное руководство по разработке полноценного Telegram бота для работы с нейросетями, такими как ChatGPT и Dall-e, начиная установкой IDE и получением токена и заканчивая подключением оплаты, базы данных и загрузки бота на сервер.

Я считаю что гайд будет полезен прежде всего тем, кто уже пробовал разобраться в теме и имеет базовые знания. Чтобы гайд был полезным необходимо иметь базовые знания в Python, всё остальное вы можете изучить в процессе. Продвинутым разработчикам ботов большая часть будет знакома и вряд ли принесёт пользу, однако есть шанс, что и вы найдёте для себя что-то полезное. Жду любую конструктивную критику как по коду и его стилю, так и по изложению.

Что мы получим в итоге?

В конце гайда у нас получится полностью функционирующий бот, с админкой, оплатой, базой данных, реферальной программой и подключенным API OpenAI. По мере выхода статей код будет появляться в репозитории на GitHub.

Используемые технологии

Будут использованы следующие технологии:

  • VS Code (или любой другой удобный редактор или IDE)
  • Python
  • Aiogram 3
  • PostgreSQL
  • API OpenAI

Подготовка окружения

Разработка любой программы начинается с подготовки среды, так что приступим. Для начала устанавливаем VS Code или любую другую вашу любимую IDE или редактор кода. Скачиваем установщик с сайта, запускаем, устанавливаем. По умолчанию среда уже готова к работе, но рекомендую установить дополнительные расширения для Python, а также по желанию темы и другие плюшки.

Конечно же надо установить сам Python, но раз вы читаете это, то уверен, что либо уже сделали это, либо разберётесь сами. Скажу лишь, что использую версию 3.10, однако код также должен работать на версиях Python 3.8 и выше.

В VS Code переходим на вкладку Git, скачиваем и устанавливаем Git. Далее инициализируйте репозиторий и желательно опубликуйте его на GitHub (для удобства дальнейшей работы), это можно сделать прямо из VS Code.

Создание и публикация репозитория на GitHub

После этого создадим виртуальное окружение, чтобы не засорять пакетами глобальyю среду. Подробнее про виртуальные окружения и преимущества их использования можете почитать здесь. Открываем палитру команд (Ctrl-Shift-P на Windows) и запускаем команду Python: Create Environment .

Далее выбираем venv и интерпретатор Python. Чтобы активировать виртуальное окружение, в терминале выполните команду .\.venv\Scripts\activate . Также выберите интерпретатор Python по умолчанию.

Выбранный интерпретатор должен находиться в папке .venv

Выбор интерпретатора по умолчанию

Теперь пришло время установить все используемые библиотеки. Их список вы можете найти у меня на github. Там же я буду выкладывать весь код по мере выхода статей. Если вы скачали файл, то установить библиотеки можно командой:

pip install -r requirements.txt

Обратите внимание что мы будем использовать aiogram версии 3, который ещё находится в бета-тестировании, 3 версия НЕ совместима с предыдущими, так что не забывайте об этом.

Следующий шаг — установка PostgreSQL. Сама установка не является чем-то сложным, поэтому не будем её подробно рассматривать. Для более удобной работы с базами данных можете установить графический клиент, такой как pgAdmin (идущий в комплекте с PostgreSQL), DBeaver или Navicat, самый удобный и используемый мною каждый день в работе (имеет бесплатную пробную версию).

На этом настройка окружения завершена, можно приступать к созданию структуры бота.

Создание структуры

Наш бот будет разделён на несколько логических частей — файлов. Можно писать весь код в одном файле - он будет также работать, однако отладка и поиск нужной функции или класса станет сущим адом.

Файловая структура нашего бота:

  • main.py — точка входа, код запуска бота и инициализации всех остальных модулей
  • config.py — файл со всеми конфигурационными параметрами, такими как токен бота и данные подключения к БД. Хранение настроек в Python-файле является не самой лучшей практикой, однако если настройки меняются очень редко, то такой способ является самым простым. Можно также хранить настройки в переменных окружения или специальных файлах (ini, json) и через config.py лишь предоставлять абстракцию данных, однако в этом боте будет использован самый простой способ
  • db.py — функции подключения и работы с базой данных. Данный файл будет являться абстракцией базы данных от основного кода
  • text.py — все тексты, используемые ботом. В этом файле будут лежать все приветствия, сообщения об ошибках и другие текстовые данные для бота. Хранение текста в Python-файле также является не лучшей практикой, так как изменить тексты можно только через код, однако тексты меняются не так часто (чаще всего никогда), поэтому снова пойдём самым простым путём
  • kb.py — все клавиатуры, используемые ботом. В этом файле будут находиться абсолютно все клавиатуры, как статические, так и динамически генерируемые через функции
  • middlewares.py — название файла говорит само за себя. В этом файле будут лежать все используемые мидлвари (их будет всего две)
  • states.py — будет хранить вспомогательные классы для FSM (машины состояний), а также фабрики Callback Data для кнопок Inline клавиатур
  • utils.py — различные функции. В этом файле будут лежать функции для рассылки, генерации текста и изображений через API и другие
  • handlers.py — основной файл, в котором будет содержать почти весь код бота. Будет состоять из функций-обработчиков с декораторами (фильтрами)
  • admin.py — обработчики событий, клавиатуры, классы и весь остальной код админки бота. Опять же если придерживаться лучших практик, стоило бы вынести это в отдельную папку, в которой уже создать модули клавиатур, текстов, хэндлеров (обработчиков) и всего остального. Наша админка будет иметь базовый функционал, поэтому реализуем всё в одном файле

В итоге ваша папка должна выглядеть так:

Получение токена

На эту тему написано настолько много материала, что крайне не хочется дублировать его, поэтому дам краткую инструкцию по получению токена:

  1. Запустите бота BotFather
  2. Создайте бота командой /newbot
  3. Следуя указаниям бота введите все данные, типа названия
  4. Скопируйте токен и вставьте его в переменную BOT_TOKEN в файле config.py

Можно также произвести настройку бота в BotFather, к примеру настроить описание, аватарку и другие параметры.

Пишем первый код!

Теперь, когда все подготовительные действия сделаны, можем приступить к написанию кода. Мы не будем писать эхо-бота, а сразу перейдём к чему-то более полезному — бот, отправляющий пользователю его ID.

В файле main.py пишем следующий код:

import asyncio import logging from aiogram import Bot, Dispatcher from aiogram.enums.parse_mode import ParseMode from aiogram.fsm.storage.memory import MemoryStorage import config from handlers import router async def main(): bot = Bot(token=config.BOT_TOKEN, parse_mode=ParseMode.HTML) dp = Dispatcher(storage=MemoryStorage()) dp.include_router(router) await bot.delete_webhook(drop_pending_updates=True) await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types()) if __name__ == "__main__": logging.basicConfig(level=logging.INFO) asyncio.run(main())

Сначала мы импортируем все нужные нам классы и модули:

  • asyncio — для асинхронного запуска бота
  • logging — для настройки логгирования, которое поможет в отладке
  • aiogram — основной модуль библиотеки aiogram, из которого мы импортируем классы Bot и Dispatcher
  • aiogram.enums.parse_mode — содержит настройки разметки сообщений (HTML, Markdown)
  • aiogram.fsm.storage.memory — хранилища данных для состояний пользователей
  • config — настройки бота, пока что только токен
  • handlers — пока пустой, но скоро мы напишем в нём функционал нашего бота

Затем мы объявляем функцию main() , в которой будет запускаться бот. Далее мы создаём объект бота с нашим токеном. Обратите внимание на параметр parse_mode , он отвечает за используемую по умолчанию разметку сообщений. Мы используем HTML, чтобы избежать проблем с экранированием символов. Затем мы создаём объект диспетчера, параметр storage=MemoryStorage() говорит о том, что все данные бота, которые мы не сохраняем в БД (к примеру состояния), будут стёрты при перезапуске. Этот вариант является оптимальным, так как хранение состояний диспетчера требуется редко.

Строка dp.include_router(router) подключает к нашему диспетчеру все обработчики, которые используют router, их вы увидите в следующем файле. Строка await bot.delete_webhook(drop_pending_updates=True) удаляет все обновления, которые произошли после последнего завершения работы бота. Это нужно, чтобы бот обрабатывал только те сообщения, которые пришли ему непосредственно во время его работы, а не за всё время. следующая строка запускает бота. Однако если сейчас попытаться запустить файл, бот не запустится, а выдаст исключение, так как файл handlers.py пустой. Давайте исправим это.

from aiogram import Router router = Router()

Давайте запустим и проверим, как работает наш бот. После запуска вы должны увидеть следующий вывод в лог:

Логи при успешном запуске бота

Это означает, что наш бот запущен и слушает обновления, однако пока что он ничего не делает, так как мы не добавили ни одного обработчика. Давайте исправим это, написав в файле handlers.py следующий код:

from aiogram import types, F, Router from aiogram.types import Message from aiogram.filters import Command router = Router() @router.message(Command("start")) async def start_handler(msg: Message): await msg.answer("Привет! Я помогу тебе узнать твой ID, просто отправь мне любое сообщение") @router.message() async def message_handler(msg: Message): await msg.answer(f"Твой ID: ")

Сначала мы импортируем все необходимое из aiogram. После этого создаём роутер для дальнешей привязки к нему обработчиков. Затем мы объявили две функции-обработчика событий, а также назначили им фильтры. Рассмотрим их подробнее. Декоратор @router.message означает, что функция является обработчиком входящих сообщений. Command("start") запускает обработчик только если входящее сообщение — команда /start . Далее мы объявляем саму функцию и в её теле отвечаем пользователю на сообщение текстом приветствия. Если мы имеем доступ к объекту сообщения, то всегда можем отправить в тот же чат любое сообщение методом msg.answer("Text") , что является аналогом await bot.send_message(msg.chat.id, "Text") .

Второй обработчик реагирует на все сообщения, так как у него не задан ни один фильтр. В теле функции мы снова отвечаем пользователю сообщением и подставляем в него значение msg.chat.id . Запустим снова код и посмотрим на результат. Обратите внимание, что запускать надо не handlers.py, а main.py, так как именно он является точкой входа в нашу программу. В консоли снова появится аналогичное сообщение об успешном запуске бота, можно перейти в чат с ботом и отправить ему команду /start .

Мы должны получить следующие ответы от бота:

Бот работает исправно

Если у вас получилось также, то поздравляю, вы настроили всё правильно и запустили минимально рабочего бота.

Заключение

В следующей части мы сделаем меню бота и подключим к боту API OpenAI. Так как это моя первая статья на Хабре, жду любой конструктивной критики в комментариях по оформлению, стилю изложения и разумеется коду.

UPD: Обновил код, удалил использование глобального объекта диспетчера, заменил на роутер. Спасибо более опытным коллегам за подсказку

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *