Статьи
YouTube-канал Отзывы

Telegram-бот на Kotlin и Spring Boot

Видеогайд Исходники

14 марта 2023

Тэги: gradle, Kotlin, Spring, Spring Boot, YouTube, руководство.

Содержание

  1. Регистрация бота в Telegram
  2. Создание бота на Spring Boot
  3. Обработка запросов от пользователя
  4. Добавляем кнопки
  5. Заключение

В настоящее время чат-боты в Telegram не делал только ленивый. Они плотно вошли в нашу жизнь и почти у каждой компании есть бот, решающий какие-то бизнесовые задачи, тем самым разгружая «живых» сотрудников. После прочтения этой статьи вы сможете создать и запустить свой чат-бот в Telegram.

Также данный материал доступен в формате видео на YouTube. Пример готового приложения можно найти на github.

Регистрация бота в Telegram

Сперва нам нужно выбрать подходящее имя для бота и зарегистрировать его в Telegram. Регистрация нового бота происходит через бота по имени BotFather. Просто найдите его через поиск контактов Telegram. В чате вы всегда можете понять, что общаетесь с ботом, т.к. рядом с его именем есть подпись «bot». BotFather позволяет управлять вашими ботами в диалоговом режиме. Команды боту представляют собой текст, начинающийся со слеша.

Для создания нового бота отправьте команду /newbot. Вам будет предложено ввести имя бота. На данном шаге постарайтесь не использовать слово «bot» в названии. Если выбранное вами имя не занято, то далее вам будет предложено ввести логин для этого бота. Причём он должен заканчиваться на «bot». Если логин не занят, то вам будет сгенерирован access token для работы с Telegram API по http. Сохраните этот токен – он нам понадобится далее.

Создание бота на Spring Boot

За основу нашего чат-бота возьмём Spring Boot. Код будем писать на Kotlin. Воспользуемся сайтом Spring Initializr для создания заготовки нашего приложения. В настройках выберем Gradle-Kotlin и язык Kotlin, в качестве зависимости нам здесь будет достаточно только Spring Web. Скачаем заготовку проекта и откроем файл build.gradle.kts. Проверьте, что в секции dependencies присутствует org.springframework.boot:spring-boot-starter-web. Также добавим туда библиотеку для работы с Telegram org.telegram:telegrambots-spring-boot-starter:6.5.0.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.telegram:telegrambots-spring-boot-starter:6.5.0")
    // другие зависимости
}

Теперь создадим новый сервис. В Telegram ботов можно подключать двумя способами: long polling и web-hook. В случае с long polling наше приложение кидает запрос и ждёт ответа от сервера telegram. Сервер ответит не сразу, а только тогда, когда произойдёт какое-либо событие (например, сообщение от пользователя). А в случае с webhook сервер telegram сам будет дёргать заранее зарегистрированные эндпоинты нашего приложения. В подключенной нами библиотеке поддерживаются оба варианта, но webhook чуть сложнее в настройке. Поэтому рассмотрим long polling.

Унаследуем наш сервис от класса TelegramLongPollingBot. Этот абстрактный класс потребует от нас реализации методов getBotUsername(), и onUpdateReceived(). Также в конструкторе базовому классу следует передать токен, который мы получили при регистрации бота. Однако его нельзя хардкодить в виде константы. Он должен подгружаться из конфига.

