Хотелось попробовать что-нибудь новое в программировании и я задумался: а как делать телеграм-ботов? Их нынче так много, и некоторые из них даже очень полезные и не кривые. Это должно быть не слишком сложно, – подумал я и принялся изучать вопрос. В этой статье и расскажу, как сделать телеграм-бот на Java (даже не ожидал, что на джаве их тоже можно писать).
Описание проекта
В этой статье создам простого бота, который для начала просто будет повторять за вами ваши слова. Позже придумаем какую-нибудь «логику» для реакции на разные команды пользователя (а не только на стандартную команду /start).
Проект пишу на Java, размещать его буду на GitHub и оттуда уже .jar файл запускать на railway (о том, как работать с этим сервисом, уже писал).
Создание бота с @BotFather
Для начала нужно создать бот в телеграме с помощью @BotFather. Отец всех ботов подскажет, что делать, но по шагам (вдруг вы, как и я, не особо часто пользуетесь телегой) требуется:
- Запустить бот командой /start
- Выбрать создание нового бота командой /newbot.
- Написать имя бота – то, под каким названием он будет появляться в списке чатов пользователя.
- Задать юзернейм бота – то, что будет появляться после @.
- Прочитать поздравления от BotFather. В сообщении будет токен, который нам пригодится. Его нужно сохранить и не показывать никому, чтобы случайно не дать к боту несанкционированный доступ.

