
Предыстория
В своей статье на Хабре я уже делилась с читателями первым опытом создания и развёртывании на сервере 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, кому-нибудь поможет при разработке своих телеграмм ботов.
