Боты - одна из особенностей Telegram, сделавших мессенджер столь популярным. А его встроенные клавиатуры дают разработчикам большую свободу взаимодействия с пользователями.
Keyboa помогает создавать встроенные клавиатуры любой сложности для ботов, разработанных на базе pyTelegramBotAPI.
В этой статье рассмотрим базовые возможности модуля - создание клавиатур из разных наборов данных, автоматическое и ручное распределение кнопок по рядам, объединение нескольких клавиатур в одну. Научимся создавать сложные, динамические callback, сохраняя в них информацию о выборе пользователя.
Статья рассчитана на тех, кто знает основы Telegram Bot API и хотя бы немного знаком с фреймворком pyTelegramBotAPI.
Модуль требует > Python 3.5 и ставится через pip:
pip install keyboa
В официальной документации Telegram объект
inline_keyboard
определен как массив, состоящий из
массивов кнопок (Array of Array of InlineKeyboardButton).
То есть основной массив (список) - это клавиатура в целом, а его
элементы (вложенные списки) - это ряды кнопок. Не переживайте, если
определение кажется сложным - позже мы разберём его на простом
примере.
Предположим, что сам бот у нас уже настроен:
import osfrom telebot import TeleBotfrom keyboa import keyboa_makertoken = os.environ["TELEGRAM_BOT_TOKEN"]uid = os.environ["TELEGRAM_USER_ID"] bot = TeleBot(token=token)
Создаём клавиатуру
Предположим, нам нужно отправить пользователю на выбор список фруктов.
fruits = [ "banana", "coconut", "orange", "peach", "apricot", "apple", "pineapple", "avocado", "melon"]
Создадим самую простую клавиатуру, в которой каждый элемент встанет на отдельный ряд:
kb_fruits = keyboa_maker(items=fruits, copy_text_to_callback=True)
Всё, что потребовалось - передать список в items
.
Оставить пустой сallback_data
(ответную часть кнопки)
мы не можем, ведь иначе не узнаем, что нажал пользователь. Поэтому
добавляем текст каждой кнопки в её callback_data
,
устанавливая параметр copy_text_to_callback
.
Отправим сообщение пользователю:
bot.send_message( chat_id=uid, reply_markup=kb_fruits, text="Please select one of the fruit:")
Распределяем кнопки по рядам
Разместить несколько кнопок в ряд можно любым из трёх способов.
Указать количество кнопок в ряду
Определите параметр items_in_row
, чтобы задать
количество кнопок в ряду. Их число должно быть от одного до восьми.
Больше кнопок нам не позволит добавить сам Telegram.
Создадим клавиатуру и отправим обновлённое сообщение:
kb_fruits = keyboa_maker(items=fruits, copy_text_to_callback=True, items_in_row=3)bot.send_message( chat_id=uid, reply_markup=kb_fruits, text="Please select one of the fruit:")
Использовать автоподбор
Если клавиатура создается динамически, мы не всегда знаем,
сколько в ней окажется кнопок. Чтобы автоматически равномерно
распределить их по рядам, установите параметр
auto_alignment
. Если выставить
True
, Keyboa попробует подобрать делитель в диапазоне
от трёх до пяти, а можно задать свой диапазон в виде списка чисел
от одного до восьми, например, [2, 3, 7]
.
Параметр reverse_alignment_range
определяет, будет
ли Keyboa искать делитель с начала или с конца диапазона,
указанного в auto_alignment
.
Распределить кнопки вручную
С помощью Keyboa можно легко вручную задать количество кнопок в каждом ряду клавиатуры с помощью вложенных списков. Для примера объединим в ряды часть наших фруктов. Принцип остался прежним: каждый элемент основного списка - отдельный ряд клавиатуры, только теперь состоящий из нескольких элементов - кнопок.
fruitscomplex = [ "banana", ["coconut", "orange"], ["peach", "apricot", "apple"], "pineapple", ["avocado", "melon"],]kb_fruits_complex = keyboa_maker(items=fruits_complex, copy_text_to_callback=True)bot.send_message( chat_id=uid, reply_markup=kb_fruits_complex, text="Please select one of the fruit:")
Таким способом удобно создавать меню или любые другие клавиатуры со статичной, но сложной структурой.
Управляем содержимым callback
Мы упоминали, что callback_data - это строка, которую Telegram отправляет вам после нажатия кнопки пользователем. По её содержимому вы можете понять, какая именно кнопка была нажата.
Порой необходимо записать в callback_data дополнительные данные, например, идентификатор или другие свойства элемента. Чтобы задать кнопкам уникальные callback_data с требуемой информацией воспользуемся списком словарей или списком кортежей.
fruits_with_ids = [ {"banana": "101"}, {"coconut": "102"}, {"orange": "103"}, {"peach": "104"}, {"apricot": "105"}, {"apple": "106"}, {"pineapple": "107"}, {"avocado": "108"}, {"melon": "109"}, ]# or [# ("banana", "101"), ("coconut", "102"), ("orange", "103"),# ("peach", "104"), ("apricot", "105"), ("apple", "106"),# ("pineapple", "107"), ("avocado", "108"), ("melon", "109"), ]
В этом случае ключ каждого словаря станет текстом кнопки, а его
значение пойдет в callback_data. Для кортежей это будут нулевой и
первый элементы соответственно. И поскольку мы явно указываем, что
добавить в callback_data, параметр
copy_text_to_callback
больше не нужен.
kb_fruits = keyboa_maker(items=fruits_with_ids, items_in_row=3)bot.send_message( chat_id=uid, reply_markup=kb_fruits,text="Please select one of the fruit:")
Теперь, после нажатия пользователем одной из кнопок, мы получим в ответ не название фрукта, а его id номер, записанный в callback_data.
{"text": "banana", "callback_data": "101"}, ...
Этого достаточно, если мы используем числа только для обозначения фруктовых id. А если нужно принимать разные числовые параметры, например вес или цену? Как определить, к чему относится полученное число?
С Keyboa мы можем явно указать это при создании клавиатуры.
Параметр front_marker
добавляет одинаковый текст в
начало callback_data каждой кнопки, а back_marker
,
соответственно, в конец. Рассмотрим на примере:
kb_fruits = keyboa_maker(items=fruits_with_ids, front_marker="&fruit_id=")
Теперь callback_data кнопок будет состоять из текста формата
front_marker + value, например:
'&fruit_id=101'
, '&fruit_id=102'
и т. д. Такой подход позволит нам легко расшифровать строку и
понять, какие получены данные и с какими значениями.
Представим, что на следующем шаге пользователю предлагают выбрать желаемый вес выбранного фрукта:
# коллбек, полученный после нажатия на кнопку, например "&fruit_id=102"selected_fruit_id = call.dataavailable_weight = [1, 2, 5, 10, 100, ]kb_available_weight = keyboa_maker(items=available_weight, copy_text_to_callback=True, front_marker="&weight=", back_marker=selected_fruit_id)
Теперь callback_data каждой кнопки будет выглядеть так:
'&weight=1&fruit_id=102'
,
'&weight=2&fruit_id=102'
,
'&weight=5&fruit_id=102'
Здесь мы используем front_marker
и
back_marker
, чтобы обернуть передаваемое
значение, указав тип текущего значения и добавив в callback_data
уже полученную информацию. Имейте в виду, что у Telegram есть
ограничение на длину callback_data в 64 байта, поэтому, если
передаваемых параметров будет много - придётся сокращать названия
или использовать аббревиатуры.
Объединение нескольких клавиатур в одну
Иногда в одну клавиатуру могут входить статические и
динамические элементы, например, навигационное меню и меняющийся
набор элементов. Тогда возникает потребность собрать клавиатуру из
нескольких частей. Специально для этого у нас есть
функцияkeyboa_combiner()
.
У нее есть только один параметр - keyboards
,
представляющий собой набор уже созданных клавиатур. Вот, как это
работает:
from keyboa import keyboa_maker, keyboa_combinercontrols = [["", "", "", "", ""], ]tracks = list(range(1, 13))keyboard_controls = keyboa_maker(items=controls, copy_text_to_callback=True)keyboard_tracks = keyboa_maker(items=tracks, items_in_row=4, copy_text_to_callback=True)keyboard = keyboa_combiner(keyboards=(keyboard_tracks, keyboard_controls))bot.send_message( chat_id=uid, reply_markup=keyboard, text="Please select the track number:")
Можно объединить сразу много клавиатур - главное, чтобы общее количество кнопок было не больше ста. Это ограничение Telegram.
Итог
В этой статье мы кратко рассмотрели создание клавиатур для ботов в Telegram с помощью Keyboa. Больше информации и описание остальных параметров можно найти на странице проекта в Github.
Буду рад вашим отзывам, идеям и конструктивной критике по развитию проекта.