Предыстория 

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

Если интересно, как работает мой последний телеграмм бот, то милости прошу: Сам себе галерист.

Комментарии (0)