@Service
class DevmarkBot(
    @Value("\${telegram.token}")
    token: String
) : TelegramLongPollingBot(token) {

    @Value("\${telegram.botName}")
    private val botName: String = ""

    override fun getBotUsername(): String = botName

Благодаря аннотации @Value Spring сам подставит параметры из файла application.properties. Пропишем их в этом файле, а лучше сразу его переименуем в application.yml, чтобы писать в yaml-формате:

telegram:
  botName: devmark_ru_bot
  token: ${TOKEN}

Здесь мы указываем имя бота (telegram.botName) в явном виде, а вот токен (telegram.token) подгружаем из переменной окружения, т.к. этот токен должен сохраняться в секрете. Переменную окружения можно указывать при запуске приложения из командной строки через опцию -D или непосредственно в Idea.

Обработка запросов от пользователя

Теперь вернёмся к нашему сервису и реализуем метод onUpdateReceived().

override fun onUpdateReceived(update: Update) {
    if (update.hasMessage()) {
        val message = update.message
        val chatId = message.chatId
        val responseText = if (message.hasText()) {
            val messageText = message.text
            when {
                messageText == "/start" -> "Добро пожаловать!"
                else -> "Вы написали: *$messageText*"
            }
        } else {
            "Я понимаю только текст"
        }
        sendNotification(chatId, responseText)
    }
}

В начале мы проверяем объект типа Update на наличие сообщения с помощью метода hasMessage(). Далее, извлекаем chatId (уникальный идентификатор пользователя в telegram). Затем проверяем, что входящее сообщение содержит текст (а не стикер, к примеру). Если сообщение от пользователя равно строке «/start», то мы приветствуем пользователя. Дело в том, что именно такое сообщение отправляется, когда вы впервые подключаетесь к боту и жмёте кнопку «Start». Для любого другого текста мы просто дублируем его в ответе. Если же текста нет, бот ответит, что понимает только текст.

Отправка сообщения происходит во вспомогательном методе sendNotification().

private fun sendNotification(chatId: Long, responseText: String) {
    val responseMessage = SendMessage(chatId.toString(), responseText)
    responseMessage.enableMarkdown(true)
    execute(responseMessage)
}

В нём мы создаём объект ответа, заполняя chatId и текст ответа. Затем с помощью метода enableMarkdown() включаем режим разметки Markdown. Этот режим позволяет делать простое форматирование текста. Например, текст, обрамлённый с двух сторон символами звёздочки будет отображаться жирным. Затем для отправки сообщения вызываем метод execute() из родительского класса.

Добавляем кнопки

Указанный выше способ взаимодействия между пользователем и ботом довольно универсален. Однако чтобы пользователю не набирать каждый раз одни и те же команды, а также чтобы исключить вероятность опечатки, мы можем предоставить ему несколько заранее заготовленных команд в виде кнопок. При нажатии на кнопку бот получит ровно тот текст, который на ней написан. В этом смысле нет разницы между нажатием на кнопку и набором этого текста вручную.

Давайте сделаем так, чтобы пользователю отображалось 4 кнопки, по 2 в ряд. Для этого модифицируем наш метод отправки сообщения пользователю:

private fun sendNotification(chatId: Long, responseText: String) {
    val responseMessage = SendMessage(chatId.toString(), responseText)
    responseMessage.enableMarkdown(true)
    // добавляем 4 кнопки
    responseMessage.replyMarkup = getReplyMarkup(
        listOf(
            listOf("Кнопка 1", "Кнопка 2"),
            listOf("Кнопка 3", "Кнопка 4")
        )
    )
    execute(responseMessage)
}

Тут мы просто указываем свойство replyMarkup, для чего вызываем метод getReplyMarkup(), передавая ему на вход список списков строк. Сам метод выглядит примерно так:

private fun getReplyMarkup(allButtons: List<List<String>>): ReplyKeyboardMarkup {
    val markup = ReplyKeyboardMarkup()
    markup.keyboard = allButtons.map { rowButtons ->
        val row = KeyboardRow()
        rowButtons.forEach { rowButton -> row.add(rowButton) }
        row
    }
    return markup
}

Создаём объект ReplyKeyboardMarkup. Как нетрудно догадаться по названию, он отвечает за разметку кнопок. Затем проходимся по каждой строке, создавая KeyboardRow и заполняем её кнопками. Для создания кнопки требуется указать только текст.

Наконец, немного модифицируем наш обработчик запросов:

when {
    messageText == "/start" -> "Добро пожаловать!"
    messageText.startsWith("Кнопка ") -> "Вы нажали кнопку" // обработка нажатия кнопки
    else -> "Вы написали: *$messageText*"
}

Как только пользователь отправит сообщение, начинающееся со строки «Кнопка », мы определим, что пользователь нажал именно кнопку.

Заключение

Как видите, Spring Boot позволяет запустить чат-бот с минимальным количеством усилий. Вам же остаётся только реализовать саму обработку сообщения согласно вашей бизнес-логике.

Подавляющее большинство чат-ботов работает именно с текстовой информацией. Помимо применения в бизнесе также известно немало успешных примеров реализации текстовых игр и даже MMORPG. Именно поэтому чат-боты Telegram сейчас используются повсеместно.


Облако тэгов

Kotlin, Java, Java 11, Java 8, Spring, Spring Boot, Spring Data, SQL, PostgreSQL, Oracle, Linux, Hibernate, Collections, Stream API, многопоточность, файлы, Nginx, Apache, maven, gradle, JUnit, YouTube, новости, руководство, ООП, алгоритмы, головоломки, rest, GraphQL, Excel, XML, json, yaml.

Последние статьи


Комментарии

10.07.2022 19:40 Данил

круто! ждём продолжения темы, про webhook очень интересно

02.10.2022 17:44 Димон

Я не понял, а как зупускать бота?

02.10.2022 21:53 devmark

Просто запускаем приложение. Либо из вашей среды разработки (для тестирования), либо "java -jar имя_файла" из консоли. Если в конфигурационных файлах правильно указан токен и имя бота, то ваш бот начнёт принимать сообщения.

01.02.2023 16:23 Ринат

Добрый день. Подскажите как добавить кнопки в сообщение? Т.е к примеру пользователь нажал на кнопку 1 а ему приходит в сообщение к примеру для выбора 3 кнопки. И что бы кнопки были по размеру самого большого текста в них
Спасибо!

04.02.2023 01:18 devmark

Набор кнопок вы можете формировать в каждом ответном сообщении (см. метод sendNotification()). Чтобы разместить три кнопки в ряд, вам нужно добавить их в первом внутреннем списке (где в примере кнопки 1 и 2). А второй внутренний список можно удалить.

Что касается размеров кнопок - это зависит от конкретного telegram-клиента и все кнопки автоматически отображаются одинакового размера.

02.03.2023 16:26 Draccul

Объясните поподробнее. как вынести бота из среды разработки, что бы запускать как программу на компьютере без лишнего по

02.03.2023 16:39 devmark

Так же, как и любое другое приложение на Java. Сначала собираем его из командной строки (если у вас gradle):
./gradlew clean build
затем запускаем
java -jar build/libs/имя_проекта.jar

03.03.2023 08:28 Draccul

А как должно выглядеть описание в Манифесте, иначе он не запускается

03.03.2023 15:14 devmark

Какой манифест имеется в виду? Вручную ничего прописывать не надо. Также не забудьте, что для запуска бота требуется установить переменную окружения TOKEN, в которую прописать токен доступа к вашему боту в Telegram.

04.03.2023 09:11 Draccul

Выдает при запуске вот такую ошибку, запускаю через командную строку...
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
        at MainKt.main(Main.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 1 more

04.03.2023 19:17 devmark

Скорее всего, у вас некорректный build.gradle.kts. Можно сгенерить его заново через сайт start.spring.io. Вам там нужно выбрать Gradle (Kotlin dsl), kotlin. Из зависимостей можно сразу добавить Spring Web, а затем вручную добавить зависимость telegram, как написано в статье.

08.03.2023 21:31 vitnine

Библиотека поддерживает корутины?

14.03.2023 00:34 devmark

Библиотека поддерживает как синхронное, так и асинхронное взаимодействие.

Я обновил статью и проект с учётом последних версий библиотек на текущий момент. Если у кого-то пример не запускается, попробуйте последнюю версию из гитхаба.

02.04.2023 20:59 Ринат

Подскажите как отправлять файлы пользователю? К примеру нужно что бы при нажатии кнопки бот отправлял файл пользователю?
Спасибо!

Добавить комментарий

×

devmark.ru