Боты - одна из особенностей Telegram, сделавших мессенджер столь популярным. А его встроенные клавиатуры дают разработчикам большую свободу взаимодействия с пользователями.
Keyboa помогает создавать встроенные клавиатуры любой сложности для ботов, разработанных на базе pyTelegramBotAPI.
В этой статье рассмотрим базовые возможности модуля - создание клавиатур из разных наборов данных, автоматическое и ручное распределение кнопок по рядам, объединение нескольких клавиатур в одну. Научимся создавать сложные, динамические callback, сохраняя в них информацию о выборе пользователя.
Статья рассчитана на тех, кто знает основы Telegram Bot API и хотя бы немного знаком с фреймворком pyTelegramBotAPI.
Модуль требует > Python 3.5 и ставится через pip:
pip install keyboa
В официальной документации Telegram объект inline_keyboard
определен как массив, состоящий из массивов кнопок (Array of Array of InlineKeyboardButton). То есть основной массив (список) - это клавиатура в целом, а его элементы (вложенные списки) - это ряды кнопок. Не переживайте, если определение кажется сложным - позже мы разберём его на простом примере.
Предположим, что сам бот у нас уже настроен:
import os
from telebot import TeleBot
from keyboa import keyboa_maker
token = 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.data
available_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_combiner
controls = [["⏹️", "⏪️", "⏏️", "⏩️", "▶️"], ]
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.
Буду рад вашим отзывам, идеям и конструктивной критике по развитию проекта.