Код для бота-попугая на Java (без Spring)
Для работы с Telegram API уже существуют готовые библиотеки. Я буду использовать TelegramBots от rubenlagus.
Для сборки проектов я использую Maven, и поэтому в новом джава-проекте в файл pom.xml добавляю необходимую зависимость:
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>6.7.0</version>
</dependency>
Теперь можно создать класс MyBot, в котором будет прописана «логика». Класс является потомком TelegramLongPollingBot и переопределяет несколько его методов.
getBotUsername() возвращает юзернейм бота (должен в точности совпадать с тем, что вы ввели в BotFather)
getBotToken() должен возвращать токен доступа. Но раскрывать его было бы плохой практикой, поэтому вместо самого токена пока верну переменную BOT_TOKEN: System.getenv(«BOT_TOKEN»). Позже в railway добавим эту переменную среды, чтобы не возвращался null и всё работало. Если вы запускаете проект локально, то эту переменную необходимо добавить в переменные среды.
onUpdateReceived() вызывается каждый раз, когда пользователь выполняет какое-то действие: пишет сообщение, команду, нажимает на кнопки. Это основная точка входа, где мы пишем логику.
В итоге класс выглядит следующим образом:
public class MyBot extends TelegramLongPollingBot {
@Override
public String getBotUsername() {
return "MyBot";
}
@Override
public String getBotToken() {
return System.getenv("BOT_TOKEN");
}
@Override
public void onUpdateReceived(Update update) {
if (update.hasMessage() && update.getMessage().hasText()) {
String message = update.getMessage().getText();
long chatId = update.getMessage().getChatId();
SendMessage response = new SendMessage();
response.setChatId(String.valueOf(chatId));
response.setText("Ты написал: " + message);
try {
execute(response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
В строчке update.hasMessage() && update.getMessage().hasText() мы проверяем, действительно ли пользователь прислал текст. Ведь он может прислать стикер или фото – тогда мы его проигнорируем.
Из апдейта мы достаём chatId – уникальный идентификатор чата, который нужен, чтобы отправить ответ туда же (ведь наш бот одновременно может использоваться не одним пользователем).
И в конце мы создаём объект SendMessage, в котором указывает этот chatId (куда отправлять) и текст (что отправлять).
Отправляем это сообщение через метод execute(). Telegram API может выдать ошибку (например, если токен неверный), поэтому оборачиваем это в try/catch.
Метод execute – это ключевой способ отправить запрос к Telegram Bot API. Он используется, чтобы:
- отправить сообщение (
SendMessage) - удалить сообщение (
DeleteMessage) - редактировать сообщение (
EditMessageText,EditMessageReplyMarkup) - отправить фото, стикер и т.д.
Теперь создадим главный класс Main. Именно здесь мы подключаемся к Telegram API и регистрируем нашего бота.
public class Main {
public static void main(String[] args) {
try {
TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
botsApi.registerBot(new MyBot());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Создаём объект TelegramBotsApi, передавая DefaultBotSession.class. Бот будет использовать стандартную сессию на long polling, то есть будет постоянно опрашивать сервер Telegram на наличие новых обновлений.
Регистрируем нашего бота в Telegram API с botsApi.registerBot(). После этой строки бот начинает слушать входящие сообщения и реагировать через метод onUpdateReceived().
В итоге мы получили код, который считывает сообщение пользователя и возвращает его обратно. Теперь нужно проект собрать в .jar файл. Для этого я пишу следующее:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<release>16</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>Main</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Если ваша версия джавы отличается (я использую 16-ю джаву – не спрашивайте, почему), то не забудьте поменять номер релиза. Если название главного класса (где находится метод main()) тоже другое, то не забудьте изменить <mainClass>Main</mainClass>.
После этого собираем проект: mvn clean package. В папке target должен появиться наш .jar файл с зависимостями. Он нам и нужен для запуска на railway, поэтому при загрузке на github кода не добавляйте весь /target в файл .gitignore.
Как только проект собран и залит, можно переходить в railway и там создавать новый проект из репозитория github. Для этого нужно связать гитхаб с аккаунтом railway, и при создании нового проекта просто выбрать из выпадающего списка нужный репозиторий (конечно, сначала надо всё туда запушить).
После этого заходим на вкладку Variables и добавляем переменную BOT_TOKEN, которую мы хотели использовать ранее. Значение переменной должно в точности повторять токен, который выдал BotFather.

Опционально. На всякий случай пропишем ещё одну команду для запуска нужного .jar. Открываем вкладку Settings, находим там Start Command и пишем:
java -jar target/mybot-1.0-jar-with-dependencies.jar
Если имя .jar-файла откличается, то замените его – оно должно быть верным, чтобы railway запустил нужный архив.

После этого снова делаем деплой, и может проверять наш бот. Он должен отвечать вам. Готово!
Добавление команд к боту
Пока что этот бот ведёт себя, как попугай и ничего полезного не делает. Попробуем добавить хоть какую-нибудь логику.
Сделаем так, чтобы пользователь мог писать не только текст, но и команды (они в телеграме начинаются со слэша / ). По команде /question бот будет возвращать тот же текст, но с вопросительным знаком. По команде /exclaim – с восклицательным. Конечно, это не самое умное поведение, но неплохо покажет, как работать с разными командами.
Для анализа команды просто смотрим на строку. Если она начинается с зарегистрированной команды, то выполняем нужное действие. Если нет, то так же, как раньше, возвращаем написанный текст.
@Override
public void onUpdateReceived(Update update) {
if (update.hasMessage() && update.getMessage().hasText()) {
String message = update.getMessage().getText();
long chatId = update.getMessage().getChatId();
String reply;
if (message.startsWith("/question")) {
String text = message.replaceFirst("/question\\s*", "");
reply = text + "?";
} else if (message.startsWith("/exclaim")) {
String text = message.replaceFirst("/exclaim\\s*", "");
reply = text + "!";
} else {
reply = "Ты написал: " + message;
}
SendMessage response = new SendMessage();
response.setChatId(String.valueOf(chatId));
response.setText(reply);
try {
execute(response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
В коде мы просто убираем команду из начала строки (заменяем её со всеми последующими пробелами на пустую строку).
Теперь бот в ответ на /question ты дурак спросит ты дурак? А в ответ на /exclaim вау закричит вау!
Всё довольно просто, но со временем можно добавлять больше команд, или реализовать более сложную логику (забирать часть данных на хранение). Сегодня остановимся на этом достижении, а в следующей статье покажу, как добавлять кнопки меню (чтобы пользователю было удобнее ориентироваться в возможных действиях в боте) и начнём хранить информацию о его действиях (например, для поэтапного ввода данных – и бот при этом не будет забывать, на каком этапе мы находимся).