Reply Markup
Telegram provides two types of reply markup: Custom keyboards and Inline keyboards.
Custom keyboards
Whenever your bot sends a message, it can pass along a special keyboard with predefined reply options. Regular keyboards are represented by ReplyKeyboardMarkup object. You can request a contact or location information from the user with KeyboardButton or send a poll. Regular button will send predefined text to the chat.
Keyboard is an array of button rows, each represented by an array of KeyboardButton objects. KeyboardButton supports text and emoji.
By default, custom keyboards are displayed until a new keyboard is sent by a bot.
Single-row keyboard markup
A ReplyKeyboardMarkup with two buttons in a single row:
// using Telegram.Bot.Types.ReplyMarkups; ReplyKeyboardMarkup replyKeyboardMarkup = new(new[] < new KeyboardButton[] < "Help me", "Call me ☎️" >, >) < ResizeKeyboard = true >; Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "Choose a response", replyMarkup: replyKeyboardMarkup, cancellationToken: cancellationToken);
We specify ResizeKeyboard = true here to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons).
Multi-row keyboard markup
A ReplyKeyboardMarkup with two rows of buttons:
// using Telegram.Bot.Types.ReplyMarkups; ReplyKeyboardMarkup replyKeyboardMarkup = new(new[] < new KeyboardButton[] < "Help me" >, new KeyboardButton[] < "Call me ☎️" >, >) < ResizeKeyboard = true >; Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "Choose a response", replyMarkup: replyKeyboardMarkup, cancellationToken: cancellationToken);
Request information
ReplyKeyboardMarkup containing buttons for contact and location requests using helper methods KeyboardButton.WithRequestLocation and KeyboardButton.WithRequestContact :
// using Telegram.Bot.Types.ReplyMarkups; ReplyKeyboardMarkup replyKeyboardMarkup = new(new[] < KeyboardButton.WithRequestLocation("Share Location"), KeyboardButton.WithRequestContact("Share Contact"), >); Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "Who or Where are you?", replyMarkup: replyKeyboardMarkup, cancellationToken: cancellationToken);
Remove keyboard
To remove keyboard you have to send an instance of ReplyKeyboardRemove object:
// using Telegram.Bot.Types.ReplyMarkups; Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "Removing keyboard", replyMarkup: new ReplyKeyboardRemove(), cancellationToken: cancellationToken);
Inline keyboards
There are times when you’d prefer to do things without sending any messages to the chat. For example, when your user is changing settings or flipping through search results. In such cases you can use Inline Keyboards that are integrated directly into the messages they belong to.
Unlike custom reply keyboards, pressing buttons on inline keyboards doesn’t result in messages sent to the chat. Instead, inline keyboards support buttons that work behind the scenes: callback buttons, URL buttons and switch to inline buttons.
Callback buttons
When a user presses a callback button, no messages are sent to the chat. Instead, your bot simply receives the relevant query. Upon receiving the query, your bot can display some result in a notification at the top of the chat screen or in an alert. In this example we use InlineKeyboardButton.WithCallbackData helper method to create a button with a text and callback data.
// using Telegram.Bot.Types.ReplyMarkups; InlineKeyboardMarkup inlineKeyboard = new(new[] < // first row new [] < InlineKeyboardButton.WithCallbackData(text: "1.1", callbackData: "11"), InlineKeyboardButton.WithCallbackData(text: "1.2", callbackData: "12"), >, // second row new [] < InlineKeyboardButton.WithCallbackData(text: "2.1", callbackData: "21"), InlineKeyboardButton.WithCallbackData(text: "2.2", callbackData: "22"), >, >); Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "A message with an inline keyboard markup", replyMarkup: inlineKeyboard, cancellationToken: cancellationToken);
URL buttons
Buttons of this type have a small arrow icon to help the user understand that tapping on a URL button will open an external link. In this example we use InlineKeyboardButton.WithUrl helper method to create a button with a text and url.
// using Telegram.Bot.Types.ReplyMarkups; InlineKeyboardMarkup inlineKeyboard = new(new[] < InlineKeyboardButton.WithUrl( text: "Link to the Repository", url: "https://github.com/TelegramBots/Telegram.Bot") >); Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "A message with an inline keyboard markup", replyMarkup: inlineKeyboard, cancellationToken: cancellationToken);
Switch to Inline buttons
Pressing a switch to inline button prompts the user to select a chat, opens it and inserts the bot’s username into the input field. You can also pass a query that will be inserted along with the username – this way your users will immediately get some inline results they can share. In this example we use InlineKeyboardButton.WithSwitchInlineQuery and InlineKeyboardButton.WithSwitchInlineQueryCurrentChat helper methods to create buttons which will insert the bot’s username in the chat’s input field.
// using Telegram.Bot.Types.ReplyMarkups; InlineKeyboardMarkup inlineKeyboard = new(new[] < InlineKeyboardButton.WithSwitchInlineQuery( text: "switch_inline_query"), InlineKeyboardButton.WithSwitchInlineQueryCurrentChat( text: "switch_inline_query_current_chat"), >); Message sentMessage = await botClient.SendTextMessageAsync( chatId: chatId, text: "A message with an inline keyboard markup", replyMarkup: inlineKeyboard, cancellationToken: cancellationToken);
Простыми словами об InlineKeyboard в Телеграмм ботах на java
В своей статье на Хабре я уже делилась с читателями первым опытом создания и развёртывании на сервере 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> rowsInline = new ArrayList<>(); // создаем список с кнопками для первого ряда List 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> rowsInline = new ArrayList<>(); List 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 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 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(«ПЕРЕХОД НА ВНЕШНИЙ САЙТ»))
Итого
Статей и разъясняющих материалов про встроенные клавиатуры телеграмм ботов с одной стороны предостаточно, однако примеров именно для java не так и много. Надеюсь, этот разбор и реализация InlineKeyboard в проекте, код которого выложен на GitHub, кому-нибудь поможет при разработке своих телеграмм ботов.
Если интересно, как работает мой последний телеграмм бот, то милости прошу: Сам себе галерист.