Предыстория
В своей статье на Хабре я уже делилась с читателями первым опытом создания и развёртывании на сервере telegram-бота на java. При разработке бота были выбраны такие способы взаимодействия пользователя с ботом как:
ReplyKeyboardMarkup – клавиатура, которая показывается вместо основной и не привязана ни к какому сообщению. Представляет собой шаблоны сообщений (варианты ответа), которые выбираются путем нажатия на готовую кнопку. Боту в качестве сообщения отправляется то, что написано на кнопке;
получение от пользователя запроса посредством введения в поле сообщения конкретной команды либо набора определенного сообщения.
За 4 месяца самостоятельной жизни моего первого бота стало понятно, что пользователям не хочется что-либо вводить в поле сообщения. Что значительно удобней было бы просто нажимать на очередную кнопку при выборе формы документа. Иными словами, в целях упрощения взаимодействия с ботом необходимо минимизировать возможность использовать обычную клавиатуру смартфона или ПК.
И такая возможность есть, если применить InlineKeyboard – вариант кнопок (за которыми скрыт необходимый функционал), прикрепленных непосредственно к сообщению от бота.
Применить такую клавиатуру я решила при разработке другого бота. Однако столкнулась с тем, что ни в документации Telegram bot Api, ни в статьях / разборах, размещенных в Интернет, нет прозрачного пошагового пояснения всей цепочки процессов. Разобравшись для себя с взаимосвязями вызовов в InlineKeyboard я решила этим поделиться с другими разработчиками.
Как оно устроено
Для создания встроенной клавиатуры необходимо работать со следующими классами:
InlineKeyboardMarkup – объект, представляющий собой встроенную клавиатуру,
InlineKeyboardButton - объект одной кнопки встроенной клавиатуры. Кнопка или набор кнопок располагается сразу под сообщением от бота. Объект принимает, помимо прочего, такие параметры как: text (отображается на кнопке, обязательный параметр, поддерживает текст и эмодзи), url (адрес на который будет перенаправлен пользователь), callback_data (строка 1-64 символа, которая будет передана боту через объект CallbackQuery).
В своем проекте, основной функционал которого заключается в отправке пользователю картинок с изображением работ художника, которого пользователь выбирает на встроенной клавиатуре, я реализовала InlineKeyboard следующим образом.
1. Сами встроенные клавиатуры я создавала в отдельных классах через методы, возвращающие объект типа SendMessage:
public static SendMessage hermitageInlineKeyboardAb (long chat_id) {
// создаем объект сообщения
SendMessage message = new SendMessage();
message.setChatId(chat_id);
message.setText(«Чтобы увидеть картины, нажми на фамилию художника или перейди в полную коллекцию на сайте музея»);
// создаем объект встроенной клавиатуры
InlineKeyboardMarkup markupInline = new InlineKeyboardMarkup();
// создаем список списков кнопок, который впоследствии объединит ряды кнопок
List<List<InlineKeyboardButton>> rowsInline = new ArrayList<>();
// создаем список с кнопками для первого ряда
List<InlineKeyboardButton> rowInline1 = new ArrayList<>();
// создаем первую кнопку для в ряду
InlineKeyboardButton inlineKeyboardButton1 = new InlineKeyboardButton();
// устанавливаем параметр текста на кнопке
inlineKeyboardButton1.setText(«Аврезе»);
// устанавливаем параметр callback_data
inlineKeyboardButton1.setCallbackData(«АВРЕЗЕ»);
// создаем по аналогии вторую кнопку в ряду
InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
inlineKeyboardButton2.setText(«Аликс»);
inlineKeyboardButton2.setCallbackData(«АЛИКС»);
// добавляем кнопки в первый ряд в том порядке,
// какой нам необходим. В рассматриваемом случае ряд будет содержать 2 кнопки,
// размер которых будет одинаково пропорционально растянут по ширине сообщения,
// под которым клавиатура располагается
rowInline1.add(inlineKeyboardButton1);
rowInline1.add(inlineKeyboardButton2);
// если необходимо в кнопку запрограммировать переход на адрес
// Интернет страницы, такой параметр устанавливается через setUrl(String s).
// При этом в приведенном примере ряд кнопок будет состоять всего из одной кнопки
InlineKeyboardButton inlineKeyboardButton21 = new InlineKeyboardButton();
inlineKeyboardButton21.setText(«Переход на внешний сайт»);
inlineKeyboardButton21.setUrl(«https://collections.hermitagemuseum.org»);
// устанавливаем url, указывая строковый параметр с адресом страницы
inlineKeyboardButton21.setCallbackData(«ПЕРЕХОД НА ВНЕШНИЙ САЙТ»);
rowInline11.add(inlineKeyboardButton21);
// настраиваем разметку всей клавиатуры
rowsInline.add(rowInline1);
rowsInline.add(rowInline2);
…
rowsInline.add(rowInline11);
// добавляем встроенную клавиатуру в сообщение
markupInline.setKeyboard(rowsInline);
message.setReplyMarkup(markupInline);
Отдельно необходимо остановиться на таком параметре как CallbackData. Это обязательный идентификатор, который позволяет боту понять, какая кнопка была нажата. У каждой кнопки – свой индивидуальный идентификатор (фактически id кнопки). Когда пользователь нажимает на кнопку, такой id передается боту, а бот в свою очередь понимает, какая из кнопок была нажата, и впоследствии совершает для пользователя определенные действия (в моем случае – высылает картинку).
Получаем итоговый код метода:
public static SendMessage hermitageInlineKeyboardAb (long chat_id) {
SendMessage message = new SendMessage();
message.setChatId(chat_id);
message.setText(«Чтобы увидеть картины, нажми на фамилию художника или перейди в полную коллекцию на сайте музея»);
InlineKeyboardMarkup markupInline = new InlineKeyboardMarkup();
List<List<InlineKeyboardButton>> rowsInline = new ArrayList<>();
List<InlineKeyboardButton> rowInline1 = new ArrayList<>();
InlineKeyboardButton inlineKeyboardButton1 = new InlineKeyboardButton();
inlineKeyboardButton1.setText(«Аврезе»);
inlineKeyboardButton1.setCallbackData(«АВРЕЗЕ»);
InlineKeyboardButton inlineKeyboardButton2 = new InlineKeyboardButton();
inlineKeyboardButton2.setText(«Аликс Ив»);
inlineKeyboardButton2.setCallbackData(«АЛИКС»);
rowInline1.add(inlineKeyboardButton1);
rowInline1.add(inlineKeyboardButton2);
List<InlineKeyboardButton> rowInline2 = new ArrayList<>();
InlineKeyboardButton inlineKeyboardButton3 = new InlineKeyboardButton();
inlineKeyboardButton3.setText(«Амелин»);
inlineKeyboardButton3.setCallbackData(«АМЕЛИН»);
InlineKeyboardButton inlineKeyboardButton4 = new InlineKeyboardButton();
inlineKeyboardButton4.setText(«Арстер»);
inlineKeyboardButton4.setCallbackData(«АРСТЕР»);
rowInline2.add(inlineKeyboardButton3);
rowInline2.add(inlineKeyboardButton4);
… // иные необходимые ряды кнопок по вышеуказанной аналогии
List<InlineKeyboardButton> rowInline11 = new ArrayList<>();
InlineKeyboardButton inlineKeyboardButton21 = new InlineKeyboardButton();
inlineKeyboardButton21.setText(«Переход на внешний сайт»);
inlineKeyboardButton21.setUrl(«https://collections.hermitagemuseum.org»);
inlineKeyboardButton21.setCallbackData(«ПЕРЕХОД НА ВНЕШНИЙ САЙТ»);
rowInline11.add(inlineKeyboardButton21);
rowsInline.add(rowInline1);
rowsInline.add(rowInline2);
…
rowsInline.add(rowInline11);
markupInline.setKeyboard(rowsInline);
message.setReplyMarkup(markupInline);
return message;
}
В боте это визуализируется следующим образом:
Некоторые разработчики пишут, что экспериментальным путем они выяснили ограничения по количеству кнопок встроенной клавиатуры: не более восьми в ряд и не более ста всего. При этом количество рядов хоть и не ограничено, но в совокупности больше ста кнопок не получается.
2. Обработка действий пользователя при нажатии кнопок встроенной клавиатуры:
В классе, где переопределен метод onUpdateReceived(), прописываем обработку событий, связанных с встроенными клавиатурами. В такой обработке обязательно проводим как минимум 2 проверки того, что пришло от пользователя – текст или CallbackData (то самое id кнопки, что мы устанавливали при создании клавиатур). Без указанных проверок ответных действий от встроенной клавиатуры не последует:
@SneakyThrows
@Override
public void onUpdateReceived(Update update) {
if (update.hasMessage() && update.getMessage().hasText()) {
message_text = update.getMessage().getText();
long chat_id = update.getMessage().getChatId();
// если получен соответствующий текст,
if (message_text.equals(«А-Б»)) {
//то отправляем пользователю нужную встроенную клавиатуру
execute(HermitageInlineKeyboardAb.hermitageInlineKeyboardAb(chat_id));
}
// если же пользователь передал не текст,
// а некое значение - id кнопки (CallbackData)
} else if (update.hasCallbackQuery()) {
// то бот совершает определенные действия
// (в моем случае – отправляет пользователю картинки
// или перенаправляет его на страницу в Интернете)
String call_data = update.getCallbackQuery().getData();
long chat_id = update.getCallbackQuery().getMessage().getChatId();
String path = «D:\\testBot\\testing\\src\\main\\resources\\page.jpg»;
if (call_data.equals(«АВРЕЗЕ»)) {
execute(Sender.sendPhoto(chat_id, path));
} else if (call_data.equals(«ПЕРЕХОД НА ВНЕШНИЙ САЙТ»)) {
execute(Sender.sendMessage(chat_id, ""));
}
Итого
Статей и разъясняющих материалов про встроенные клавиатуры телеграмм ботов с одной стороны предостаточно, однако примеров именно для java не так и много. Надеюсь, этот разбор и реализация InlineKeyboard в проекте, код которого выложен на GitHub, кому-нибудь поможет при разработке своих телеграмм ботов.
Если интересно, как работает мой последний телеграмм бот, то милости прошу: Сам себе галерист.