Подготовка рабочего места в Windows и Linux. Virtual Environment (venv). Ответы на вопросы
Весь текст ниже появился как попытка дать универсальный ответ на те вопросы, которые дорогие читатели присылали и продолжают присылать раз за разом. Здесь не будет кода, связанного с ботами напрямую, а лишь советы по организации процесса написания. И, конечно же, не нужно воспринимать это как истину в последней инстанции, напротив, выбирайте те инструменты и подходы к разработке, которые лично вам кажутся удобными. Важная деталь: текст написан в конце 2019 года. Достаточно вступительных слов, поехали!
Предположим, вы уже немного знаете язык Python 3 (забудьте про Python 2.7, он мёртв), умеете писать простенькие программы и хотите взяться за разработку ботов. Где это делать? В чём писать код? Как правило, у большинства начинающих программистов основной операционкой используется Microsoft Windows. С неё и начнём, но сперва…
Virtual environment
Вы когда-нибудь пользовались VirtualBox? Например, чтобы «пощупать» другие операционные системы или просто установить какой-нибудь подозрительный софт. Python Virtual environment (далее — venv) чем-то напоминает «виртуалку». При его использовании создаётся копия выбранного интерпретатора Питон, а все устанавливаемые модули хранятся изолированно от общесистемных, тем самым, упрощается их обновление, удаление или изменение. Часто venv позволяет избежать ошибок, связанных с обратной совместимостью некоторых библиотек, а также обойтись без конфликтов с системными модулями. Работа с venv будет подробнее описана ниже в разделе Linux, но использовать его мы будем везде.
Windows
Первым делом, разумеется, нужно скачать сам интерпретатор Python. На момент написания этого текста актуальной версией является Python 3.8.1. В качестве каталога установки я рекомендую использовать что-то простое, вроде C:\Python38. Где писать код — личное дело каждого, конечно, но я всё же рекомендую использовать специальную среду разработки под названием PyCharm Community Edition. Бесплатной версии (та самая Community) более чем достаточно. После установки и первичной настройки выберите пункт File -> New Project. Укажите имя вашего первого проекта, а ниже укажите «New environment using virtualenv», ниже в качестве интерпретатора путь к python.exe каталога с Питоном (например, C:\Python38\python.exe).
Создание нового проекта в PyCharm
После запуска откройте вкладку Terminal в левом нижнем углу и установите библиотеку pytelegrambotapi (не telebot!). Для любителей тыкать мышкой есть более запутанный вариант: File -> Settings -> Project -> Project Interpreter -> кнопка «+» в правой части экрана.
Установка библиотеки через терминал в PyCharm
Прекрасно, теперь начинайте творить! Создайте первый файл с исходником, нажав правой кнопкой мышки по имени проекта в списке файлов, затем New и Python File.
Создание нового файла кода в PyCharm
Запустить код можно, выбрав сверху пункт Run, затем снова Run…, но с многоточием, и затем выбрав созданный ранее файл.
Как залить файлы на сервер?
Для копирования файлов на удалённый сервер (обычно там стоит Linux), я использую замечательную бесплатную программу WinSCP, причём в ней присутствует режим автоматической синхронизации файлов, чтобы при любом изменении в локальном каталоге обновлялось содержимое на удалённой машине, избавляя вас от необходимости копировать всё вручную.
Скриншот программы WinSCP
При помощи WinSCP можно даже просто подключиться к серверу и подправить файл «на лету», не забудьте только потом перезапустить бота!
Linux
Если Linux у вас используется вместо Windows, то работа с PyCharm будет точно такой же, поэтому второй раз писать не имеет смысла. Далее рассмотрен процесс запуска в терминале на удалённом сервере. Прекрасно, вы написали бота и хотите где-то его запустить. Например, арендовали сервер у Scaleway/DigitalOcean/AWS/etc. Запустили сервер, подключились к нему по SSH, а там чёрный экран и терминальная Linux-сессия. Во-первых, давайте посмотрим, какой интерпретатор у нас выбран по умолчанию, введя команду python3 .
Хорошим правилом будет иметь на сервере ровно ту же версию Python, что и на своей локальной машине, во избежание различных неприятностей. Если версия на сервере ниже 3.7 и/или ниже той, что установлена локально, лучше всего будет установить её отдельно. Очень рекомендую вот эту статью, по которой я для себя написал простой скрипт для автоматизации рутинных действий. Итак, интерпретатор установлен, теперь пора создать каталог, куда положим файлы бота. Выполните по очереди следующие команды:
mkdir mybot cd mybot python3.7 -m venv venv source venv/bin/activate pip install pytelegrambotapi deactivate
В результате должно получиться примерно то же самое, что на скриншоте ниже, с той лишь разницей, что я прервал процесс установки библиотеки для читабельности. Обратите внимание, что после подгрузки файла venv/bin/activate, перед названием пользователя и текущего каталога появится приписка (venv), означающая, что мы «вошли» в виртуальное окружение и устанавливаем библиотеки именно в него.
Создание venv в Linux-терминале
Что произошло выше? Во-первых, мы создали каталог с названием mybot и перешли в него. Во-вторых, мы использовали Python версии 3.7 (в вашем случае это может быть не так), чтобы создать виртуальное окружение в подкаталоге venv. В-третьих, мы «активировали» venv и выполнили в нём нужную нам команду. Внутри venv команды pip и python точно знают, к какому именно интерпретатору они относятся, поэтому путаницы вроде «я установил библиотеку для Python 3.5, а запускаю из-под Python 3.7» попросту не будет. Наконец, в-четвёртых, мы деактивировали venv, поскольку он напрямую нам больше не нужен. Чтобы сделать жизнь ещё приятнее, давайте настроим автозагрузку бота, чтобы при возникновении ошибок или при перезапуске сервера он вновь запускался, избавляя нас от необходимости постоянно проверять всё вручную. Для этого мы воспользуемся подсистемой инициализации systemd, которая всё больше распространена в современных Linux-дистрибутивах. Прежде, чем описать службу systemd, откройте главный файл с ботом, в котором прописан его запуск и добавьте в качестве первой строки следующий код: #!venv/bin/python
Сохраните файл, закройте его и выполните команду chmod +x имяфайласботом.py , чтобы сделать его исполняемым. Теперь создайте файл mybot.service, и скопируйте туда следующий текст:
[Unit] Description=MY BOT After=network.target [Service] Type=simple WorkingDirectory=/home/user/mybot ExecStart=/home/user/mybot/bot.py KillMode=process Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
Отредактируйте поля Description , WorkingDirectory и ExecStart , сохраните и закройте файл. Далее скопируйте его в каталог /etc/systemd/system , введя свой пароль при необходимости (если сидите не из-под рута, что правильно, то ваш юзер должен иметь возможность выполнять команды от имени sudo). Затем выполните следующие команды для включения автозагрузки и запуска бота (опять-таки, требуются права суперпользователя):
systemctl enable mybot.service systemctl restart mybot
Наконец, проверьте состояние вашего бота командой systemctl status mybot . Его статус должен быть Active (running) зелёного цвета (если поддерживается разноцветный режим).
Проверка статуса бота через systemd
Как редактировать файлы на сервере?
Если что-то нужно подправить небольшое, то неплохим вариантом остаётся старое доброе подключение по SSH и использование редакторов вроде micro, nano или даже vim с emacs. Но если вдруг у вас в качестве локальной машины применяется Linux, то крайне рекомендую редактор Visual Studio Code (https://code.visualstudio.com) с дополнением Remote-SSH. В этом случае, вы сможете прямо в VS Code открывать каталоги на сервере и редактировать файлы в удобном окружении и с подсветкой синтаксиса. К сожалению, насколько мне известно, расширение Remote-SSH не работает в Windows, но впоследствии этот недочёт будет устранён.
Ответы на часто задаваемые вопросы (FAQ)
Хочу научиться писать ботов. С чего мне начать?
Прежде всего, пожалуйста, изучите хотя бы немного сам язык Python. Он довольно простой, но перед созданием ботов стоит понять азы. Конкретнее: переменные, циклы, функции, классы, обработка исключений, работа с файлами и файловой системой.
Можно ли писать ботов на телефоне?
Да кто ж вам запретит-то? Но лучше от этого никому не будет, поверьте. Будет трудно, неудобно и контрпродуктивно. Используйте нормальный компьютер.
[pyTelegramBotAPI] Ошибка AttributeError: module ‘telebot’ has no attribute ‘TeleBot’!
На 99% уверен, что вы установили библиотеку telebot вместо pytelegrambotapi. С учётом всего вышесказанного проще создать новое окружение venv, перенести туда нужные файлы и установить именно pytelegrambotapi, при этом в исходниках должно остаться import telebot.
Как мне держать бота запущеным в Windows?
Запустите бота в PyCharm, не закрывайте приложение и не выключайте комп. Почти шутка. По-моему, Windows — не самая лучшая операционка для подобных вещей, проще арендовать сервер у европейских провайдеров, заодно не будет геморроя с варварами из Российского Консорциума Неадекватов.
Библиотека pyTelegramBotAPI не поддерживает новые фичи Bot API!
К сожалению, упомянутая библиотека в 2019 году развивалась гораздо медленнее, чем хотелось. Если вы уже чувствуете себя уверенным ботописателем, подумайте о переходе на альтернативы вроде aiogram.
В завершение хочется напомнить, что если у вас возникли замечания, предложения или вопросы, вы всегда можете открыть issue на Github или прийти к нам в чатик.
Урок №2. Тело telegram бота
В этом уроке мы научимся создавать проект, устанавливать библиотеки. Также мы создадим отдельный файл, который не пушиться на сервер и хранится локально на нашем компьютере, и где будут храниться наш секретный токен, пароли и другое. Также мы подключим нашего telegram бота к python для дальнейшей работы с ним.
1.2K показов
1.7K открытий
Создание проекта.
Я буду использовать программу PyCharm. В ней вам необходимо создать новый проект с любым названием(File -> New project -> Create). Я назову проект pythontelegram.Также внутри проекта необходимо создать новый файл для написания кода, название может быть любым(внутри проекта нажимаем правой кнопкой мыши -> New -> Python File)
Как установить telebot
В чем проблема активировать необходимый енвайрмент и установить библиотеку с помощью pip?
conda activate my_env pip install pytelegrambotapi conda deactivate
Отслеживать
ответ дан 13 дек 2019 в 9:39
Alex Zaharchuk Alex Zaharchuk
843 5 5 серебряных знаков 13 13 бронзовых знаков
pip3 install pytelegrambotapi pip install pytelegrambotapi
Отслеживать
ответ дан 28 фев в 20:21
hackerson7 hackerson7
1 3 3 бронзовых знака
добро пожаловать на stack overflow на русском! пожалуйста, постарайтесь оставлять чуть более развёрнутые ответы. дополнить ответ можно, нажав править. в частности, поясните, чем ваш ответ принципиально отличается от того, который был дан три года назад.
Библиотека python-telegram-bot в Python
Пакет python-telegram-bot содержит ряд высокоуровневых классов, которые делают разработку ботов простой и понятной. Эти классы содержатся в модуле telegram.ext . Он совместим с версиями Python 3.7+. Пакет python-telegram-bot также может работать с PyPy3 (официально не поддерживается), хотя раньше было много проблем.
Внимание! Пакеты python-telegram-bot версии 13.x будут придерживаться многопоточной парадигмы программирования (на данный момент актуальна версия 13.15). Пакеты версий 20.x и новее предоставляют чистый асинхронный Python интерфейс для Telegram Bot API. Дополнительно смотрите основные изменения в пакете python-telegram-bot версии 20.x.
Установка пакета python-telegram-bot в виртуальное окружение:
Пакет python-telegram-bot пытается использовать как можно меньше сторонних зависимостей. Однако для некоторых функций использование сторонней библиотеки более разумно, чем повторная реализация функциональности. Поскольку эти функции являются необязательными, соответствующие сторонние зависимости не устанавливаются по умолчанию. При установке пакета их можно перечислены как необязательные зависимости. Это позволяет избежать ненужных конфликтов зависимостей для пользователей, которым не нужны дополнительные функции.
Единственная необходимая зависимость — это httpx ~= 0.23.3 для telegram.request.HTTPXRequest , сетевого бэкенда по умолчанию.
# создаем виртуальное окружение, если нет $ python3 -m venv .telegram --prompt TelegramBot # активируем виртуальное окружение $ source .telegram/bin/activate # ставим последнюю многопоточную версию python-telegram-bot (TelegramBot):~$ python3 -m pip install python-telegram-bot==13.15 -U # или установка асинхронной версии со всеми зависимостями (TelegramBot):~$ python3 -m pip install python-telegram-bot[all] -U # устанавливает все необязательные зависимости расширения `telegram.ext` (TelegramBot):~$ python3 -m pip install python-telegram-bot[ext] -U # При установке зависимости можно перечислить отдельно как: # python-telegram-bot[rate-limiter,webhooks,callback-data,job-queue]
- pip install python-telegram-bot[passport] — устанавливает библиотеку cryptography . Используется для функции, связанных с Telegram Passport.
- pip install python-telegram-bot[socks] — устанавливает httpx[socks] . Используется для работы с сервером Socks5.
- pip install python-telegram-bot[http2] — устанавливает httpx[http2] . Используется для работы с сервером HTTP/2.
- pip install python-telegram-bot[rate-limiter] — устанавливает aiolimiter . Используется для работы с telegram.ext.AIORateLimiter .
- pip install python-telegram-bot[webhooks] — устанавливает библиотеку tornado . Используется для работы с telegram.ext.Updater.start_webhook / telegram.ext.Application.run_webhook .
- pip install python-telegram-bot[callback-data] — устанавливает библиотеку cachetools . Используется для работы с произвольными callback_data .
- pip install python-telegram-bot[job-queue] — устанавливает библиотеку APScheduler и применяет pytz , где pytz является зависимостью APScheduler . Используется для работы с telegram.ext.JobQueue .
Чтобы установить несколько необязательных зависимостей, разделите их запятыми, например. pip install python-telegram-bot[socks,webhooks] .
Пакет python-telegram-bot в основном будет разбираться на примерах. Содержание, обзорного/вводного материала по библиотеке ниже. Меню с материалами по всему разделу — справа.
Высокоуровневый интерфейс пакета python-telegram-bot построен поверх чистой реализации Telegram Bot API и находится в подмодуле расширений telegram.ext . Он предоставляет простой в использовании интерфейс и снимает с программиста некоторую работу.
Для выполнения примеров требуется сгенерировать токен доступа к API. Для этого необходимо пообщаться с @BotFather и выполнить несколько простых шагов, описанных в разделе Команды и оповещения @BotFather в Telegram.
Содержание:
- Многопоточный модуль расширения telegram.ext (версия 13.x);
- Создание Telegram bot, шаг за шагом (версия 13.x);
- Режим встроенных запросов (версия 13.x);
- Весь код созданного многопоточного бота.
- Создание асинхронного Telegram bot, шаг за шагом (версия 20.x);
- Режим встроенных запросов (версия 20.x);
Многопоточный модуль расширения telegram.ext (версия 13.x).
Модуль расширений telegram.ext состоит из нескольких классов, но два наиболее важных — это telegram.ext.Updater и telegram.ext.Dispatcher .
Класс Updater постоянно слушает сервер Telegram, получает новые сообщения и передает их классу Dispatcher . Если создать объект Updater , то он автоматически создаст Dispatcher и свяжет их вместе с очередью. Затем в объекте Dispatcher можно зарегистрировать обработчики разных типов, которые будут сортировать полученные объектом Updater сообщения. Поступающие сообщения будут обрабатываться в соответствии с зарегистрированными обработчиками и передавать их в функцию обратного вызова, которую необходимо определить.
Еще нужно знать и понимать, что экземпляр Updater реализует все методы класса telegram.Bot (API Telegram), которые будут связаны с данным Updater . У экземпляра Dispatcher , в свою очередь, есть так называемый контекст context , который, при регистрации любого обработчика сообщений передается в функцию обратного вызова этого обработчика (кстати в нее так же передается updater ). Так вот, у этого контекста то же есть экземпляр класса telegram.Bot , только он связан с конкретным сообщением, которое попало в эту функцию обратного вызова.
Каждый обработчик является экземпляром подкласса класса telegram.ext.Handler . Пакет python-telegram-bot предоставляет классы обработчиков почти на все стандартные случаи, но если нужно что-то конкретное, то можно создать собственный обработчик, наследуясь от класса Handler .
Создание Telegram bot, шаг за шагом.
Во-первых, нужно создать объект Updater . В коде ниже замените константу TOKEN на API-токен вашего бота. Для более быстрого доступа к Dispatcher , в который Updater посылает сообщение, можно создать его отдельно:
from telegram.ext import Updater TOKEN = 'Замените эту строку на token, полученный от @BotFather' # получаем экземпляр `Updater` updater = Updater(token=TOKEN, use_context=True) # получаем экземпляр `Dispatcher` dispatcher = updater.dispatcher
Примечание. Аргумент use_context=True (по умолчанию False ) — это специальный аргумент, необходимый только для python-telegram-bot меньше версии 12.x. Это обеспечивает лучшую обратную совместимость со старыми версиями и дает пользователям время для обновления. Начиная с версии 13.x, значение аргумента use_context=True используется по умолчанию (указывать его не нужно).
Чтобы знать, когда и почему что-то не работает должным образом, настроим модуль ведения журнала логов:
import logging logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
Примечание. если хотите узнать больше об обработке исключений с python-telegram-bot , прочтите подраздел об «Обработка исключений».
Теперь определим функцию, которая должна обрабатывать определенный тип сообщения, отправленных боту:
# Обратите внимание, что из обработчика в функцию # передаются экземпляры `update` и `context` def start(update, context): # `bot.send_message` это метод Telegram API # `update.effective_chat.id` - определяем `id` чата, # откуда прилетело сообщение context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!")
- аргументы update и context передаются автоматически;
- update — это объект связанный с экземпляром Update который присылает и отправляет все сообщения. Через него можно получить доступ к экземпляру telegram.Bot() как update.bot ;
- context — это объект связанный с контекстом обработанного сообщения. Через него также можно получить доступ к экземпляру telegram.Bot() как context.bot .
Цель состоит в том, чтобы эта функция вызывалась каждый раз, когда бот получает сообщение с серверов Telegram, содержащее команду /start . Для этого можно использовать класс CommandHandler (один из предоставленных подклассов Handler ) и зарегистрировать его в Dispatcher :
# импортируем обработчик CommandHandler, # который фильтрует сообщения с командами from telegram.ext import CommandHandler # говорим обработчику, если увидишь команду `/start`, # то вызови функцию `start()` start_handler = CommandHandler('start', start) # добавляем этот обработчик в `dispatcher` dispatcher.add_handler(start_handler)
И это все, что нужно! Для запуска бота дописываем команду:
# говорим экземпляру `Updater`, # слушай сервера Telegram. updater.start_polling()
Начните чат со своим ботом и введите команду /start — если все пойдет хорошо, он ответит.
Созданный бот может отвечать только на команду /start . Добавим еще один обработчик, который прослушивает обычные сообщения. Для этого используем класс MessageHandler — другой подкласс Handler , для вывода всех текстовых сообщений:
# функция обратного вызова def echo(update, context): # добавим в начало полученного сообщения строку 'ECHO: ' text = 'ECHO: ' + update.message.text # `update.effective_chat.id` - определяем `id` чата, # откуда прилетело сообщение context.bot.send_message(chat_id=update.effective_chat.id, text=text) # импортируем обработчик `MessageHandler` и класс с фильтрами from telegram.ext import MessageHandler, Filters # говорим обработчику `MessageHandler`, если увидишь текстовое # сообщение (фильтр `Filters.text`) и это будет не команда # (фильтр ~Filters.command), то вызови функцию `echo()` echo_handler = MessageHandler(Filters.text & (~Filters.command), echo) # регистрируем обработчик `echo_handler` в экземпляре `dispatcher` dispatcher.add_handler(echo_handler)
С этого момента создаваемый бот должен обрабатывать все получаемые текстовые сообщения, а так же работать с командой /start , но не будет реагировать на любые другие команды (например, /your_command ) .
Примечание: как только новые обработчики добавляются в диспетчер, они сразу вступают в силу.
Примечание. Класс telegram.ext.Filters содержит ряд так называемых фильтров, которые фильтруют входящие сообщения по тексту, изображениям, обновлениям статуса и т. д. Любое сообщение, которое возвращает True хотя бы для одного из фильтров, переданных в MessageHandler , будет принято. Если необходимо, то можно написать свои собственные фильтры. Подробнее смотрите раздел «Все о фильтрации сообщений python-telegram-bot в Python».
Добавим боту другую функциональность и реализуем команду /caps , которая будет принимать какой-то текст в качестве аргумента и отвечать на него тем же текстом, только в верхнем регистре. Аргументы команды (например /caps any args ) будут поступать в функцию обратного вызова в виде списка [‘any’, ‘args’] , разделенного по пробелам:
def caps(update, context): # если аргументы присутствуют if context.args: # объединяем список в строку и # переводим ее в верхний регистр text_caps = ' '.join(context.args).upper() # `update.effective_chat.id` - определяем `id` чата, # откуда прилетело сообщение context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps) else: # если в команде не указан аргумент context.bot.send_message(chat_id=update.effective_chat.id, text='No command argument') context.bot.send_message(chat_id=update.effective_chat.id, text='send: /caps argument') # обработчик команды '/caps' caps_handler = CommandHandler('caps', caps) # регистрируем обработчик в диспетчере dispatcher.add_handler(caps_handler)
Примечание. Обратите внимание на использование context.args . Объект CallbackContext будет иметь много разных атрибутов в зависимости от того, какой обработчик используется.
Режим встроенных запросов.
Еще одна интересная особенность официального Telegram Bot API — это режим встроенных запросов к ботам. Помимо отправки команд в личных сообщениях или группах, пользователи могут взаимодействовать с ботом с помощью встроенных запросов. Если встроенные запросы включены, то пользователи могут вызвать бота, введя его имя @bot_username и запрос в поле ввода текста в любом чате. Запрос отправляется боту в обновлении. Таким образом, люди могут запрашивать контент у ботов в любом из своих чатов, групп или каналов, вообще не отправляя им никаких отдельных сообщений.
Если необходимо реализовать такую функциональность для своего бота, то сначала необходимо изменить конфигурацию в @BotFather , включив этот режим при помощи команды /setinline . Иногда требуется какое-то время, пока бот не зарегистрируется в качестве встроенного бота на вашем клиенте. Можно ускорить процесс, перезапустив приложение Telegram или иногда просто нужно немного подождать.
Здесь используется ряд новых типов:
from telegram import InlineQueryResultArticle, InputTextMessageContent def inline_caps(update, context): query = update.inline_query.query if not query: return results = list() results.append( InlineQueryResultArticle( id=query.upper(), title='Convert to UPPER TEXT', input_message_content=InputTextMessageContent(query.upper()) ) ) context.bot.answer_inline_query(update.inline_query.id, results) from telegram.ext import InlineQueryHandler inline_caps_handler = InlineQueryHandler(inline_caps) dispatcher.add_handler(inline_caps_handler)
Теперь бот может работать и через режим встроенных запросов.
Пользователи могут попытаться отправить боту команды, которые он не понимает, поэтому можно использовать обработчик MessageHandler с фильтром Filters.command , чтобы отвечать на все команды, которые не были распознаны предыдущими обработчиками.
def unknown(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.") unknown_handler = MessageHandler(Filters.command, unknown) dispatcher.add_handler(unknown_handler)
Примечание. Этот обработчик должен быть добавлен последним. Если его поставить первым, то он будет срабатывать до того, как обработчик CommandHandlers для команды /start увидит обновление. После обработки обновления функцией unknown() все дальнейшие обработчики будут игнорироваться.
Чтобы обойти такое поведение, можно передать в метод dispatcher.add_handler(handler, group) , помимо самой функции обработчика аргумент group со значением, отличным от 0. Аргумент group можно воспринимать как число, которое указывает приоритет обновления обработчика. Более низкая группа означает более высокий приоритет. Обновление может обрабатываться (максимум) одним обработчиком в каждой группе.
Остановить бота можно командой updater.stop() .
Примечание. Объект Updater запускается в отдельном потоке, это хорошо, если вы запускаете команды в интерпретаторе Python. Но если запустить скрипт с написанным ботом, то вероятно, удобнее будет останавливать бота, нажатием Ctrl + C , отправив сигнал процессу бота. Для этого, после запуска бота командой updater.start_polling() допишите в коде следующей строкой команду updater.idle() .
Весь код созданного бота:
# sample-bot.py from telegram import InlineQueryResultArticle, InputTextMessageContent from telegram.ext import Updater, CommandHandler from telegram.ext import MessageHandler, Filters, InlineQueryHandler TOKEN = 'Замените эту строку на token, полученный от @BotFather' updater = Updater(token=TOKEN) dispatcher = updater.dispatcher # функция обработки команды '/start' def start(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!") # функция обработки текстовых сообщений def echo(update, context): text = 'ECHO: ' + update.message.text context.bot.send_message(chat_id=update.effective_chat.id, text=text) # функция обработки команды '/caps' def caps(update, context): if context.args: text_caps = ' '.join(context.args).upper() context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps) else: context.bot.send_message(chat_id=update.effective_chat.id, text='No command argument') context.bot.send_message(chat_id=update.effective_chat.id, text='send: /caps argument') # функция обработки встроенного запроса def inline_caps(update, context): query = update.inline_query.query if not query: return results = list() results.append( InlineQueryResultArticle( id=query.upper(), title='Convert to UPPER TEXT', input_message_content=InputTextMessageContent(query.upper()) ) ) context.bot.answer_inline_query(update.inline_query.id, results) # функция обработки не распознных команд def unknown(update, context): context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.") # обработчик команды '/start' start_handler = CommandHandler('start', start) dispatcher.add_handler(start_handler) # обработчик текстовых сообщений echo_handler = MessageHandler(Filters.text & (~Filters.command), echo) dispatcher.add_handler(echo_handler) # обработчик команды '/caps' caps_handler = CommandHandler('caps', caps) dispatcher.add_handler(caps_handler) # обработчик встроенных запросов inline_caps_handler = InlineQueryHandler(inline_caps) dispatcher.add_handler(inline_caps_handler) # обработчик не распознанных команд unknown_handler = MessageHandler(Filters.command, unknown) dispatcher.add_handler(unknown_handler) # запуск прослушивания сообщений updater.start_polling() # обработчик нажатия Ctrl+C updater.idle()
Асинхронный модуль расширения telegram.ext (версия 20.x).
Асинхронный модуль расширений telegram.ext состоит из нескольких классов, но здесь самый важный — это telegram.ext.Application .
Класс Application отвечает за получение обновлений из очереди update_queue , где класс Updater постоянно получает новые обновления из Telegram и добавляет их в эту очередь. Если создать объект приложения с помощью ApplicationBuilder , то он автоматически создаст средство обновления и свяжет их вместе с помощью asyncio.Queues . Затем можно зарегистрировать обработчики различных типов в приложении, которое будут сортировать обновления, полученные средством обновления, в соответствии с зарегистрированными обработчиками и доставлять их в определенную функцию обратного вызова.
Каждый обработчик является экземпляром любого подкласса класса telegram.ext.BaseHandler . Библиотека предоставляет классы-обработчики практически для всех случаев использования, но если нужно что-то очень конкретное, то можете сами создать подкласс Handler .
Создание асинхронного Telegram bot, шаг за шагом.
Первое. В описании работы асинхронного бота, в коде будут комментироваться только отличительные моменты. Если что-то непонятно — смотрим сначала создание многопоточного бота.
Второе. для выполнения примеров создадим новый файл, например sync-bot.py . По ходу материала в этот файл будем добавлять новый код несколько раз. Другими словами, для краткости не будем повторять весь код создаваемого бота каждый раз, когда что-то добавляем.
И третье, самое важное. Для создания асинхронного бота (без наворотов) достаточно понимать и учитывать в коде следующие вещи:
- Все функции-обработчики становятся сопрограммами, т.е. добавляется асинхронный оператор async перед определением функции-обработчика.
- Внутри функции-обработчика появляется асинхронный оператор await , который ставится перед методами объектов context или update , ожидающие своей очереди для каких-то сетевых операций (например, отправить/ответить/изменить сообщение и т.д.). Объекты context или update передаются в качестве аргументов функциям-обработчикам. Важно! Здесь нужно четко понимать какие методы ожидают подключения к сети. Например, метод context.bot.send_message() или update.message.reply_text() явно хотят что-то отправить по сети в чат, следовательно перед ним ставиться await . А вот свойство update.message.chat_id или update.effective_chat.id просто извлекают значение chat_id из словаря, полученного во время фоновой операции Update и не являются сопрограммами — перед ними оператор await НЕ СТАВИТЬСЯ.
- Создание экземпляра приложения Telegram осуществляется вызовом одного класса ApplicationBuilder() или Application.builder() , а настройка (параметров по умолчанию) происходит вызовом цепочки методов этого экземпляра.
И так, вставим в файл следующий код:
import logging from telegram import Update from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler # настроим модуль ведения журнала логов logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) # определяем асинхронную функцию async def start(update, context): # ожидание отправки сообщения по сети - нужен `await` await context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!") if __name__ == '__main__': TOKEN = 'Замените эту строку на token, полученный от @BotFather' # создание экземпляра бота через `ApplicationBuilder` application = ApplicationBuilder().token(TOKEN).build() # создаем обработчик для команды '/start' start_handler = CommandHandler('start', start) # регистрируем обработчик в приложение application.add_handler(start_handler) # запускаем приложение application.run_polling()
Переварим написанный код, пройдемся по нему шаг за шагом.
import logging # настройки модуля ведения журнала logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO )
Вышеуказанный код предназначена для настройки модуля ведения журнала, чтобы знать, когда (и почему) что-то не работает должным образом:
TOKEN = 'Замените эту строку на token, полученный от @BotFather' # создание экземпляра бота через `ApplicationBuilder` application = ApplicationBuilder().token(TOKEN).build()
Здесь создается объект Application . Константу TOKEN необходимо заменить API-токеном своего бота.
Приложение само по себе ничего не делает. Чтобы добавить функциональность, необходимо сделать две вещи. Во-первых, определить функцию, которая должна обрабатывать определенный тип обновления:
async def start(update, context): await context.bot.send_message( chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!" )
- аргументы update и context передаются автоматически;
- update — это объект связанный с экземпляром Update который присылает и отправляет все сообщения. Через него можно получить доступ к экземпляру telegram.Bot() как update.bot ;
- context — это объект связанный с контекстом обработанного сообщения. Через него также можно получить доступ к экземпляру telegram.Bot() как context.bot .
Цель состоит в том, чтобы асинхронная функция start() вызывалась каждый раз, когда бот получает сообщение от сервера Telegram, содержащее команду /start . Для этого можно использовать один из обработчиков CommandHandler (подклассов Handler ) и зарегистрировать его в приложении:
from telegram.ext import CommandHandler # регистрируем обработчик команды 'start' start_handler = CommandHandler('start', start) # добавляем обработчик в приложение application.add_handler(start_handler)
И это все, что на данном этапе нужно. И наконец, строка application.run_polling() запускает бота, пока не поступит нажмете CTRL+C .
Теперь попробуем. В чат со своим ботом введем команду /start — если все прошло правильно, он ответит.
Теперь бот умеет отвечать только на команду /start . Добавим еще один обработчик, который прослушивает обычные сообщения. Для этого используем класс обработчика MessageHandler (подкласс Handler ) для отображения всех текстовых сообщений. Остановим запущенного бота ( CTRL+C ), определим новую функцию echo() , добавим ее в обработчик MessageHandler , а созданный обработчик зарегистрируем в приложении:
from telegram import Update from telegram.ext import filters, MessageHandler, ApplicationBuilder, CommandHandler, ContextTypes async def start(update, context): . async def echo(update, context): await context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text) if __name__ == '__main__': . # создаем обработчик текстовых сообщений, # которые будут поступать в функцию `echo()` echo_handler = MessageHandler(filters.TEXT & (~filters.COMMAND), echo) # регистрируем обработчик для `start()` application.add_handler(start_handler) # регистрируем обработчик для `echo()` application.add_handler(echo_handler) # запускаем приложение application.run_polling()
С этого момента бот должен повторять все получаемые им текстовые (некомандные) сообщения.
Примечание. Модуль filters содержит ряд так называемых фильтров, которые фильтруют входящие сообщения по тексту, изображениям, обновлениям статуса и многому другому. Любое сообщение, возвращающее True хотя бы для одного из фильтров, переданных в MessageHandler , будет принято. Можно написать свои собственные фильтры, если это необходимо.
Добавим боту некоторые реальные функции. Реализуем команду /caps , которая будет принимать некоторый текст в качестве аргумента и отвечать на него копией сообщения в верхнем регистре. Чтобы упростить задачу, будем получать аргументы команды в виде списка, разделенного пробелами, которые передаются команде /caps в функцию обратного вызова caps() :
. async def caps(update, context): text_caps = ' '.join(context.args).upper() await context.bot.send_message(chat_id=update.effective_chat.id, text=text_caps) if __name__ == '__main__': . # создаем новый обработчик для функции `caps()` caps_handler = CommandHandler('caps', caps) # здесь регистрируются созданные обработчики application.add_handler(start_handler) application.add_handler(echo_handler) # вот регистрация для функции `caps()` application.add_handler(caps_handler) # запускаем приложение application.run_polling()
Примечание. Обратите внимание на использование context.args . CallbackContext будет иметь несколько атрибутов, в зависимости от того, какой обработчик используется.
Режим встроенных запросов.
Еще одна интересная функция Telegram Bot API — режим встроенных запросов. Если необходимо реализовать такую функциональность для своего бота, то сначала необходимо изменить конфигурацию в @BotFather , включив этот режим при помощи команды /setinline . Иногда требуется какое-то время, пока бот не зарегистрируется в качестве встроенного бота на вашем клиенте. Можно ускорить процесс, перезапустив приложение Telegram или иногда просто нужно немного подождать.
Здесь используется ряд новых типов, так что смотрим внимательно (комментировать код не имеет смысла, т.к. просматривается аналогия с предыдущим кодом):
from telegram import InlineQueryResultArticle, InputTextMessageContent from telegram.ext import InlineQueryHandler . async def inline_caps(update, context): query = update.inline_query.query if not query: return results = [] results.append( InlineQueryResultArticle( id=query.upper(), title='Caps', input_message_content=InputTextMessageContent(query.upper()) ) ) await context.bot.answer_inline_query(update.inline_query.id, results) if __name__ == '__main__': . # создаем обработчик для функции `inline_caps()` inline_caps_handler = InlineQueryHandler(inline_caps) # регистрируем обработчик application.add_handler(inline_caps_handler) application.run_polling()
Теперь бот может отвечать копией сообщения в верхнем регистре и через режим встроенных запросов.
Некоторые сбитые с толку пользователи могут попытаться отправить созданному боту команды, которые он не понимает, поэтому можно использовать MessageHandler с фильтром filters.COMMAND , чтобы отвечать на все команды, которые не были распознаны предыдущими обработчиками.
. async def unknown(update, context): await context.bot.send_message(chat_id=update.effective_chat.id, text="Sorry, I didn't understand that command.") if __name__ == '__main__': . # создаем обработчик для функции `unknown()` unknown_handler = MessageHandler(filters.COMMAND, unknown) # регистрируем обработчик application.add_handler(unknown_handler) application.run_polling()
Примечание. Этот обработчик должен быть добавлен последним. Если его поставить первым, то он будет срабатывать до того, как обработчик CommandHandlers увидит обновление. После обработки обновления функцией unknown() все дальнейшие обработчики будут игнорируются.
Чтобы обойти такое поведение, можно передать в метод dispatcher.add_handler(handler, group) , помимо самой функции обработчика аргумент group со значением, отличным от 0. Аргумент group можно воспринимать как число, которое указывает приоритет обновления обработчика. Более низкая группа означает более высокий приоритет. Обновление может обрабатываться (максимум) одним обработчиком в каждой группе.
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Переход на асинхронный python-telegram-bot версии 20.x
- Чистый интерфейс Python для Telegram Bot API
- Команды и оповещения @BotFather в Telegram
- Обработка сообщений модулем python-telegram-bot
- Фильтры сообщений модуля python-telegram-bot
- Хранение временных данных модулем python-telegram-bot
- Настройки по умолчанию модуля python-telegram-bot
- Планировщик сообщений модуля python-telegram-bot
- Форматирование и отправка сообщений в python-telegram-bot
- Работа с файлами/media, модуль python-telegram-bot
- Меню из кнопок, модуль python-telegram-bot
- Объект CallbackContext модуля python-telegram-bot
- Подключения Telegram-бота через webhook
- Обработка исключений модуля python-telegram-bot
- Создание Inline-бота, модуль python-telegram-bot
- Работа с опросами в модуле python-telegram-bot
- Создание разговоров ConversationHandler в python-telegram-bot
- Перезапуск телеграмм-бота в случае ошибки
- Декоратор-обработчик сообщений в python-telegram-bot
- Авторизация на сайте через Telegram Passport
- Ведение публикаций в Telegram-канале с python-telegram-bot
- UTF коды emoji/эмодзи для отправки в Telegram из Python
