Telegram bot получение текста после нажатия на inline keyboard
Разрабатываю Телеграм бота на Java. Имеется кнопка "Send feedback", представляющая собой InlineKeyboardMarkup. При нажатии на неё бот ожидает ввода и отправки сообщения пользователем. (т.е. отправляется новое сообщение с префиксом "/send-feedback " + то что ввёл пользователь) и только я получаю сообщение в своём чате.
Однако, если первый пользователь нажал на FEEDBACK, но ещё не ввёл сообщение, а второй пользователь вызвал любую другую команду, то второй пользователь и отправит этот фидбек с командой, которую вызвал, текстом, а человек который реально нажал его отправить, введёт текст, нажмёт отправить и ему выдаст, что такой команды не существует. Я понимаю, что возможно дело в переменной "prefix", но я не знаю как архитектурно верно сделать многопользовательский ввод, чтобы юзеры не перехватывали команды друг друга и было грамотно. Текущее решение:
// добавил поле
private String prefix = "";
if (update.hasMessage() && update.getMessage().hasText()) {
// здесь добавляю префикс к сообщению
String messageText = prefix + update.getMessage().getText();
long chatId = update.getMessage().getChatId();
// 2. Прилетело сюда
if (messageText.startsWith("/send-feedback")) {
String feedback = messageText.substring(messageText.indexOf(" ") + 1);
new Thread(() -> sendFeedback(chatId, feedback)).start();
//...
} else if (update.hasCallbackQuery()) {
String callbackData = update.getCallbackQuery().getData();
chatIdCallback = update.getCallbackQuery().getMessage().getChatId();
switch (callbackData) {
// 1. Нажатие на кнопку
case "FEEDBACK" -> {
prefix = "/send-feedback ";
// возможность отмены ввода и отправка сообщения
cancelButton(chatIdCallback);
}
// Отмена ввода
case "CANCEL" -> {
prefix = "";
}
// Отмена ввода
private void cancelButton(long chatId) {
SendMessage message = prepareMessage(chatId, "Отменить");
Map<String, String> buttons = new LinkedHashMap<>();
buttons.put("CANCEL", cancelButtonText);
message.setReplyMarkup(InlineKeyboards.inlineKeyboardMaker(buttons));
executeMessage(message);
}
Всё работает корректно пока второй пользователь не встрянет между нажатием кнопки и вводом текста первым пользователем.
Как грамотно реализовать сценарий:
- юзер нажал кнопку клавиатуры "Send feedback"
- ввёл текст отзыва
- нажал отправить сообщение
- сообщение ушло админу
И все эти операции должны быть единым целым. Может как-то потоками реализовать, но не знаю как. Да - неужели у самого бота нет такого функционала!
Весь код здесь https://github.com/mrprogre/avandy-news-bot
Ответы (1 шт):
Создайте класс, который будет хранить состояние для каждого пользователя. Например, UserState:
public class UserState { private String currentState; private String feedbackMessage; // Конструкторы, геттеры, сеттеры }Вместо использования глобальной переменной, используйте Map, чтобы связать идентификатор пользователя с его состоянием. Для примера:
private Map<Long, UserState> userStates = new ConcurrentHashMap<>();Теперь у каждого пользователя может быть свой набор данных, а не общий prefix
Доработайте обработчики сообщений и колбэков так, чтобы они проверяли и обновляли состояние пользователя, используя этот Map:
if (update.hasCallbackQuery()) { long userId = update.getCallbackQuery().getFrom().getId(); String callbackData = update.getCallbackQuery().getData(); switch (callbackData) { case "FEEDBACK" -> { UserState userState = userStates.computeIfAbsent(userId, UserState::new); userState.setCurrentState("AWAITING_FEEDBACK"); // Отобразить пользователю, что он должен ввести отзыв } // Другие случаи... } }Обрабатывайте текстовые сообщения в контексте состояния пользователя:
if (update.hasMessage() && update.getMessage().hasText()) { long userId = update.getMessage().getFrom().getId(); String messageText = update.getMessage().getText(); UserState userState = userStates.get(userId); if (userState != null && "AWAITING_FEEDBACK".equals(userState.getCurrentState())) { // Пользователь отправил отзыв userState.setFeedbackMessage(messageText); userState.setCurrentState("FEEDBACK_RECEIVED"); // Отправить отзыв админу sendFeedback(userId, messageText); } else { // Обработка других текстовых сообщений } }Как только отзыв будет обработан, измените состояние пользователя обратно, чтобы он мог интерактировать с ботом далее:
userState.setCurrentState(null); // или другое состояниеТаким образом, вы сможете отслеживать отдельно поведение каждого пользователя