Телеграм-бот «Java ассистент»

Работа с кнопками (inline keyboards)

Добавим простую логику в наш бот: выдача подходящего курса Java, соответствующего уровню участника.
Для этого подключим работу с командами Telegram и с кнопками (Lesson 6 в Telegram Bot Tutorial)
В ветке master примените патч 2_1_keyboard из архива патчей
  • Команды Telegram — это специальные сообщения, начинающиеся со знака "/" (косая черта), которые используются для запуска определённых действий в ботах. Например, пользователь может отправить /start, чтобы начать общение с ботом, или /help, чтобы получить справку. Для работ с командами добавим класс CommandHandler
  • Добавляем утильный класс KeyboardHandler для создания кнопок. Кнопки можно помещать в один ряд и создавать из них несколько рядов, callbackData - это данные, которые возвратит бот при нажатии на кнопку.
  • Добавляем метод ClientHandler#sendMdAndKeyboard, который отсылает в бота markdown-сообoщение вместе с созданными кнопками.
  • В UpdateHandler добавляем
    • InlineKeyboardMarkup YES_NO_KEYBOARD - простой ряд с кнопками Yes/No
    • Метод getDataFromCallbackQuery для получения callbackData нажатой кнопки
  • Мы сразу готовим код для большого проекта, поэтому создали отдельный класс Util, пока там только метод проверки на null
  • Наконец в классе-реализации бота реализуем простую логику:
    • Ответ на команду /help
    • Начало диалога при команде /start
    • Обработка ответа на YES_NO_KEYBOARD
    • Обработка ответа оценки уровня знания Java участника ("How high out of 10 would you rate your Java knowledge?")
Запустите этого бота, подебажте его и поиграйтесь с ответами.

Хранение состояния

Telegram API не хранит состояния (stateless протокол). Если нам нужен диалог с пользователей с сохранением состояния, необходимо сделать его на стороне приложения. При этом, если нам важно, чтобы он не прерывался при рестарте приложения, его нужно хранить в базе данных приложения, или, например, в MapDB. Однако рестарт приложение - событие редкое и, если диалог недолгий, мы можем хранить состояние просто в памяти.
В ветке master примените патч 2_2_state из архива патчей
  • Т.к. мы создаем основу реального приложения, которое работает с множеством пользователей, в pom.xml подключим
    in-memory cache Caffeine, чтобы удалять состояние после заданного промежутка времени.
  • Добавляем класс хранения состояний States с ивалидацией .expireAfterWrite(120, TimeUnit.MINUTES). В одном приложении мы можем запускать несколько ботов, поэтому делаем этот класс Generic.
  • В UpdateHandler добавлем универсальный обработчик каллбэков от YES_NO_KEYBOARD
  • Наконец, в AIBot мы делаем новую логику, которую не смогли бы реализовать без сохранения состояния. На каждом шаге мы или завершаем диалог, или сохраняем состояние, которое будет обрабатываться на следующем шаге: states.update(tgId, nextStage)

Подключаем Spring Boot

Подключим Spring Boot 4 и используем его для конфигурирования токена:
В ветке master примените патч 2_3_spring_boot из архива патчей
  • Делаем класс AIBot бином Spring (можем сюда инжектить, например, репозитории для хранения пользователей и результатов), инжектим в него значение @Value("${BOT_HTTP_API_TOKEN}") и через @PostConstruct инициализируем ClientHandler
  • AIBotMain аннотируем @SpringBootApplication и через lombok @RequiredArgsConstructor инжектим сюда AIBot
  • Запустить проект можно с переменной окружения BOT_HTTP_API_TOKEN или с профилем dev, сделав для него конфигурацию:
    в корне проекта создайте \config\application-dev.yaml (скопируйте в \config из application-dev.yaml и замените свой BOT_HTTP_API_TOKEN). В запуске нужно указать профиль:

Присоединяйся к нашей группе Java-разработчиков в Telegram

Если курс принес пользу - большая просьба сделать отзыв
или оценить звездочками этот курс на Stepik

В разработке: интеграция с ИИ и Spring AI. Следите за рассылкой