Telegram бот API: использование клавиатуры в Python
Telegram — одно из самых популярных мессенджеров в мире, который предлагает своим пользователям богатые возможности для общения. Одной из таких возможностей являются боты — автоматические другие пользователи, которые могут выполнять различные функции. Создание ботов в Telegram облегчено за счёт Telegram bot API, которое предоставляет разработчикам интерфейс для взаимодействия с ботами.
Один из важных элементов бота в Telegram — клавиатура, которая позволяет пользователю выбирать команды и варианты ответов одним нажатием. Создать и настроить клавиатуру в Telegram bot API с помощью Python очень просто, благодаря популярной библиотеке python-telegram-bot.
Для создания клавиатуры необходимо создать объект ReplyKeyboardMarkup, который представляет собой массив кнопок. Каждая кнопка может содержать текст и/или контакт, номер телефона или геолокацию. После создания клавиатуры, ее можно отправить пользователю с помощью метода send_message в Telegram bot API.
Эта статья расскажет о том, как создать и настроить клавиатуру в боте Telegram с использованием Python и библиотеки python-telegram-bot. Вы узнаете, как добавить и настроить кнопки, а также как обрабатывать нажатия на клавиатуру для выполнения различных действий.
Python как язык для разработки ботов
Telegram — один из самых популярных мессенджеров, который предоставляет API для разработки ботов. Используя Python и Telegram API, разработчики могут создавать ботов, которые общаются с пользователями, отвечают на команды, отправляют сообщения и многое другое.
Одним из ключевых преимуществ использования Python для разработки ботов является его простота и читаемость. Python имеет простой и лаконичный синтаксис, который позволяет разработчикам легко писать чистый и понятный код. Это особенно полезно при работе с ботами, так как часто приходится взаимодействовать с различными API и обрабатывать много данных.
Python также имеет обширную библиотеку сторонних модулей, которая упрощает разработку ботов. Например, для работы с Telegram API разработчики могут использовать пакет python-telegram-bot, который предоставляет удобный интерфейс для взаимодействия с Telegram API.
Кроме того, Python обладает отличной поддержкой регулярных выражений, что позволяет легко разбирать текстовые данные, полученные от пользователя. Это особенно полезно при разработке ботов, которые обрабатывают команды и аргументы.
Python также позволяет легко запустить бота на сервере или в облачной среде, такой как Heroku, используя фреймворки, такие как Flask или Django. Это позволяет разработчикам развернуть и масштабировать ботов легко и быстро.
В целом, Python — идеальный выбор для разработки ботов благодаря его простоте, гибкости и мощи. Он обеспечивает быстрое развертывание, легкую интеграцию с API и простоту в обслуживании. Благодаря этим преимуществам Python становится все более популярным языком для разработки ботов, в том числе и в Telegram.
Зачем нужна клавиатура в Telegram боте
Клавиатура позволяет разработчикам создавать пользовательский интерфейс, состоящий из кнопок и быстрых команд, что делает взаимодействие с ботом более простым и удобным для пользователей. Вместо того, чтобы вводить текстовые команды или отвечать на вопросы, пользователи могут просто нажимать кнопки, чтобы выполнить определенные действия или получить нужную информацию.
Использование клавиатуры в Telegram боте может значительно упростить процесс навигации по функционалу бота и сделать его более интуитивным для новых и опытных пользователей. Кроме того, клавиатура может помочь вместить больше информации на экране, что позволяет предоставлять дополнительные опции и команды без создания путаницы.
Клавиатура также улучшает визуальный аспект бота, делая его более привлекательным и профессиональным для пользователей. Пользователи видят кнопки и могут сразу понять, какие действия могут быть выполнены, что увеличивает вероятность того, что они будут активно использовать бота и оставаться с ним на связи.
В целом, использование клавиатуры в Telegram боте является полезным и эффективным способом улучшить пользовательский опыт, облегчить навигацию и повысить уровень удобства общения с ботом. Разработчики ботов могут максимально использовать возможности клавиатуры, чтобы создать более функциональные и привлекательные боты для своих пользователей.
Создание клавиатуры в Telegram bot api
Telegram bot api предоставляет возможность создания интерактивной клавиатуры для вашего бота. Клавиатура позволяет пользователям взаимодействовать с ботом, просто нажимая на кнопки.
Для создания клавиатуры в Telegram bot api используется метод ReplyKeyboardMarkup. Этот метод позволяет создать клавиатуру с определенным набором кнопок.
Пример создания клавиатуры:
import telegram from telegram import ReplyKeyboardMarkup # Создание клавиатуры с одной кнопкой keyboard = [[telegram.KeyboardButton('Нажми на меня')]] # Создание объекта ReplyKeyboardMarkup и передача ему клавиатуры markup = ReplyKeyboardMarkup(keyboard) # Отправка клавиатуры пользователю bot.send_message(chat_id=chat_id, text='Выберите пункт:', reply_markup=markup)
В примере выше создается клавиатура с одной кнопкой «Нажми на меня». Затем создается объект ReplyKeyboardMarkup, которому передается созданная клавиатура. И наконец, клавиатура отправляется пользователю с помощью метода send_message.
Помимо этого, можно создавать более сложные клавиатуры, состоящие из нескольких строк и кнопок:
# Создание клавиатуры с несколькими кнопками keyboard = [ [telegram.KeyboardButton('Кнопка 1'), telegram.KeyboardButton('Кнопка 2')], [telegram.KeyboardButton('Кнопка 3')], [telegram.KeyboardButton('Кнопка 4'), telegram.KeyboardButton('Кнопка 5'), telegram.KeyboardButton('Кнопка 6')] ] # Создание объекта ReplyKeyboardMarkup и передача ему клавиатуры markup = ReplyKeyboardMarkup(keyboard) # Отправка клавиатуры пользователю bot.send_message(chat_id=chat_id, text='Выберите пункт:', reply_markup=markup)
В данном примере создается клавиатура с тремя строками и несколькими кнопками в каждой строке. После этого клавиатура отправляется пользователю с помощью метода send_message.
Клавиатура в Telegram bot api может быть не только текстовой, но и содержать в себе другие типы кнопок, такие как inline-кнопки и кнопки с запросом контакта или геолокации. Для создания таких кнопок используются другие методы и объекты.
Теперь вы знаете, как создать и отправить клавиатуру в Telegram bot api. Используйте эту функциональность, чтобы сделать вашего бота более удобным и интерактивным для пользователей.
PyTelegramBotAPI на примере проекта сбора обратной связи #2
Всем привет, вот и пришло время для второй части цикла статей посвященных разработке ТГ ботов на PyTelegramBotAPI.
Сегодня мы продолжим изучении ботов, и разберём несколько полезных фукнций.
Напомню, что в прошлый раз мы получили токен через BotFather, и написали функцию отправляющую приветственное сообщение на команду /start.
Давайте модернизируем нашего бота. Будем запрашивать имя и фамилию и сохранять эти данные.
Для сохранения данных можно использовать:
Я в своём примере остановлюсь на словарях. Почему? Всё просто, БД (база данных) несомненно лучший вариант, но надо уметь с ней работать, знать язык запросов (SQL). Файлы — тоже неплохо. Но у них ряд минусов. Во первых при деплое (разворачивании нашего проекта на сервере) к нему может не быть прав, файл занимает дополнительное место на диске, работа с файлами содержит некоторые другие нюансы (например: проблема с кодировкой на Windows). Да и не нужно использовать для такой задачи файлы или БД, можно обойтись и обычными словарями.
Как мы знаем, словари могут хранить любую информации в удобном виде (через ключ-значение), в нашем случае у нас будет два ключа: name и surname. Но, тут возможен очень неприятный баг, который может всплыть только при релизе нашего проекта. Если два пользователя в одно и тоже время воспользуются нашим ботом, то данные в словаре смешаются. Пример:
Пользователь1 вводит имя, например, Иван. Далее он вводит свою фамилию, Александрович. Но, пока Пользователь1 вводил свою фамилию, зашёл Пользователь2 и начал вводить своё имя, он ввел Никита. И из-за того, что у нас один словарь для всех клиентов, данные одного смешались с данными другого. Имя и фамилия Пользователя1 будет не Иван Александрович, а Никита Александрович. Если вы не поняли, прочтите этот абзац заново, это достаточно важно.
Для устранения подобного бага введём ещё один ключ, это будет уникальный id чата. Мы его использовали в прошлый раз для отправки сообщений. Тогда наша структура будет выглядеть так:
1) ID первого чата
1.1) Имя пользователя
2) ID второго чата
Если приводить пример с кодом, то это будет выглядеть как-то так:
users = < 123456: < 'name': 'Henry', 'surname': 'Ford' >, 374519: < 'name': 'Ivan', 'surname': 'Ivanov' >>
Добавим переменную users в нашего бота, и перейдем к написанию функций.
Для начала сделаем функцию сохранения имени:
import telebot from config import TOKEN bot = telebot.TeleBot(TOKEN) users = <> @bot.message_handler(commands=['start']) def welcome(message): chat_id = message.chat.id bot.send_message(chat_id, 'Добро пожаловать в бота сбора обратной связи! Введие своё имя') users[chat_id] = <> bot.register_next_step_handler(message, save_username) def save_username(message): chat_id = message.chat.id name = message.text users[chat_id] = name bot.send_message(chat_id, f'Отлично, . Теперь укажи свою фамилию') if __name__ == '__main__': print('Бот запущен!') bot.infinity_polling()
Как вы можете заметить, наш код значительно увеличился и усложнился. Но не стоит пугаться, сейчас всё расскажу.
На верху (6 строка) был добавлен пустой словарь, он и будет хранить данные пользователей. Далее в функции welcome (14 строка) мы создаем ключ у словаря users, который является id чата. Т.е. наш словарь сейчас выглядит так:
users = < 548269: <>>
После этого, на 15 строчке мы определили следующее действие нашего бота. Не понятно? Сейчас объясню: мы должны запросить у пользователя имя, это понятно, но как это сделать, если до этого мы работали с командами (например команда /start ). А имя это не команда. В этом случае, нам не поможет декоратор message_handler. Необходимо указать боту, что после запуска функции welcome надо запустить другую функцию, которая и будет записываться имя. Другими словами register_next_step_handler — функция позволяющая задать следующий шаг бота. В этой функции мы указываем два аргумента: message — основной объект в телеграм ботах, который содержит всю необходимую информацию (id чата, текст сообщения и тд), а также ссылка на функцию, которая будет далее вызываться.
Надеюсь понятно, если нет — задавайте вопрос в комментариях, постараюсь на все ответить.
Идём дальше. Разберём функцию save_username.
Эта функция нужна для сохранения имени пользователя в файл. В начале функции мы создаём переменные chat_id и name. Первая переменная хранит id чата, это как мы помним уникальный идентификатор чата, используется, например, чтобы отправить сообщение (а в нашем случае ещё как ключ словаря). Вторая переменная — name — хранит текст, который ввёл пользователя в сообщении (копирует текст сообщения). А так как в функции welcome мы запросили имя, то текущий текст это имя.
Далее записываем указанное имя в словарь, и просим пользователя ввести фамилию. Сейчас наш словарь выглядит так (пример):
users = < 548269: < 'name': 'Iv' >>
Давайте теперь создадим функцию для записи фамилии. А также функцию, которая будет выводить данные пользователя (имя и фамилию).
import telebot from config import TOKEN bot = telebot.TeleBot(TOKEN) users = <> @bot.message_handler(commands=['start']) def welcome(message): chat_id = message.chat.id bot.send_message(chat_id, 'Добро пожаловать в бота сбора обратной связи! Введие своё имя') users[chat_id] = <> bot.register_next_step_handler(message, save_username) def save_username(message): chat_id = message.chat.id name = message.text users[chat_id]['name'] = name bot.send_message(chat_id, f'Отлично, . Теперь укажи свою фамилию') bot.register_next_step_handler(message, save_surname) def save_surname(message): chat_id = message.chat.id surname = message.text users[chat_id]['surname'] = surname bot.send_message(chat_id, f'Ваши данные успешно сохранены!') @bot.message_handler(commands=['who_i']) def who_i(message): chat_id = message.chat.id name = users[chat_id]['name'] surname = users[chat_id]['surname'] bot.send_message(chat_id, f'Вы: ') if __name__ == '__main__': print('Бот запущен!') bot.infinity_polling()
Код очень схож с предыдущим. Добавилась фукнция save_surname, которая сохраняет фамилию. Она работает аналогично с save_name. А у save_name добавился register_next_step_handler, так как нам после ввода имени, необходимо следом ввести и фамилию.
Также появилась новая команда /who_i, её функция (who_i) отправляет имя и фамилию текущего пользователя.
Запустим и проверим бота:
Отлично, всё получилось! Разберём наши достижения, сегодня мы:
- Создали аналог базы данных, с помощью словарей
- Сделали цепочку вызовов через register_next_step_handler
- Значительно увеличили функционал бота
Вот и подошёл к концу второй урок из цикла, сегодня было сделано много важной и сложной работы. А в следующий раз мы разберём клавиатуру, и наш бот уже выйдет на финишную прямую.
Следующая часть выйдет через три дня.
Меню из кнопок, модуль python-telegram-bot
Внимание! Пакеты python-telegram-bot версии 13.x будут придерживаться многопоточной парадигмы программирования (*на данный момент актуальна версия 13.15). Пакеты версий 20.x и новее предоставляют чистый асинхронный Python интерфейс для Telegram Bot API. Дополнительно смотрите основные изменения в пакете python-telegram-bot версии 20.x.
Всякий раз, когда бот отправляет сообщение, он может передать специальную клавиатуру с предопределенными параметрами ответа. Приложения Telegram, которые получают сообщение, будут отображать эту клавиатуру для пользователя. Нажатие любой из кнопок немедленно отправит соответствующую команду. Таким образом можно значительно упростить взаимодействие пользователя с ботом.
Содержание.
- Встроенные клавиатуры Telegramm в сообщения бота;
- Классы KeyboardButton и InlineKeyboardButton ;
- Алгоритм построения и отправки кнопок в Telegram чат;
- Обработчик сообщений CallbackQueryHandler ;
- Базовый пример, использующий встроенную клавиатуру;
- Пример встроенной клавиатуры с 2-мя состояниями для версии 13.x;
- Пример встроенной клавиатуры с 2-мя состояниями для версии 20.x;
- Как работает обработчике разговора ConversationHandler() .
Встроенные клавиатуры Telegramm в сообщения бота.
Бывают случаи, когда нужно что-либо сделать, не отправляя никаких сообщений в чат. Например, когда пользователь меняет настройки или просматривает результаты поиска. В таких случаях можно использовать встроенные InlineKeyboardButton клавиатуры, которые интегрированы непосредственно в сообщения, которым они принадлежат.
В отличие от настраиваемых клавиатур KeyboardButtons , которые посылают текст кнопки в качестве ответа, нажатие кнопок на встроенных клавиатурах InlineKeyboardButton не приводит к отправке сообщений в чат. Вместо этого встроенные клавиатуры поддерживают кнопки, которые работают за кулисами: кнопки обратного вызова, кнопки с URL и переключение на встроенные кнопки.
Когда используются кнопки обратного вызова (с аргументом callback_data ), бот может обновлять свои существующие сообщения или клавиатуры. При нажатии на такую кнопку, бот просто получает соответствующий запрос. Получив запрос, бот может отображать результат в уведомлении в верхней части экрана чата или в предупреждении.
Классы KeyboardButton и InlineKeyboardButton .
Данные классы определяют атрибуты и методы, одноименные с названиями аргументов.
KeyboardButton(text, request_contact=None, request_location=None, request_poll=None, **_kwargs) :
Объект KeyboardButton представляет собой одну кнопку клавиатуры для ответа текстом text , который отображается на кнопке. Необязательные аргументы исключают друг друга. Импортируется из основного модуля telegram.KeyboardButton .
Значение и поведение аргументов KeyboardButton :
- text (str) — текст кнопки. Если ни одно из дополнительных полей не используется, оно будет отправлено боту в виде сообщения при нажатии кнопки.
- request_contact ( bool , необязательно) — если True , то при нажатии будет отправлен телефонный номер пользователя, как контакт. Доступно только в приватных чатах.
- request_location ( bool , необязательный) — если True , то при нажатии будет отправлено текущее местоположение пользователя. Доступно только в приватных чатах.
- request_poll ( KeyboardButtonPollType , необязательно) — если указано, то при нажатии кнопки пользователю будет предложено создать опрос и отправить его боту. Доступно только в приватных чатах.
- **_kwargs ( dict ) — произвольные ключевые аргументы.
InlineKeyboardButton(text, url=None, callback_data=None, switch_inline_query=None, switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **_kwargs)
Объект InlineKeyboardButton представляет одну кнопку встроенной клавиатуры. Допускается использовать ровно одно из необязательных полей. Импортируется из основного модуля telegram.InlineKeyboardButton .
Значение и поведение аргументов InlineKeyboardButton :
- text ( str ) — текст кнопки. Если ни одно из дополнительных полей не используется, оно будет отправлено боту в виде сообщения при нажатии кнопки.
- url ( str ) — HTTP или tg://url, который открывается при нажатии кнопки.
- login_url ( telegram.LoginUrl , необязательно) — URL-адрес HTTP, используемый для автоматической авторизации пользователя. Может использоваться как замена виджета входа в Telegram.
- callback_data ( str , необязательно) — данные, которые будут отправлены в запросе обратного вызова боту при нажатии кнопки, UTF-8 1-64 байта.
- switch_inline_query ( str , необязательно) — если установлено, то нажатие кнопки предложит пользователю выбрать один из своих чатов, открыть этот чат и вставить логин бота и указанный встроенный запрос в поле ввода. Может быть пустым, и в этом случае будет вставлено только логин бота. Это дает пользователям простой способ начать использовать вашего бота во встроенном режиме, в то время как они находятся с ним в приватном чате. Особенно полезно в сочетании с действиями switch_pm* — в этом случае пользователь автоматически вернется в чат, из которого он переключился, пропуская экран выбора чата.
- switch_inline_query_current_chat ( str , необязательно) — если установлено, то нажатие кнопки вставит логин бота и указанный встроенный запрос в поле ввода текущего чата. Может быть пустым, и в этом случае будет вставлено только логин бота. Это предлагает пользователю быстрый способ открыть вашего бота во встроенном режиме в том же чате — удобно для выбора чего-либо из нескольких вариантов.
- callback_game ( telegram.CallbackGame , необязательно) — описание игры, которая будет запускаться при нажатии кнопки пользователем. Кнопка этого типа всегда должна быть первой кнопкой в первом ряду.
- pay ( bool , необязательно) — укажите True , чтобы отправить кнопку Pay. Кнопка этого типа всегда должна быть первой кнопкой в первом ряду.
- **_kwargs ( dict ) — произвольные ключевые аргументы.
Алгоритм построения и отправки кнопок в Telegram чат.
Для создания макета кнопок со столбцами n_cols из списка кнопок необходимо создать функцию build_menu() , которая будет шаблоном для построения кнопок:
def build_menu(buttons, n_cols, header_buttons=None, footer_buttons=None): menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)] if header_buttons: menu.insert(0, [header_buttons]) if footer_buttons: menu.append([footer_buttons]) return menu
В коде выше определены списки header_buttons и footer_buttons , их можно использовать чтобы поместить кнопки в первую или последнюю строку соответственно.
В приведенном ниже фрагменте кода нужно заменить . соответствующим значением аргумента callback_data — это строка (UTF-8 1-64 байта) с данными, отправляемые боту в ответном запросе при нажатии кнопки. Если будете использовать кнопки KeyboardButtons для создания списка кнопок button_list , то для построения передаваемой в чат клавиатуры из кнопок используйте ReplyKeyboardMarkup вместо InlineKeyboardMarkup .
# список кнопок button_list = [ InlineKeyboardButton("col1", callback_data=. ), InlineKeyboardButton("col2", callback_data=. ), InlineKeyboardButton("row 2", callback_data=. ) ] # сборка клавиатуры из кнопок `InlineKeyboardButton` reply_markup = InlineKeyboardMarkup(build_menu(button_list, n_cols=2)) # отправка клавиатуры в чат для ВЕРСИИ 13.x bot.send_message(chat_id=chat_id, text="Меню из двух столбцов", reply_markup=reply_markup) # или # отправка клавиатуры в чат для ВЕРСИИ 20.x # await bot.send_message(chat_id=chat_id, text="Меню из двух столбцов", reply_markup=reply_markup)
Или, если нужна динамическая версия, используйте генератор списка для динамического создания button_list из списка строк:
# построение простых кнопок для ответа # текстом, расположенным на кнопках some_strings = ["col1", "col2", "row2"] button_list = [[KeyboardButton(ss)] for ss in some_strings] # сборка клавиатуры из кнопок `KeyboardButton` reply_markup = ReplyKeyboardMarkup(build_menu(button_list, n_cols=2)) # отправка клавиатуры в чат для ВЕРСИИ 13.x bot.send_message(chat_id=chat_id, text="Меню из двух столбцов", reply_markup=reply_markup) # или # отправка клавиатуры в чат для ВЕРСИИ 20.x # await bot.send_message(chat_id=chat_id, text="Меню из двух столбцов", reply_markup=reply_markup)
Это особенно полезно, если поместить внутрь вспомогательного метода, такого как get_data_buttons , для работы с динамическими данными и обновления меню в соответствии с вводом пользователя.
Чтобы обработать callback_data , необходимо подключить обработчик CallbackQueryHandler .
Обработчик сообщений CallbackQueryHandler .
Обработчик сообщений CallbackQueryHandler определяет атрибуты и методы, одноименные с названиями аргументов. Обработчик CallbackQueryHandler импортируется из модуля расширения telegram.ext .
CallbackQueryHandler(callback, pattern=None, block=True) :
Объект CallbackQueryHandler представляет собой обработчик запросов обратного вызова Telegram. Может использовать дополнительную фильтрацию на основе регулярных выражений модуля re .
Значение и поведение аргументов InlineKeyboardButton :
- callback — Функция обратного вызова для этого обработчика. Будет вызываться, когда сообщение должно быть обработано этим обработчиком.
- pattern=None ( str , необязательно) — шаблон регулярного выражения. Если не None , то для поиска совпадений в telegram.CallbackQuery.data (должно ли сообщение обрабатываться этим обработчиком) будет использоваться функция re.match() .
- run_async=False ( bool ) — (удален в версии 20.x) определяет, будет ли обратный вызов выполняться асинхронно.
- block=True ( bool ) — (новое в версии 20.x) определяет, следует ли ожидать возвращаемого значения обратного вызова перед обработкой следующего обработчика в telegram.ext.Application.process_update() .
Базовый пример, использующий встроенную клавиатуру.
Примечание Этот пример будет работать в версии пакета 13.x. Для асинхронной версии пакета 20.x необходимые изменения прокомментированы в коде.
Дополнительно смотрите обзорный материал, подраздел «Асинхронный модуль расширения telegram.ext (версия 20.x)«.
import logging from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import Updater, CommandHandler, CallbackQueryHandler logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) def start(update, _): keyboard = [ [ InlineKeyboardButton("Option 1", callback_data='1'), InlineKeyboardButton("Option 2", callback_data='2'), ], [InlineKeyboardButton("Option 3", callback_data='3')], ] reply_markup = InlineKeyboardMarkup(keyboard) # для версии 13.x update.message.reply_text('Пожалуйста, выберите:', reply_markup=reply_markup) # для версии 20.x необходимо использовать оператор await # await update.message.reply_text('Пожалуйста, выберите:', reply_markup=reply_markup) def button(update, _): query = update.callback_query variant = query.data # `CallbackQueries` требует ответа, даже если # уведомление для пользователя не требуется, в противном # случае у некоторых клиентов могут возникнуть проблемы. # смотри https://core.telegram.org/bots/api#callbackquery. query.answer() # для версии 20.x необходимо использовать оператор await # await query.answer() # редактируем сообщение, тем самым кнопки # в чате заменятся на этот ответ. query.edit_message_text(text=f"Выбранный вариант: variant>") # для версии 20.x необходимо использовать оператор await # await query.edit_message_text(text=f"Выбранный вариант: ") def help_command(update, _): update.message.reply_text("Используйте `/start` для тестирования.") # для версии 20.x необходимо использовать оператор await # await update.message.reply_text("Используйте `/start` для тестирования.") if __name__ == '__main__': # в версии 13.x создаются 2 объекта: # `updater` и диспетчер `app` updater = Updater("TOKEN") app = updater.dispatcher # для версии 20.x необходимо создать только 1 объект # приложение через `Application.builder()` # app = Application.builder().token("TOKEN").build() app.add_handler(CommandHandler('start', start)) app.add_handler(CallbackQueryHandler(button)) app.add_handler(CommandHandler('help', help_command)) # Запуск бота в версии 13.x происходит # через объект `updater` updater.start_polling() updater.idle() # Запуск бота в версии 20.x # app.run_polling()
Пример встроенной клавиатуры с 2-мя состояниями для версии 13.x.
Данный пример снабжен комментариями, так что понять как и что работает не составит труда. Он так же демонстрирует использование обработчиков CallbackQueryHandler и ConversationHandler .
import logging from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import ( Updater, CommandHandler, CallbackQueryHandler, ConversationHandler, ) # Ведение журнала логов logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO ) logger = logging.getLogger(__name__) # Этапы/состояния разговора FIRST, SECOND = range(2) # Данные обратного вызова ONE, TWO, THREE, FOUR = range(4) def start(update, _): """Вызывается по команде `/start`.""" # Получаем пользователя, который запустил команду `/start` user = update.message.from_user logger.info("Пользователь %s начал разговор", user.first_name) # Создаем `InlineKeyboard`, где каждая кнопка имеет # отображаемый текст и строку `callback_data` # Клавиатура - это список строк кнопок, где каждая строка, # в свою очередь, является списком `[[. ]]` keyboard = [ [ InlineKeyboardButton("1", callback_data=str(ONE)), InlineKeyboardButton("2", callback_data=str(TWO)), ] ] reply_markup = InlineKeyboardMarkup(keyboard) # Отправляем сообщение с текстом и добавленной клавиатурой `reply_markup` update.message.reply_text( text="Запустите обработчик, выберите маршрут", reply_markup=reply_markup ) # Сообщаем `ConversationHandler`, что сейчас состояние `FIRST` return FIRST def start_over(update, _): """Тот же текст и клавиатура, что и при `/start`, но не как новое сообщение""" # Получаем `CallbackQuery` из обновления `update` query = update.callback_query # На запросы обратного вызова необходимо ответить, # даже если уведомление для пользователя не требуется. # В противном случае у некоторых клиентов могут возникнуть проблемы. query.answer() keyboard = [ [ InlineKeyboardButton("1", callback_data=str(ONE)), InlineKeyboardButton("2", callback_data=str(TWO)), ] ] reply_markup = InlineKeyboardMarkup(keyboard) # Отредактируем сообщение, вызвавшее обратный вызов. # Это создает ощущение интерактивного меню. query.edit_message_text( text="Выберите маршрут", reply_markup=reply_markup ) # Сообщаем `ConversationHandler`, что сейчас находимся в состоянии `FIRST` return FIRST def one(update, _): """Показ нового выбора кнопок""" query = update.callback_query query.answer() keyboard = [ [ InlineKeyboardButton("3", callback_data=str(THREE)), InlineKeyboardButton("4", callback_data=str(FOUR)), ] ] reply_markup = InlineKeyboardMarkup(keyboard) query.edit_message_text( text="Вызов `CallbackQueryHandler`, выберите маршрут", reply_markup=reply_markup ) return FIRST def two(update, _): """Показ нового выбора кнопок""" query = update.callback_query query.answer() keyboard = [ [ InlineKeyboardButton("1", callback_data=str(ONE)), InlineKeyboardButton("3", callback_data=str(THREE)), ] ] reply_markup = InlineKeyboardMarkup(keyboard) query.edit_message_text( text="Второй CallbackQueryHandler", reply_markup=reply_markup ) return FIRST def three(update, _): """Показ выбора кнопок""" query = update.callback_query query.answer() keyboard = [ [ InlineKeyboardButton("Да, сделаем это снова!", callback_data=str(ONE)), InlineKeyboardButton("Нет, с меня хватит . ", callback_data=str(TWO)), ] ] reply_markup = InlineKeyboardMarkup(keyboard) query.edit_message_text( text="Третий CallbackQueryHandler. Начать сначала?", reply_markup=reply_markup ) # Переход в состояние разговора `SECOND` return SECOND def four(update, _): """Показ выбора кнопок""" query = update.callback_query query.answer() keyboard = [ [ InlineKeyboardButton("2", callback_data=str(TWO)), InlineKeyboardButton("4", callback_data=str(FOUR)), ] ] reply_markup = InlineKeyboardMarkup(keyboard) query.edit_message_text( text="Четвертый CallbackQueryHandler, выберите маршрут", reply_markup=reply_markup ) return FIRST def end(update, _): """Возвращает `ConversationHandler.END`, который говорит `ConversationHandler` что разговор окончен""" query = update.callback_query query.answer() query.edit_message_text(text="See you next time!") return ConversationHandler.END if __name__ == '__main__': updater = Updater("TOKEN") dispatcher = updater.dispatcher # Настройка обработчика разговоров с состояниями `FIRST` и `SECOND` # Используем параметр `pattern` для передачи `CallbackQueries` с # определенным шаблоном данных соответствующим обработчикам # ^ - означает "начало строки" # $ - означает "конец строки" # Таким образом, паттерн `^ABC$` будет ловить только 'ABC' conv_handler = ConversationHandler( entry_points=[CommandHandler('start', start)], states= # словарь состояний разговора, возвращаемых callback функциями FIRST: [ CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'), CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'), CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'), CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$'), ], SECOND: [ CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'), CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'), ], >, fallbacks=[CommandHandler('start', start)], ) # Добавляем `ConversationHandler` в диспетчер, который # будет использоваться для обработки обновлений dispatcher.add_handler(conv_handler) updater.start_polling() updater.idle()
Пример встроенной клавиатуры с 2-мя состояниями для версии 20.x.
from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update from telegram.ext import ( Application, CommandHandler, ContextTypes, ConversationHandler, MessageHandler, filters, ) logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3) reply_keyboard = [ ["Age", "Favourite colour"], ["Number of siblings", "Something else. "], ["Done"], ] markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) def facts_to_str(user_data: Dict[str, str]) -> str: """Вспомогательная функция для форматирования собранной информации о пользователе.""" facts = [f"key> - value>" for key, value in user_data.items()] return "\n".join(facts).join(["\n", "\n"]) async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Начvало разговора, просьба ввести данные.""" await update.message.reply_text( "Hi! My name is Doctor Botter. I will hold a more complex conversation with you. " "Why don't you tell me something about yourself?", reply_markup=markup, ) return CHOOSING async def regular_choice(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Запрос информации о выбранном предопределенном выборе.""" text = update.message.text context.user_data["choice"] = text await update.message.reply_text(f"Your text.lower()>? Yes, I would love to hear about that!") return TYPING_REPLY async def custom_choice(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Запрос описания пользовательской категории.""" await update.message.reply_text( 'Alright, please send me the category first, for example "Most impressive skill"' ) return TYPING_CHOICE async def received_information(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Store info provided by user and ask for the next category.""" user_data = context.user_data text = update.message.text category = user_data["choice"] user_data[category] = text del user_data["choice"] await update.message.reply_text( "Neat! Just so you know, this is what you already told me:" f"facts_to_str(user_data)>You can tell me more, or change your opinion" " on something.", reply_markup=markup, ) return CHOOSING async def done(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: """Вывод собранной информации и завершение разговора.""" user_data = context.user_data if "choice" in user_data: del user_data["choice"] await update.message.reply_text( f"I learned these facts about you: facts_to_str(user_data)>Until next time!", reply_markup=ReplyKeyboardRemove(), ) user_data.clear() return ConversationHandler.END if __name__ == "__main__": application = Application.builder().token("TOKEN").build() conv_handler = ConversationHandler( entry_points=[CommandHandler("start", start)], states= CHOOSING: [ MessageHandler( filters.Regex("^(Age|Favourite colour|Number of siblings)$"), regular_choice ), MessageHandler(filters.Regex("^Something else. $"), custom_choice), ], TYPING_CHOICE: [ MessageHandler( filters.TEXT & ~(filters.COMMAND | filters.Regex("^Done$")), regular_choice ) ], TYPING_REPLY: [ MessageHandler( filters.TEXT & ~(filters.COMMAND | filters.Regex("^Done$")), received_information, ) ], >, fallbacks=[MessageHandler(filters.Regex("^Done$"), done)], ) application.add_handler(conv_handler) # Запуск бота. application.run_polling()
Как работает обработчик разговора ConversationHandler() .
Основная магия происходит в обработчике разговора ConversationHandler() . Обработчик ConversationHandler() имеет три основные точки, которые необходимо определить для ведения беседы:
- entry_points — точка входа в разговор, представляет собой список обработчиков, которые запускают разговор. Разговор можно запустить по команде, отправленной пользователем (в данном случае /start ) и/или по каким то фразам, которые можно поймать при помощи обработчика MessageHandler() и фильтра Filters.regex (например: Filters.regex(‘(поговорим|скучно)’), callback_func)],
- states — состояния разговора. Представляет собой словарь, в котором ключ, это этап разговора, который явно возвращает функция обратного вызова, при этом высылает или отвечает на сообщение или передает кнопки для выбора и т.д. Так вот, реакция/ответ пользователя на это сообщение/нажатие кнопки будет обрабатываться обработчиками, находящихся в списке значений этого ключа — этапа/состояния разговора.
- fallbacks — точка выхода из разговора. Разговор заканчивается, если функция обработчик сообщения явно возвращает return ConversationHandler.END
Переключение между этапами разговора происходит при помощи функций обратного вызова, которые при обработке/анализе ответа пользователя будут возвращать нужный этап/состояние разговора.
conv_handler = ConversationHandler( # точка входа в разговор entry_points=[CommandHandler('start', start)], # словарь состояний разговора, возвращаемых callback функциями states= # Этап `FIRST` - т.е. функция обработчик какого то сообщения явно # вернула константу FIRST (return `FIRST`), а так же послала/ответила # на сообщение. Ответ пользователя на это сообщение будет # обрабатываться обработчиками определенными в этом списке FIRST: [ CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'), CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'), CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'), CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$'), ], # Этап `SECOND` - происходит то же самое, что и в описании этапа `FIRST` SECOND: [ CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'), CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'), ], >, # точка выхода из разговора fallbacks=[CommandHandler('start', start)], )
- КРАТКИЙ ОБЗОР МАТЕРИАЛА.
- Переход на асинхронный 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
Встроенные кнопки в Telegram Bot API — pyTelegramBotAPI
Добрый день уважаемые читатели, давайте рассмотрим, какие основные типы встроенных кнопок предлагают чат-боты telegram и в чем их особенности. Статья будет полезна всем, кто хочет разобраться в возможностях взаимодействия с пользователями telegram в версии bot API 2.0.
Для обзора возможностей нам понадобится установить 3 целых 2 десятых Python`a и пару ложек pyTelegramBotAPI. Особенности настройки и регистрации чат-бота мы рассматривать не будем, т.к. есть множество статей на эту тему.
И так, что же такое встроенные кнопки(клавиатура) в мессенджере Telegram? Это кнопки которые выводятся во внутренней области чата и привязываются к конкретному сообщению. Они жестко связаны с сообщением(если удалить сообщение, внутренние кнопки так же удаляются вместе с ним.). Они дают возможность динамически видоизменять его.
В данный момент есть три типа встроенных кнопок:
URL-кнопки
Для создания кнопки используется тип InlineKeyboardMarkup, давайте создадим кнопку «Наш сайт»:
@bot.message_handler(commands = ['url']) def url(message): markup = types.InlineKeyboardMarkup() btn_my_site= types.InlineKeyboardButton(text='Наш сайт', url='https://habrahabr.ru') markup.add(btn_my_site) bot.send_message(message.chat.id, "Нажми на кнопку и перейди на наш сайт.", reply_markup = markup)
Тут название говорит само за себя, это тип кнопок предназначен для перенаправления пользователя по ссылке, с соответствующим предупреждением. Кнопка имеет соответствующий ярлычок в правом верхнем углу, чтобы дать понять пользователю, что это ссылка.
Switch-кнопки
Этот тип кнопок предназначен для перенаправления пользователя в какой либо чат, с последующей активацией (встроенного) inline-режима общения с ботом. Данный режим можно активировать вручную: в чате, вводим: «@название бота», но switch-кнопки позволяют это сделать автоматически (помогая знакомиться с inline-режимом новичкам).
Для того что-бы создать подобный переключатель, необходимо указать аргумент switch_inline_query либо пустой, либо с каким-либо текстом.
@bot.message_handler(commands = ['switch']) def switch(message): markup = types.InlineKeyboardMarkup() switch_button = types.InlineKeyboardButton(text='Try', switch_inline_query="Telegram") markup.add(switch_button) bot.send_message(message.chat.id, "Выбрать чат", reply_markup = markup)

Теперь, если мы нажмем на кнопку и выберем чат, вот что получится:
Шаг 1:
Нажимаем на кнопку.

Шаг 2:
Выбираем чат.

Шаг 3:
Активировался встроенный inline-режим.
Callback-кнопки
Ну и наконец самое интересное — это кнопки с обратной связью: позволяют динамически обновлять сообщение/встроенные кнопки (не засоряя при этом ленту), а так же отображать уведомление в верху чат-бота или модальном окне.

Например, их можно использовать для просмотра длинного сообщения, аналогично пагинации страниц на сайтах, или например сделать календарь. Я не стану изобретать велосипед, а через поиск по GitHub, найду готовую библиотеку calendar-telegram. Выполнив указанные инструкции, получаем готовый календарь, который можно динамически изменять по нажатию на соответствующие кнопки:
@bot.message_handler(commands=['calendar']) def get_calendar(message): now = datetime.datetime.now() #Текущая дата chat_id = message.chat.id date = (now.year,now.month) current_shown_dates[chat_id] = date #Сохраним текущую дату в словарь markup = create_calendar(now.year,now.month) bot.send_message(message.chat.id, "Пожалйста, выберите дату", reply_markup=markup)
Так же можно добавить уведомление по нажатию на дату, для этого достаточно указать сообщение в ответе:
bot.answer_callback_query(call.id, text="Дата выбрана")

(Пример в десктопной версии)

(Пример в мобильной версии)
Если изменить show_alert на True, то мы получим модальное окно:
bot.answer_callback_query(call.id, show_alert=True, text="Дата выбрана")

Заключение
По последним данным, в нашумевшем мессенджере Telegram регистрируются больше 600к пользователей ежедневно. Именно поэтому важно подхватить тренд и разобраться с его основными особенностями, т.к. различные методы взаимодействия с ботами существенно облегчает жизнь разработчиков и пользователей.

Cпасибо за Ваш интерес к данной теме.
- telegram bots
- telegram
- telegram api
