Как написать телеграмм бота на java

Время на прочтение
15 мин

Количество просмотров 121K

В следующих сериях

Это первая статья в моей серии «для самых маленьких» — следующая посвящена Telegram-боту на вебхуках на Spring с

блекджеком и

Redis и клавиатурами. Будут ещё:)

Для кого написано

Если вы ни разу не писали Telegram-ботов на Java и только начинаете разбираться — эта статья для вас. В ней подробно и с пояснениями описано создание реального бота, автоматизирующего одну конкретную функцию. Можно использовать статью как мануал для создания скелета своего бота, а потом подключить его к своей бизнес-логике.

Предыстория

Когда моя дочь начала изучать арифметику, я между делом накидал алгоритм генерации простых примеров на сложение и вычитание вида «5 + 7 =», чтобы не придумывать и не гуглить для неё задания.

И тут на глаза попалась новость, что Telegram выпустил новую версию Bot API 5.0. Ботов я раньше не писал, и потому решил попробовать поднять бота как интерфейс для своей поделки. Все примеры, которые мне удалось найти, показались либо совсем простыми (нужные мне функции не были представлены), либо очень сложными для новичка. Также мне не хватало объяснений, почему выбран тот или иной путь. В общем, написано было сразу для умных, а не для меня. Потому я решил описать свой опыт создания простого бота — надеюсь, кому-нибудь это поможет быстрее въехать в тему.

Что в статье есть, чего нет

В статье есть про:

  • создание бекенда не-инлайн бота на Java 11 с использованием Telegram Bot Api 5.0;
  • обработка команд вида /dosomething;
  • обработка текстовых сообщений, не являющихся командами (т.е. не начинающихся с «/»);
  • отправку пользователю текстовых сообщений и файлов;
  • деплой и запуск бота на heroku.

В статье нет про:

  • использование функций ботов, не перечисленных выше (например, создание клавиатур — я их сначала добавил, но в итоге они мне просто не пригодились);
  • создание списка заданий;
  • работу с Word-документом;
  • обеспечивающие функции — логирование, тесты и т.п.;
  • общение с BotFather (создание бота, получение его токена и формирование списка команд подробно и понятно описано во многих источниках, вот первый попавшийся мануал).

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

Бизнес-функции бота

Бот позволяет:

  • выдавать пользователю справочную текстовую информацию в ответ на команды /start, /help и /settings;
  • обрабатывать и запоминать пользовательские настройки, направленные текстовым сообщением заданного формата. Настроек три — минимальное + максимальное число, используемые в заданиях, и количество страниц выгружаемого файла;
  • оповещать пользователя о несоблюдении им формата сообщения;
  • формировать Word-файл с заданиями на сложение, вычитание или вперемешку в ответ на команды /plus, /minus и /plusminus с использованием дефолтных или установленных пользователем настроек.

Можно потыкать — MentalCalculationBot (должен работать). Выглядит так:

Порядок разработки

  • разобраться с зависимостями;
  • создать класс бота и реализовать обработку текстовых сообщений пользователя, не являющихся командами;
  • создать классы команд;
  • прописать запуск приложения;
  • задеплоить на heroku.

Ниже подробно расписан каждый пункт.

Зависимости

Для управления зависимостями использовался Apache Maven. Нужные зависимости — собственно Telegram Bots и Lombok, использовавшийся для упрощения кода (заменяет стандартные java-методы аннотациями).

Вот что вышло в

pom.xml

    <groupId>***</groupId>
    <artifactId>***</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>***</name>
    <description>***</description>
    <packaging>jar</packaging>

    <properties>
        <java.version>11</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <org.projectlombok.version>1.18.16</org.projectlombok.version>
        <apache.poi.version>4.1.2</apache.poi.version>
        <telegram.version>5.0.1</telegram.version>
    </properties>

    <dependencies>
        <!-- Telegram API -->
        <dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambots</artifactId>
            <version>${telegram.version}</version>
        </dependency>
        <dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambotsextensions</artifactId>
            <version>${telegram.version}</version>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${org.projectlombok.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <release>${java.version}</release>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${org.projectlombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.2</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>
        </plugins>
    </build>

Класс бота и обработка текстовых сообщений

Мой класс Bot унаследован от TelegramLongPollingCommandBot, который, в свою очередь, наследуется от более распространённого в примерах TelegramLongPollingBot. Он хорош тем, что в нём уже реализованы приём и обработка команд — то есть сообщений, начинающихся с «/». Можно создавать отдельные классы команд (подробнее о них ниже), инициализировать их в конструкторе бота и уже в них писать логику их обработки.

В классе Bot таким образом остаётся только логика обработки текстовых сообщений, не являющихся командами. В моём случае это пользовательские настройки или мусорные сообщения, не соответствующие формату. Для лаконичности логику их обработки тоже стоит вынести в отдельный вспомогательный класс, вызывая его метод из переопределенного метода processNonCommandUpdate(Update update) класса Bot.

В качестве параметров для инициализации бота используются его имя и токен, полученные от BotFather. В ходе разработки удобно создать тестового бота и прописать его параметры прямо в коде, чтобы проще было запускать бекенд локально и отлаживать прямо в Telegram, однако перед релизом стоит пересоздать бота и вынести эти настройки из кода — например, в переменные окружения (об этом ниже).

Получился вот такой

Bot.java

import lombok.Getter;
import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import ru.taksebe.telegram.mentalCalculation.telegram.commands.operations.MinusCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.commands.operations.PlusCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.commands.operations.PlusMinusCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.commands.service.HelpCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.commands.service.SettingsCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.commands.service.StartCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.nonCommand.NonCommand;
import ru.taksebe.telegram.mentalCalculation.telegram.nonCommand.Settings;

import java.util.HashMap;
import java.util.Map;

public final class Bot extends TelegramLongPollingCommandBot {
    private final String BOT_NAME;
    private final String BOT_TOKEN;

    //Настройки по умолчанию
    @Getter
    private static final Settings defaultSettings = new Settings(1, 15, 1);

    //Класс для обработки сообщений, не являющихся командой
    private final NonCommand nonCommand;

    /**
     * Настройки файла для разных пользователей. Ключ - уникальный id чата
     */
    @Getter
    private static Map<Long, Settings> userSettings;

    public Bot(String botName, String botToken) {
        super();
        this.BOT_NAME = botName;
        this.BOT_TOKEN = botToken;
        //создаём вспомогательный класс для работы с сообщениями, не являющимися командами
        this.nonCommand = new NonCommand();
        //регистрируем команды
        register(new StartCommand("start", "Старт"));
        register(new PlusCommand("plus", "Сложение"));
        register(new MinusCommand("minus", "Вычитание"));
        register(new PlusMinusCommand("plusminus", "Сложение и вычитание"));
        register(new HelpCommand("help","Помощь"));
        register(new SettingsCommand("settings", "Мои настройки"));
        userSettings = new HashMap<>();
    }

    @Override
    public String getBotToken() {
        return BOT_TOKEN;
    }

    @Override
    public String getBotUsername() {
        return BOT_NAME;
    }

    /**
     * Ответ на запрос, не являющийся командой
     */
    @Override
    public void processNonCommandUpdate(Update update) {
        Message msg = update.getMessage();
        Long chatId = msg.getChatId();
        String userName = getUserName(msg);

        String answer = nonCommand.nonCommandExecute(chatId, userName, msg.getText());
        setAnswer(chatId, userName, answer);
    }

    /**
     * Получение настроек по id чата. Если ранее для этого чата в ходе сеанса работы бота настройки не были установлены, используются настройки по умолчанию
     */
    public static Settings getUserSettings(Long chatId) {
        Map<Long, Settings> userSettings = Bot.getUserSettings();
        Settings settings = userSettings.get(chatId);
        if (settings == null) {
            return defaultSettings;
        }
        return settings;
    }

    /**
     * Формирование имени пользователя
     * @param msg сообщение
     */
    private String getUserName(Message msg) {
        User user = msg.getFrom();
        String userName = user.getUserName();
        return (userName != null) ? userName : String.format("%s %s", user.getLastName(), user.getFirstName());
    }

    /**
     * Отправка ответа
     * @param chatId id чата
     * @param userName имя пользователя
     * @param text текст ответа
     */
    private void setAnswer(Long chatId, String userName, String text) {
        SendMessage answer = new SendMessage();
        answer.setText(text);
        answer.setChatId(chatId.toString());
        try {
            execute(answer);
        } catch (TelegramApiException e) {
            //логируем сбой Telegram Bot API, используя userName
        }
    }
}

Класс обработки текстовых сообщений

NonCommand.java

import ru.taksebe.telegram.mentalCalculation.exceptions.IllegalSettingsException;
import ru.taksebe.telegram.mentalCalculation.telegram.Bot;

/**
 * Обработка сообщения, не являющегося командой (т.е. обычного текста не начинающегося с "/")
 */
public class NonCommand {

    public String nonCommandExecute(Long chatId, String userName, String text) {
        Settings settings;
        String answer;
        try {
            //создаём настройки из сообщения пользователя
            settings = createSettings(text);
            //добавляем настройки в мапу, чтобы потом их использовать для этого пользователя при генерации файла
            saveUserSettings(chatId, settings);
            answer = "Настройки обновлены. Вы всегда можете их посмотреть с помощью /settings";
            //логируем событие, используя userName
        } catch (IllegalSettingsException e) {
            answer = e.getMessage() +
                    "nn Настройки не были изменены. Вы всегда можете их посмотреть с помощью /settings";
            //логируем событие, используя userName
        } catch (Exception e) {
            answer = "Простите, я не понимаю Вас. Возможно, Вам поможет /help";
            //логируем событие, используя userName
        }
        return answer;
    }

    /**
     * Создание настроек из полученного пользователем сообщения
     * @param text текст сообщения
     * @throws IllegalArgumentException пробрасывается, если сообщение пользователя не соответствует формату
     */
    private Settings createSettings(String text) throws IllegalArgumentException {
        //отсекаем файлы, стикеры, гифки и прочий мусор
        if (text == null) {
            throw new IllegalArgumentException("Сообщение не является текстом");
        }
        //создаём из сообщения пользователя 3 числа-настройки (min, max, listCount) либо пробрасываем исключение о несоответствии сообщения требуемому формату
        return new Settings(min, max, listCount);
    }

    /**
     * Добавление настроек пользователя в мапу, чтобы потом их использовать для этого пользователя при генерации файла
     * Если настройки совпадают с дефолтными, они не сохраняются, чтобы впустую не раздувать мапу
     * @param chatId id чата
     * @param settings настройки
     */
    private void saveUserSettings(Long chatId, Settings settings) {
        if (!settings.equals(Settings.getDefaultSettings())) {
            Bot.getUserSettings().put(chatId, settings);
        } else {
            Bot.getUserSettings().remove(chatId);
        }
    }
}

Классы команд

Все классы команд наследуются от BotCommand.

Команды в моём боте делятся на 2 группы:

  • Сервисные — возвращают справочную информацию;
  • Основные — формируют файл с заданиями.

Структура классов команд для этих групп идентична — в абстрактном суперклассе реализуются общие методы, в наследуемые от него классах отдельных команд вынесена их кастомная логика. Разница лишь в том, что Основные команды обращаются к внешним классам, где реализована бизнес-логика, в то время как Сервисные просто формируют текстовый ответ.

Начнём с более простых Сервисных команд. В абстрактный суперкласс вынесен метод отправки пользователю ответа, а в классах команд формируется текст ответа.

Абстрактный суперкласс Сервисных команд

ServiceCommand.java

import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.bots.AbsSender;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

/**
 * Суперкласс для сервисных команд
 */
abstract class ServiceCommand extends BotCommand {

    ServiceCommand(String identifier, String description) {
        super(identifier, description);
    }

    /**
     * Отправка ответа пользователю
     */
    void sendAnswer(AbsSender absSender, Long chatId, String commandName, String userName, String text) {
        SendMessage message = new SendMessage();
        //включаем поддержку режима разметки, чтобы управлять отображением текста и добавлять эмодзи
        message.enableMarkdown(true);
        message.setChatId(chatId.toString());
        message.setText(text);
        try {
            absSender.execute(message);
        } catch (TelegramApiException e) {
            //логируем сбой Telegram Bot API, используя commandName и userName
        }
    }
}

Класс Сервисной команды на примере

StartCommand.java

import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;

/**
 * Команда "Старт"
 */
public class StartCommand extends ServiceCommand {

    public StartCommand(String identifier, String description) {
        super(identifier, description);
    }

    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
        //формируем имя пользователя - поскольку userName может быть не заполнено, для этого случая используем имя и фамилию пользователя
        String userName = (user.getUserName() != null) ? user.getUserName() :
                String.format("%s %s", user.getLastName(), user.getFirstName());
        //обращаемся к методу суперкласса для отправки пользователю ответа
        sendAnswer(absSender, chat.getId(), this.getCommandIdentifier(), userName,
                "Давайте начнём! Если Вам нужна помощь, нажмите /help");
    }
}

В суперклассе Основных команд, помимо аналогичного метода отправки ответов, содержится формирование Word-документа.

OperationCommand.java

import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand;
import org.telegram.telegrambots.meta.api.methods.send.SendDocument;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.InputFile;
import org.telegram.telegrambots.meta.bots.AbsSender;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import ru.taksebe.telegram.mentalCalculation.calculation.Calculator;
import ru.taksebe.telegram.mentalCalculation.calculation.PlusMinusService;
import ru.taksebe.telegram.mentalCalculation.enums.OperationEnum;
import ru.taksebe.telegram.mentalCalculation.fileProcessor.WordFileProcessorImpl;
import ru.taksebe.telegram.mentalCalculation.telegram.Settings;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

/**
 * Суперкласс для команд создания заданий с различными операциями
 */
abstract class OperationCommand extends BotCommand {
    private PlusMinusService service;

    OperationCommand(String identifier, String description) {
        super(identifier, description);
        this.service = new PlusMinusService(new WordFileProcessorImpl(), new Calculator());
    }

    /**
     * Отправка ответа пользователю
     */
    void sendAnswer(AbsSender absSender, Long chatId, List<OperationEnum> operations, String description, String commandName, String userName) {
        try {
            absSender.execute(createDocument(chatId, operations, description));
        } catch (IOException | IllegalArgumentException e) {
            sendError(absSender, chatId, commandName, userName);
            e.printStackTrace();
        } catch (TelegramApiException e) {
            //логируем сбой Telegram Bot API, используя commandName и userName
        }
    }

    /**
     * Создание документа для отправки пользователю
     * @param chatId id чата
     * @param operations список типов операций (сложение и/или вычитание)
     * @param fileName имя, которое нужно присвоить файлу
     */
    private SendDocument createDocument(Long chatId, List<OperationEnum> operations, String fileName) throws IOException {
        FileInputStream stream = service.getPlusMinusFile(operations, Bot.getUserSettings(chatId));
        SendDocument document = new SendDocument();
        document.setChatId(chatId.toString());
        document.setDocument(new InputFile(stream, String.format("%s.docx", fileName)));
        return document;
    }

    /**
     * Отправка пользователю сообщения об ошибке
     */
    private void sendError(AbsSender absSender, Long chatId, String commandName, String userName) {
        try {
            absSender.execute(new SendMessage(chatId.toString(), "Похоже, я сломался. Попробуйте позже"));
        } catch (TelegramApiException e) {
            //логируем сбой Telegram Bot API, используя commandName и userName
        }
    }
}

Класс Основной команды на примере

PlusMinusCommand.java

import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
import ru.taksebe.telegram.mentalCalculation.enums.OperationEnum;

/**
 * Команда получение файла с заданиями на сложение и вычитание
 */
public class PlusMinusCommand extends OperationCommand {

    public PlusMinusCommand(String identifier, String description) {
        super(identifier, description);
    }

    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {
        //формируем имя пользователя - поскольку userName может быть не заполнено, для этого случая используем имя и фамилию пользователя
        String userName = (user.getUserName() != null) ? user.getUserName() :
                String.format("%s %s", user.getLastName(), user.getFirstName());
        //обращаемся к методу суперкласса для формирования файла на сложение и вычитание (за это отвечает метод getPlusMinus() перечисления OperationEnum) и отправки его пользователю
        sendAnswer(absSender, chat.getId(), OperationEnum.getPlusMinus(), this.getDescription(), this.getCommandIdentifier(), userName);
    }
}

Приложение

В методе main инициализируется TelegramBotsApi, в котором и регистрируется Bot.

TelegramBotsApi в качестве параметра принимает Class<? extends BotSession>. Если нет никаких заморочек с прокси, можно использовать DefaultBotSession.class.

Чтобы получать имя и токен бота как переменные окружения, необходимо использовать System.getenv().

Получаем вот такой

MentalCalculationApplication.java

import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
import ru.taksebe.telegram.mentalCalculation.telegram.Bot;

import java.util.Map;

public class MentalCalculationApplication {
    private static final Map<String, String> getenv = System.getenv();

    public static void main(String[] args) {
        try {
            TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
            botsApi.registerBot(new Bot(getenv.get("BOT_NAME"), getenv.get("BOT_TOKEN")));
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }
}

Деплой на heroku

Для начала нужно создать в корне проекта файл Procfile и написать в него одну строку:
worker: java -Xmx300m -Xss512k -XX:CICompilerCount=2 -Dfile.encoding=UTF-8 -cp ./target/classes:./target/dependency/* <путь до приложения, в моём случае ru.taksebe.telegram.mentalCalculation.MentalCalculationApplication>
, где worker — это тип процесса.

Если в проекте используется версия Java, отличная от 8, также необходимо создать в корне проекта файл system.properties и прописать в нём одну строку:
java.runtime.version=<версия Java>

Далее порядок такой:

  1. Регистрируемся на heroku и идём в консоль;
  2. mvn clean install;
  3. heroku login — после выполнения потребуется нажать любую клавишу и залогиниться в открывшемся окне браузера;
  4. heroku create <имя приложения> — создаём приложение на heroku;
  5. git push heroku master — пушим в репозиторий heroku;
  6. heroku config:set BOT_NAME=<имя бота> — добавляем имя бота в переменные окружения;
  7. heroku config:set BOT_TOKEN=<токен бота> — добавляем токен бота в переменные окружения;
  8. heroku config:get BOT_NAME (аналогично BOT_TOKEN) — убеждаемся, что переменные окружения установлены верно;
  9. heroku ps:scale worker=1 — устанавливаем количество контейнеров (dynos) для типа процесса worker (ранее мы выбрали этот тип в Procfile), при этом происходит рестарт приложения;
  10. В интерфейсе управления приложением в личном кабинете на heroku переходим к логам (прячутся под кнопкой «More» в правом верхнем углу) и убеждаемся, что приложение запущено;
  11. Тестируем бота через Telegram.

Если вы храните код на GitHub, то в интерфейсе управления приложением в личном кабинете на heroku на вкладке «Deploy» вы можете в дальнейшем переключить деплой на GitHub-репозиторий (по запросу или автоматически), чтобы не пушить параллельно в два репозитория.

Что можно доделать

Heroku периодически перезапускает приложение, и тогда введённые пользователем настройки удаляются. Можно добавить к проекту БД (например, Heroku Redis, как в другом моём боте), чтобы этого избежать.

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

Вместо заключения

Как выяснилось, не только лишь все видели чудесный советский мультик про козлёнка, который учился считать до 10.

This guide will walk you through everything you need to know to build your first Telegram Bot.
If you already know your way around some of the basic steps, you can jump directly to the part you’re missing. Equivalent examples are available in C#, Python, Go and TypeScript .

  • Introduction
  • Basic Tutorial
  • Environment
  • First Run
  • Echo Bot
  • Advanced Tutorial
  • Commands
  • Navigation
  • Database
  • Hosting
  • Further Reading

Introduction

At its core, you can think of the Telegram Bot API as software that provides JSON-encoded responses to your queries.

A bot, on the other hand, is essentially a routine, software or script that queries the API by means of an HTTPS request and waits for a response. There are several types of requests you can make, as well as many different objects that you can use and receive as responses.

Since your browser is capable of sending HTTPS requests, you can use it to quickly try out the API. After obtaining your token, try pasting this string into your browser:

https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getMe

In theory, you could interact with the API with basic requests like this, either via your browser or other tailor-made tools like cURL. While this can work for simple requests like the example above, it’s not practical for larger applications and doesn’t scale well.
For that reason, this guide will show you how to use libraries and frameworks, along with some basic programming skills, to build a more robust and scalable project.

If you know how to code, you’ll fly right through each step in no time – and if you’re just starting out, this guide will show you everything you need to learn.

We will use Java throughout this guide as it’s one of the most popular programming languages, however, you can follow along with any language as all the steps are fundamentally the same.
Since Java is fully cross-platform, each code example will work with any operating system.
If you pick another language, equivalent examples are available in C#, Python, Go and TypeScript .

Getting Ready

First, we will briefly cover how to create your first project, obtain your API token and download all necessary dependencies and libraries.

For the purposes of this guide, a copy of the bot you will be creating is also live at @TutorialBot – feel free to check it out along the way to see how your own implementation should look after each step.

Obtain Your Bot Token

In this context, a token is a string that authenticates your bot (not your account) on the bot API. Each bot has a unique token which can also be revoked at any time via @BotFather.

Obtaining a token is as simple as contacting @BotFather, issuing the /newbot command and following the steps until you’re given a new token. You can find a step-by-step guide here.

Your token will look something like this:

4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc

Make sure to save your token in a secure place, treat it like a password and don’t share it with anyone.

Download an IDE

To program in Java you’ll need an IDE – a special text editor that will let you write, compile and run your code.
In this tutorial, we’ll use IntelliJ – there are several free, open source alternatives like Eclipse or NetBeans which work in the exact same way.

You will also need a JDK, a software kit that allows your Java code to run.
Most IDEs don’t include a JDK, so you should download a version compatible with your operating system separately. You can find a free, open source version here.

If you use another language, the steps are identical. You will just have to download a different IDE and software development kit.

Pick a Framework or Library

You can think of a framework as software that handles all the low-level logic for you, including the API calls, and lets you focus on your bot-specific logic.

In this tutorial, we’ll use TelegramBots, but you can follow along with any equivalent implementation, since all the underlying methods are either similar or exactly the same.

You can find many frameworks, along with code examples, in our dedicated list.

Create Your Project

In IntelliJ, go to File > New > Project.

Fill in the fields accordingly:

  • Name — The name of your project. For example, BotTutorial.
  • Location — Where to store your project. You can use the default value.
  • Language — Java
  • Build System — The framework that will handle your dependencies. Pick Maven.
  • JDK — Pick whichever version you downloaded. We’ll be using version 17.
  • Add Sample Code — Leave this selected, it will generate some needed files for you.
  • Advanced Settings > GroupId — We suggest tutorial.
  • Advanced Settings > ArtifactId — You can use the default value.

After hitting Create, if you did everything correctly, your Project view in the top left should show a project structure along these lines:

BotTutorial
├─ .idea
├─ src
│  └─ main
│     └─ java
│        └─ tutorial
│           └─ Main
└─ pom.xml

Other IDEs will follow a similar pattern. Your dependency management system will have a different name (or no name at all if it’s built-in) depending on the language you chose.

If this looks scary, don’t worry. We will only be using the Main file and the pom.xml file.
In fact, to check that everything is working so far, double click on Main and click on the small green arrow on the left of public class Main, then select the first option.
If you followed the steps correctly, Hello world! should appear in the console below.

Add Framework Dependency

We will now instruct the IDE to download and configure everything needed to work with the API.
This is very easy and happens automatically behind the scenes.

First, locate your pom.xml file on the left side of the screen.
Open it by double-clicking and simply add:

<dependencies>
    <dependency>
        <groupId>org.telegram</groupId>
        <artifactId>telegrambots</artifactId>
        <version>6.0.1</version>
    </dependency>
</dependencies>

right after the </properties> tag.

When you’re done, your pom.xml should look something like this.

Start Coding

We are ready to start coding. If you’re a beginner, consider that being familiar with your language of choice will greatly help. With this tutorial, you’ll be able to teach your bot basic behaviors, though more advanced features will require some coding experience.

Creating a Bot Class

If you’re familiar with object-oriented programming, you’ll know what a class is.
If you’ve never heard of it before, consider a class as a file where you write some logic.

To create the class that will contain the bot logic, right click on tutorial from the project tree on the left and select New > Java Class. Name it Bot and hit enter.

Now we have to connect this class to the bot framework. In other words, we must make sure it extends TelegramLongPollingBot. To do that, just add extends TelegramLongPollingBot right after Bot.
A red line will appear – it simply means we’re missing some important methods.

To fix this, hover over the red line, click on implement methods, then hit OK.
Depending on the IDE, this option may be called implement missing methods or something similar.

You should end up with this – if something went wrong, feel free to copy it from here and paste it in your class:

package tutorial;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.objects.Update;

public class Bot extends TelegramLongPollingBot {

  @Override
  public String getBotUsername() {
      return null;
  }

  @Override
  public String getBotToken() {
      return null;
  }

  @Override
  public void onUpdateReceived(Update update) {}

}

If you get a red line under TelegramLongPollingBot, it means you didn’t set up your pom.xml correctly. If this is the case, restart from here.

Available Methods

Let’s look into these 3 methods one by one.

  • getBotUsername — This method must be edited to always return your bot’s username. You should replace the null return value with it.
  • getBotToken — This method will be used by the framework to retrieve your bot token. You should replace the null return value with the token.
  • onUpdateReceived — This is the most important method. It will be called automatically whenever a new Update is available. Let’s add a System.out.println(update); call in there to quickly show what we are getting.

After you’ve replaced all the strings, you should end up with this:

@Override
public String getBotUsername() {
    return "TutorialBot";
}

@Override
public String getBotToken() {
    return "4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc";
}

@Override
public void onUpdateReceived(Update update) {
    System.out.println(update);
}

At this point, the bot is configured and ready to go – time to register it on the API and start processing updates.

In the future, you should consider storing your token in a dedicated settings file or in environment variables. Keeping it in the code is fine for the scope of this tutorial, however, it’s not very versatile and is generally considered bad practice.

Registering the Bot

To register the bot on the API, simply add a couple of lines in the main method that will launch the application. If you named your class Bot, this is what your main method should look like:

public static void main(String[] args) throws TelegramApiException {
  TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
  botsApi.registerBot(new Bot());
}

You can place this method in any class. Since we have an auto-generated main method in the Main class, we’ll be using that one for this tutorial.

First Run

It’s time to run your bot for the first time.
Hit the green arrow to the left of public static void main and select the first option.

And then there was nothing. Yes, a bit anticlimactic.
This is because your bot has nothing to print – there are no new updates because nobody messaged it yet.

If you try messaging the bot on Telegram, you’ll then see new updates pop up in the console. At this point, you have your very own Telegram Bot – quite the achievement. Now, on to making it a bit more intelligent.

If nothing pops up, make sure you messaged the right bot and that the token you pasted in the code is correct.

Receiving Messages

Every time someone sends a private message to your bot, your onUpdateReceived method will be called automatically and you’ll be able to handle the update parameter, which contains the message, along with a great deal of other info which you can see detailed here.

Let’s focus on two values for now:

  • The user — Who sent the message. Access it via update.getMessage().getFrom().
  • The message — What was sent. Access it via update.getMessage().

Knowing this, we can make it a bit more clear in the console output.

@Override
public void onUpdateReceived(Update update) {
    var msg = update.getMessage();
    var user = msg.getFrom();

    System.out.println(user.getFirstName() + " wrote " + msg.getText());
}

This is just a basic example – you can now play around with all the methods to see everything you can pull out of these objects. You can try getUsername, getLanguageCode, and dozens more.

Knowing how to receive, process and print incoming messages, now it’s time to learn how to answer them.

Remember to stop and re-launch your bot after each change to the code.

Sending Messages

To send a private text message, you generally need three things:

  • The user must have contacted your bot first. (Unless the user sent a join request to a group where your bot is an admin, but that’s a more advanced scenario).
  • You must have previously saved the User ID (user.getId())
  • A String object containing the message text, 1-4096 characters.

With that out of the way, let’s create a new method to send the first message:

public void sendText(Long who, String what){
   SendMessage sm = SendMessage.builder()
                    .chatId(who.toString()) //Who are we sending a message to
                    .text(what).build();    //Message content
   try {
        execute(sm);                        //Actually sending the message
   } catch (TelegramApiException e) {
        throw new RuntimeException(e);      //Any error will be printed here
   }
}

And proceed to run this in the main method, right after registering the bot.
For this example, we’ll assume your User ID is 1234.

public static void main(String[] args) throws TelegramApiException {
   TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
   Bot bot = new Bot();                  //We moved this line out of the register method, to access it later
   botsApi.registerBot(bot);            
   bot.sendText(1234L, "Hello World!");  //The L just turns the Integer into a Long
}

If you did everything correctly, your bot should text you Hello World! every time you launch your code. Sending messages to groups or channels – assuming you have the relevant permissions – is as simple as replacing 1234 with the ID of the respective chat.

Try experimenting with other types of messages, like SendPhoto, SendSticker, SendDice…
A full list is available starting here.

Echo Bot

Let’s practice everything we tried so far by coding an Echo Bot.
Its functionality will be rather simple: every text message it receives will be sent right back to the user.

Copying Text

The most intuitive way of coding this is saving the User ID and calling sendText right after each update.

In other words:

@Override
public void onUpdateReceived(Update update) {
    var msg = update.getMessage();
    var user = msg.getFrom();
    var id = user.getId();

    sendText(id, msg.getText());
}

This works for text but can be extended to stickers, media and files.

Copying Everything

There are more specific functions that can be used to copy messages and send them back.
Let’s build a method to do just that:

public void copyMessage(Long who, Integer msgId){
   CopyMessage cm = CopyMessage.builder()
              .fromChatId(who.toString())  //We copy from the user
           .chatId(who.toString())      //And send it back to him
           .messageId(msgId)            //Specifying what message
           .build();
    try {
        execute(cm);
    } catch (TelegramApiException e) {
        throw new RuntimeException(e);
    }
}

After replacing the method call inonUpdateReceived, running the code will result in a fully functional Echo Bot.

This tutorial assumes that updates always contain messages for the sake of simplicity. This may not always be true – be sure to implement all the proper checks in your code to handle every type of update with the appropriate methods.

Executing Commands

To learn what a command is and how it works, we recommend reading this dedicated summary.
In this guide, we’ll focus on the technical side of things.

Creating Your Command

Begin by opening @BotFather.
Type /mybots > Your_Bot_Name > Edit Bot > Edit Commands.

Now send a new command, followed by a brief description.
For the purpose of this tutorial, we’ll implement two simple commands:

scream - Speak, I'll scream right back 
whisper - Shhhhhhh

Command Logic

We want the Echo Bot to reply in uppercase when it’s in scream mode and normally otherwise.

First, let’s create a variable to store the current mode.

public class Bot extends TelegramLongPollingBot {

   private boolean screaming = false;

   [...]
}

Then, let’s change some logic to account for this mode.

public void onUpdateReceived(Update update) {
    [...]                                   //Same variables as the previous versions
   if(screaming)                            //If we are screaming
       scream(id, update.getMessage());     //Call a custom method
   else
       copyMessage(id, msg.getMessageId()); //Else proceed normally
}

private void scream(Long id, Message msg) {
   if(msg.hasText())
       sendText(id, msg.getText().toUpperCase());
   else
       copyMessage(id, msg.getMessageId());  //We can't really scream a sticker
}

Finally, let’s add a couple more lines to the onUpdateReceived method to process each command before replying.

if(msg.isCommand()){ 
   if(msg.getText().equals("/scream"))         //If the command was /scream, we switch gears
      screaming = true;
   else if (msg.getText().equals("/whisper"))  //Otherwise, we return to normal
      screaming = false;

   return;                                     //We don't want to echo commands, so we exit
}

As you can see, it checks if the message is a command. If it is, the bot enters scream mode.
In the update method, we check which mode we are in and either copy the message or convert it to upper case before sending it back.

And that’s it. Now the bot can execute commands and change its behavior accordingly.

Naturally, this simplified logic will change the bot’s behavior for everyone – not just the person who sent the command. This can be fun for this tutorial but won’t work in a production environment – consider using a Map, dictionary or equivalent data structure to assign settings for individual users.

Remember to always implement a few basic global commands.
You can practice by implementing a simple feedback to the /start command, which we intentionally left out.

Buttons and Keyboards

To streamline and simplify user interaction with your bot, you can replace many text-based exchanges with handy buttons. These buttons can perform a wide variety of actions and can be customized for each user.

Button Types

There are two main types of buttons:

  • Reply Buttons — used to provide a list of predefined text reply options.
  • Inline Buttons — used to offer quick navigation, shortcuts, URLs, games and so much more.

Using these buttons is as easy as attaching a ReplyKeyboardMarkup or an InlineKeyboardMarkup to your SendMessage object.

This guide will focus on inline buttons since they only require a few extra lines of code.

Creating Buttons

First of all, let’s create some buttons.

 var next = InlineKeyboardButton.builder()
            .text("Next").callbackData("next")           
            .build();

 var back = InlineKeyboardButton.builder()
            .text("Back").callbackData("back")
            .build();

 var url = InlineKeyboardButton.builder()
            .text("Tutorial")
            .url("https://core.telegram.org/bots/api")
            .build();

Let’s go back through the fields we specified:

  • Text — This is what the user will see, the text that appears on the button
  • Callback Data — This will be sent back to the code instance as part of a new Update, so we can quickly identify what button was clicked.
  • Url — A button that specifies a URL doesn’t specify callbackdata since its behavior is predefined – it will open the given link when tapped.

Creating Keyboards

The buttons we created can be assembled into two keyboards, which will then be used to navigate back and forth between two sample menus.

First, add two fields to store the necessary keyboards.

private boolean screaming = false;

private InlineKeyboardMarkup keyboardM1;
private InlineKeyboardMarkup keyboardM2;

Then, build and assign them.

keyboardM1 = InlineKeyboardMarkup.builder()
          .keyboardRow(List.of(next)).build();  

//Buttons are wrapped in lists since each keyboard is a set of button rows
keyboardM2 = InlineKeyboardMarkup.builder()
          .keyboardRow(List.of(back))
          .keyboardRow(List.of(url))
          .build();

You can place this code wherever you prefer, the important thing is making sure that keyboard variables are accessible from the method call that will send the new menu. If you’re confused by this concept and don’t know where to put them, just paste them above the command processing flow.

Sending Keyboards

Sending a keyboard only requires specifying a reply markup for the message.

public void sendMenu(Long who, String txt, InlineKeyboardMarkup kb){
    SendMessage sm = SendMessage.builder().chatId(who.toString())
            .parseMode("HTML").text(txt)
            .replyMarkup(kb).build();

    try {
        execute(sm);
    } catch (TelegramApiException e) {
        throw new RuntimeException(e);
    }
}

You may have noticed that we also added a new parameter, HTML.
This is called a formatting option and will allow us to use HTML tags and add formatting to the text later on.

Menu Trigger

We could send a new menu for each new user, but for simplicity let’s add a new command that will spawn a menu. We can achieve this by adding a new else clause to the previous command flow.

 var txt = msg.getText();
 if(msg.isCommand()) {
        if (txt.equals("/scream"))
            screaming = true;
        else if (txt.equals("/whisper"))
            screaming = false;
        else if (txt.equals("/menu"))
            sendMenu(id, "<b>Menu 1</b>", keyboard1);
        return;
 }

Try sending /menu to your bot now. If you did everything correctly, you should see a brand new menu pop up.

In a production environment, commands should be handled with an appropriate design pattern that isolates them into different executor classes – modular and separated from the main logic.

Navigation

When building complex bots, navigation is essential. Your users must be able to move seamlessly from one menu to the next.

In this example, we want the Next button to lead the user to the second menu.
The Back button will send us back.
To do that, we will start processing incoming CallbackQueries, which are the results we get after the user taps on a button.

A CallbackQuery is essentially composed of three main parameters:

  • queryId — Needed to close the query. You must always close new queries after processing them – if you don’t, a loading symbol will keep showing on the user’s side on top of each button.
  • data — This identifies which button was pressed.
  • from — The user who pressed the button.

Processing in this context just means executing the action uniquely identified by the button, then closing the query.

A very basic button handler could look something like:

private void buttonTap(Long id, String queryId, String data, int msgId) {

    EditMessageText newTxt = EditMessageText.builder()
            .chatId(id.toString())
            .messageId(msgId).text("").build();

    EditMessageReplyMarkup newKb = EditMessageReplyMarkup.builder()
            .chatId(id.toString()).messageId(msgId).build();                           

    if(data.equals("next")) {
        newTxt.setText("MENU 2");
        newKb.setReplyMarkup(keyboardM2);
    } else if(data.equals("back")) {
        newTxt.setText("MENU 1");
        newKb.setReplyMarkup(keyboardM1);
    }

    AnswerCallbackQuery close = AnswerCallbackQuery.builder()
            .callbackQueryId(queryId).build();

    execute(close);
    execute(newTxt);
    execute(newKb);
}

With this handler, whenever a button is tapped, your bot will automatically navigate between inline menus.
Expanding on this concept allows for endless combinations of navigable submenus, settings and dynamic pages.

Database

Telegram does not host an update database for you – once you process and consume an update, it will no longer be available. This means that features like user lists, message lists, current user inline menu, settings, etc. have to be implemented and maintained by bot developers.

If your bot needs one of these features and you want to get started on data persistence, we recommend that you look into serialization practices and libraries for your language of choice, as well as available databases.

Implementing a database is out of scope for this guide, however, several guides are available online for simple embedded open source software solutions like SQLite, HyperSQL, Derby and many more.

Your language of choice will also influence which databases are available and supported – the list above assumes you followed this Java tutorial.

Hosting

So far, your bot has been running on your local machine – your PC. While this may be good for developing, testing and debugging, it is not ideal for a production environment.
You’ll want your bot to be available and responsive at all times, but your computer might not always be online.

This can be done in four steps:

  • Package your code
    Making your bot easy to move and runnable outside of an IDE is essential to host it elsewhere.
    If you followed this tutorial, this standard guide will work for you. If you didn’t, look into export or packaging guides for your IDE and language of choice – procedures may vary but the end result is the same.

  • Purchase a VPS or equivalent service
    A server is essentially a machine that is always online and running, without you having to worry about anything. To host your bot, you can opt for a VPS which serves this purpose and can be rented from several different providers.
    Another option would be to purchase a network-capable microcontroller, which come in all different specs and sizes depending on your needs.

You should ensure that all user data remains heavily encrypted at all times in your database to guarantee the privacy of your users. The same concept applies to your local instance, however, this becomes especially important once you transfer your database to a remote server.

  • Upload your executable/package

Once you have a working ssh connection between your machine and your new server, you should upload your executable and all associated files.
We will assume the runnable jar TutorialBot.jar and its database dbase.db are currently in the /TBot folder.

$ scp -r /TBot/ username@server_ip:/bots/TBotRemote/
  • Run your application

Depending on which language you chose, you might have to configure your server environment differently. If you chose Java, you just need to install a compatible JDK.

$ apt install openjdk-17-jre
$ java -version

If you did everything correctly, you should see a Java version as the output, along with a few other values. This means you’re ready to run your application.

Now, to run the executable:

$ cd /bots/TBotRemote/
$ java -jar TutorialBot.jar

Your bot is now online and users can interact with it at any time.

To streamline and modularize this process, you could employ a specialized docker container or equivalent service.
If you followed along in one of the equivalent examples (C#, Python, Go and TypeScript) you can find a detailed set of instructions to export and run your code here.

Further Reading

If you got this far, you might be interested in these additional guides and docs:

  • General Bot Platform Overview
  • Detailed List of Bot Features
  • Full API Reference

If you encounter any issues while following this guide, you can contact us on Telegram at @BotSupport.

Как сделать бота для telegram на java?

Как вы выбираете кандидата, кому доверить задание? Я задался этим вопросом, после того, как отклонили мое предложение о разработке бота для «вконтакте». Это задело мое самолюбие. На работе я делаю куда более сложные вещи, чем разработка ботов. Как доказать тому человеку, который находится по ту сторону экрана, который незнает меня и не доверяет, что я могу сделать простейшего бота информатора? Ответ есть — сделать этого бота и задокументировать процесс его создания. Статья расчитана на новичков, желающих познакомится с новым стандартом java 15 и простейшим ботостроением. Итак, нам понадобятся:

  • IntelliJ IDEA CE
  • Java JDK_15_PREVIEW
  • Библиотека для взаимодействия с телеграмом

С какими трудностями мы столкнемся? Для меня, самым сложным было настроить среду разработки для работы с джавой 15 превью версии. Нужно отдельно настроить gradle и выставить в настройках запуска проекта аргумент «—enable-preview».

Начнем по порядку с создания проекта:
Рис. 1
Рис. 1 Создание нового проекта

Нажимаем на кнопку «New Project». Следом увидим вот такое меню:

Рис. 2
Рис. 2 Выбор типа проекта

За основу я взял сборщик проектов Gradle. Выбираем Java и затем кнопку Next

Рис. 3
Рис. 3 Задаем имя проекта

Теперь нужно дать имя проекту. В моем случае это «telegram-bot-example-java»

Рис. 4
Рис.4 Ждем, пока проект проиндексируется

Какое-то время идея и gradle будут загружаться. Кстати, я уже допустил одну ошибку в конфигурации проекта, заметили, какую? Вернемся к этому позже.

Рис. 5
Рис.5 Создание структуры java packages

Кликаем правой кнопкой по папке «src/main/java» -> New -> Package -> «org.example.tgbot»

Рис. 6
Рис. 6 Создаем точку входа в программу

Теперь самое главное, без чего программа не запустится — точка входа и метод «main». Выбираем «org.example.tgbot» -> New -> Java Class. Называем новый класс Main.

Рис. 7
Рис. 7 Файл Main.java

Вот такой код должен быть в файле «Main.java». Обратите внимание на две зеленые стрелки рядом с определением класса и метода «main». Если вы их видите, значит сделали все правильно и IDEA может запустить ваш проект.

Рис. 8
Рис. 8 Тестовый запуск

Проверим, что все ок, запустив проект.

Рис. 9
Рис. 9 Успешный запуск

Если все хорошо, вы должны увидеть «done». У меня он есть, значит, можно продолжать.

Рис. 10
Рис. 10 Проверяем новую фичу java 15

Итак, вот мы дошли до ошибки, о которой я упоминал выше. В чем тут дело? Тип «record» был добавлен в java 15 и в превью версии должен присутствовать. Но я при запуске указал джаву восьмой версии. Что теперь делать? Можно сделать новый проект и указать правильную версию. Или можно исправить текущий проект. Сделать новый слишком просто, поэтому я исправлю этот (на самом деле нет, я попробовал, это не решило проблему).

Рис. 11
Рис. 11 Настройки проекта

Исправляем проблему. Нужно поменять версию джавы.Открываем настройки проекта.

Рис. 12
Рис. 12 Настройки версии java

Выбираем «Project SDK» -> 15. Если у вас ее нет, можно скачать ниже, в выпадающем списке.
В «Project language level» выбираем «15 (Preview) — Sealed types, records, patterns, local enums and interfaces». Сохраняем настройки.

Рис. 13
Рис. 13 Record тип работает

Теперь все ок, можно наконец-то взяться за программирование? Увы, нет. IDEA распознает новые фичи, но кроме нее есть еще gradle, который не сможет скомпилировать этот код. Чтобы это проверить, создаим рядом с «Main.java «еще один файл — «Bot.java» в котором будет происходить обработка сообщений.

Рис. 14
Рис. 14 Bot.java

У gradle будут проблемы со сборкой этого файла, а именно — из за 11 строки. Модификатор «sealed», как и «record», является экспериментальным. Проверим, соберем проект.

Рис. 15
Рис. 15 Gradle error

Еще немного борьбы и мы запустим этот код. Нужно настроить сборку gradle и добавить аргумент «—enable-preview» при запуске.

Рис. 16
Рис. 16 Gradle java 15 settings

Нужно добавить новую секцию, в которой будут задаваться флаги сборки «—enable-preview» и «-Xlint:Preview». Второй флаг не обязательный, нужен для отображения новых warnings. В комментарии пример, как можно задать все флаги одной строкой. Кроме этого, нужно добавить строку «jvmArgs([‘—enable-preview’])» в секцию «test». На этом с gradle закончили.

Рис. 17
Рис. 17 Настройки сборки

Далее, нужно добавить аргумент для виртуальной машины java. Отрываем настройки.

Рис. 18
Рис. 18 Открыть меню «Add VM options»

После чего у вас появится поле редактирование опций виртуальной машины.

Рис. 19
Рис. 19 Редактор опций виртуальной машины

В пустое поле вписываем «—enable-preview». Также проверьте, что у вас стоит «java 15». Сохраняем настройки и собираем проект. У меня сборка и запуск прошли успешно. Теперь настроим прием сообщений и ответы.

Рис. 20
Рис. 20 Bot.java

Добавляем следующий код в файл «Bot.java». В нем два метода, хотя можно было обойтись и одним, выбранная мною библиотека присилает обновления в виде массива, а не по одному. Ах да, я забыл показать, как добавить эту библиотеку.

Рис. 21
Рис. 21 Добавляем зависимость в «build.gradle»

В секцию «dependencies» добавьте строку «implementation ‘com.github.pengrad:java-telegram-bot-api:5.0.1′» как показано на рисунке (13 строка). И финальный штрих, обновляем Main класс, чтобы запустить бота.

Рис. 22
Рис. 22 Новый Main класс

Здесь я читаю BOT_TOKEN из переменных среды, это значит, ее нужно как то добавить. Это можно сделать глобально в системе или задать в IDEA. Я выбираю второй вариант.

Рис. 23
Рис. 23 Снова открываем «Edit configurations»

Рис. 24
Рис. 24 Редактирование переменных среды

В поле «Environment variables» вставьте строку «BOT_TOKEN=123», где 123 — ваш токен. А я вставлю свой :) Сохраняем настройки и запускаем проект.

Рис. 25
Рис. 25 Бот успешно запущен

Бот работает! Пруф:

Рис. 25
Рис. 25 Телеграм чат

Скорее всего, если вы захотите проверить моего бота, он вам не ответит. Потому что программа, которую мы написали, запущена локально, у меня на компьютере. Чтобы бот работал 24/7, программу нужно разметить на удаленном сервере (или просто держать компьютер всегда включенным всесте с запущенной программой). Это материал для другой статьи. Я также могу рассказать, как сделать ответы на команды в чате, отображать динамическую информацию, информировать клиентов. Или как написать бота на scala >_<. Пишите в комментариях, что у вас не получилось. До встречи в других статьях!

В этой статье я покажу, как написать Telegram-бот на Java с использованием Spring Boot, PostgreSQL и JPA. Также создадим исполняемый jar-файл. Сам же бот будет подсчитывать сообщения от пользователей и записывать эти данные в БД.

  1. Создаём Spring проект на Java
  2. Реализация базового функционала
  3. Добавление кнопок
  4. Подключение Telegram-бота на Java к базе данных
  5. Создание исполняемого jar-файла в Intellij IDEA
  6. Выводы

Для этого воспользуемся сервисом быстрого создания Spring Initializr: он предоставляет интерфейс для генерации заготовки проекта с добавлением стандартных зависимостей. При необходимости в дальнейшем их можно настроить под свои нужды.

Мои настройки Spring Initializr выглядят так:

Обратите внимание на кнопку Add Dependencies: с её помощью можно добавить важные зависимости уже на старте.

После того, как вы всё указали, нажмите Generate, разархивируйте стартовый проект и откройте его с помощью удобной IDE. У меня это IntelliJ IDEA.

Реализация базового функционала

Для начала напишем на Java самый примитивный Telegram bot, который будет отвечать на наши сообщения.

Создание Telegram-бота и конфигурация

Начнём с того, что это Maven-проект. Сразу добавим в pom.xml дополнительные зависимости для работы с Телеграм ботом и базами данных:

  • Telegram Bots
  • Hibernate Core Relocation
  • PostgreSQL JDBC Driver
  • Lombok

В каталоге resources создадим файл config.properties, где будут храниться данные для подключения к боту и в будущем к БД.

Примечание Данный файл не следует включать в коммиты.

Теперь создадим бота. Для этого перейдём в Telegram в BotFather и создадим нового бота командой /newbot. Выбираем для него название, которое будет отображаться для всех, и его username. После этого BotFather выдаст токен для взаимодействия с бэкендом Телеграмма.

Теперь запишем в файл config.properties следующее:

bot.name = юзернейм_вашего_бота
bot.token = токен_вашего_бота
bot.chatId = id_нужного_чата

Добавим в основной каталог проекта пакет config, а внутри него создадим новый класс BotConfig.

Вы наверняка заметили, что мы добавили в pom.xml Lombok. Это популярная библиотека для сокращения кода и расширения функциональности Java. С ней и Spring наш класс BotConfig будет выглядеть очень лаконично:

@Configuration
@Data
@PropertySource("config.properties")
public class BotConfig {
    @Value("${bot.name}") String botName;
    @Value("${bot.token}") String token;
    @Value("${bot.chatId}") String chatId;
}

Что здесь происходит?

  1. @Configuration указывает, что класс содержит методы определения @Bean (наши @Value).
  2. @Data на этапе компиляции генерирует для всех полей геттеры, сеттеры, toString и предопределяет equals и hashCode.

С остальным, думаю, всё понятно.

Класс Телеграм бота на Java

Давайте теперь выйдем из пакета config и создадим в основном пакете проекта класс бота. Поскольку это бот-счётчик, назовём его CounterTelegramBot.

Сразу унаследуемся от TelegramLongPollingBot — класса, который позволяет взаимодействовать с Telegram. И имплементируем методы getBotUsernamegetBotToken и onUpdateReceived. Создадим конструктор и добавим две аннотации перед классом: @Component (авто-создание экземпляра) и @Slf4j (для работы с логером).

На старте получаем следующий класс:

@Slf4j
@Component
public class CounterTelegramBot extends TelegramLongPollingBot {
    final BotConfig config;
    
    public CounterTelegramBot(BotConfig config) { this.config = config; }
    @Override
    public String getBotUsername() { return config.getBotName(); }
    @Override
    public String getBotToken() { return config.getToken(); }
    @Override
    public void onUpdateReceived(@NotNull Update update) {}
}

Для начала сделаем так, чтобы на команду /start Telegram-бот что-то нам отвечал и выводил в логи сообщение об успехе. Другие сообщения будут выводить в логи "Unexpected message":

    @Override
    public void onUpdateReceived(@NotNull Update update) {
        if(update.hasMessage() && update.getMessage().hasText()){
            String messageText = update.getMessage().getText();
            long chatId = update.getMessage().getChatId();
            String memberName = update.getMessage().getFrom().getFirstName();

            switch (messageText){
                case "/start":
                    startBot(chatId, memberName);
                    break;
                default: log.info("Unexpected message");
            }
        }
    }

    private void startBot(long chatId, String userName) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText("Hello, " + userName + "! I'm a Telegram bot.");

        try {
            execute(message);
            log.info("Reply sent");
        } catch (TelegramApiException e){
            log.error(e.getMessage());
        }
    }

И последним штрихом является инициализация бота. Добавим в пакет config класс Initializer:

@Slf4j
@Component
public class Initializer {
    @Autowired CounterTelegramBot bot;

    @EventListener({ContextRefreshedEvent.class})
    public void init() {
        try {
            TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class);
            telegramBotsApi.registerBot((LongPollingBot) bot);
        } catch (TelegramApiException e) {
            log.error(e.getMessage());
        }
    }
  1. @Autowired обеспечивает контроль над тем, где и как осуществить автосвязывание (чтобы Spring автоматически подключил бота).
  2. @EventListener — слушатель, который вешаем на изменение класса.

Запустите и проверьте работу бота.

Примечание Если возникает ошибка Failed to configure a DataSource, измените аннотацию в исполняемом классе на @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}). Ошибка исчезнет, как только мы добавим информацию для доступа к БД в config.properties.

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

Чтобы Telegram bot на Java и Spring Boot выглядел по-настоящему серьёзным, давайте добавим ему команду /help и пару кнопок.

Создадим в основной директории проекта пакет components. В него добавим:

1. Интерфейс BotCommands:

public interface BotCommands {
    List<BotCommand> LIST_OF_COMMANDS = List.of(
            new BotCommand("/start", "start bot"),
            new BotCommand("/help", "bot info")
    );

    String HELP_TEXT = "This bot will help to count the number of messages in the chat. " +
            "The following commands are available to you:nn" +
            "/start - start the botn" +
            "/help - help menu";
}

2. Класс Buttons:

public class Buttons {
    private static final InlineKeyboardButton START_BUTTON = new InlineKeyboardButton("Start");
    private static final InlineKeyboardButton HELP_BUTTON = new InlineKeyboardButton("Help");

    public static InlineKeyboardMarkup inlineMarkup() {
        START_BUTTON.setCallbackData("/start");
        HELP_BUTTON.setCallbackData("/help");

        List<InlineKeyboardButton> rowInline = List.of(START_BUTTON, HELP_BUTTON);
        List<List<InlineKeyboardButton>> rowsInLine = List.of(rowInline);

        InlineKeyboardMarkup markupInline = new InlineKeyboardMarkup();
        markupInline.setKeyboard(rowsInLine);

        return markupInline;
    }
}

В классе мы создаём две кнопки, которые будут расположены в одной линии. Одна из них отвечает за команду старта, а вторая — за вызов меню помощи.

Теперь немного улучшим класс CounterTelegramBot:

@Slf4j
@Component
public class CounterTelegramBot extends TelegramLongPollingBot implements BotCommands {
    final BotConfig config;
    
    public CounterTelegramBot(BotConfig config) {
        this.config = config;
        try {
            this.execute(new SetMyCommands(LIST_OF_COMMANDS, new BotCommandScopeDefault(), null));
        } catch (TelegramApiException e){
            log.error(e.getMessage());
        }
    }

    @Override
    public String getBotUsername() {
        return config.getBotName();
    }

    @Override
    public String getBotToken() {
        return config.getToken();
    }

    @Override
    public void onUpdateReceived(@NotNull Update update) {
        long chatId = 0;
        long userId = 0; //это нам понадобится позже
        String userName = null;
        String receivedMessage;

        //если получено сообщение текстом
        if(update.hasMessage()) {
            chatId = update.getMessage().getChatId();
            userId = update.getMessage().getFrom().getId();
            userName = update.getMessage().getFrom().getFirstName();

            if (update.getMessage().hasText()) {
                receivedMessage = update.getMessage().getText();
                botAnswerUtils(receivedMessage, chatId, userName);
            }
            
        //если нажата одна из кнопок бота    
        } else if (update.hasCallbackQuery()) {
            chatId = update.getCallbackQuery().getMessage().getChatId();
            userId = update.getCallbackQuery().getFrom().getId();
            userName = update.getCallbackQuery().getFrom().getFirstName();
            receivedMessage = update.getCallbackQuery().getData();

            botAnswerUtils(receivedMessage, chatId, userName);
        }
    }

    private void botAnswerUtils(String receivedMessage, long chatId, String userName) {
        switch (receivedMessage){
            case "/start":
                startBot(chatId, userName);
                break;
            case "/help":
                sendHelpText(chatId, HELP_TEXT);
                break;
            default: break;
        }
    }

    private void startBot(long chatId, String userName) {
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText("Hi, " + userName + "! I'm a Telegram bot.'");
        message.setReplyMarkup(Buttons.inlineMarkup());

        try {
            execute(message);
            log.info("Reply sent");
        } catch (TelegramApiException e){
            log.error(e.getMessage());
        }
    }

    private void sendHelpText(long chatId, String textToSend){
        SendMessage message = new SendMessage();
        message.setChatId(chatId);
        message.setText(textToSend);

        try {
            execute(message);
            log.info("Reply sent");
        } catch (TelegramApiException e){
            log.error(e.getMessage());
        }
    }
}

switch вынесли в отдельный метод, добавили обработку команд, в том числе и нажатие кнопок.

Подключение Telegram-бота на Java к базе данных

Перед началом работы установите PostgerSQL, если СУБД ещё не установлена. В случае, если вы работаете с другими СУБД, просто измените настройки доступа в файле config.properties. Для тех же, кто работает с PostgerSQL, config.properties будет выглядеть примерно так:

bot.name = юзернейм_вашего_бота
bot.token = токен_вашего_бота
bot.chatId = id_нужного_чата

#db related settings

spring.jpa.database = PostgreSQL
spring.jpa.show-sql = false
# для автоматического создания/обновления таблицы в бд
spring.jpa.hibernate.ddl-auto = update

spring.datasource.driverClassName = org.postgresql.Driver
# ниже прописываете порт и название бд
spring.datasource.url = jdbc:postgresql://localhost:5432/tg
# ваши кредлы для доступа к бд
spring.datasource.username = postgres
spring.datasource.password = root

В директорию проекта добавляем пакет database. В нём следует создать:

1. Класс User:

@Data
@Entity(name = "tg_data") //привязываемся к существующей таблице с готовыми колонками
public class User {

    @Id
    private long id; //BigInt
    private String name; //Text
    private int msg_numb; //Integer
}

2. Интерфейс UserRepository:

public interface UserRepository extends CrudRepository<User, Long> {
    @Transactional
    @Modifying
    @Query("update tg_data t set t.msg_numb = t.msg_numb + 1 where t.id is not null and t.id = :id")
    void updateMsgNumberByUserId(@Param("id") long id);
}

Данный интерфейс нам нужен для удобной работы с CrudRepository — интерфейсом данных Spring для общих операций CRUD. Сюда же вшиваем запрос на апдейт нашей таблицы: добавление +1 сообщения пользователю в случае, если он написал в чат.

В классе CounterTelegramBot объявим новый интерфейс с аннотацией @Autowired, которая говорит Spring, что в это поле нужно инжектнуть бин:

@Autowired
private UserRepository userRepository;

Там же создаём метод добавления пользователя в базу данных, если он написал впервые, и просто обновление столбца сообщений, если пользователь уже существует:

    private void updateDB(long userId, String userName) {
        if(userRepository.findById(userId).isEmpty()){
            User user = new User();
            user.setId(userId);
            user.setName(userName);
            //сразу добавляем в столбец каунтера 1 сообщение
            user.setMsg_numb(1);

            userRepository.save(user);
            log.info("Added to DB: " + user);
        } else {
            userRepository.updateMsgNumberByUserId(userId);
        }
    }

Финально обновим метод onUpdateReceived в классе CounterTelegramBot:

    @Override
    public void onUpdateReceived(@NotNull Update update) {
        long chatId = 0;
        long userId = 0;
        String userName = null;
        String receivedMessage;

        if(update.hasMessage()) {
            chatId = update.getMessage().getChatId();
            userId = update.getMessage().getFrom().getId();
            userName = update.getMessage().getFrom().getFirstName();

            if (update.getMessage().hasText()) {
                receivedMessage = update.getMessage().getText();
                botAnswerUtils(receivedMessage, chatId, userName);
            }
        } else if (update.hasCallbackQuery()) {
            chatId = update.getCallbackQuery().getMessage().getChatId();
            userId = update.getCallbackQuery().getFrom().getId();
            userName = update.getCallbackQuery().getFrom().getFirstName();
            receivedMessage = update.getCallbackQuery().getData();

            botAnswerUtils(receivedMessage, chatId, userName);
        }

        if(chatId == Long.valueOf(config.getChatId())){
            updateDB(userId, userName);
        }
    }

Примечание Вы можете не делать ограничение по chatId, но тогда следует дополнительно прописать логику для создания отдельной таблицы под каждый чат. В моём случае бот писался под конкретный чат.

Важно Не забудьте предоставить боту права администратора чата.

Создание исполняемого jar-файла в Intellij IDEA

У Telegram API есть одно неприятное ограничение, в соответствии с которым наш бот на Java позволяет достучаться только до сообщений, отправленных за последние 24 часа. Всё, что было отправлено раньше, не учтётся.

Поэтому после вы можете либо создать exe-файл с установкой времени выполнения, либо воспользоваться удалённым сервером. Например, в статье о Telegram-боте на Python мы рассказали, как настроить Docker и задеплоить бота на AWS.

Здесь же я просто покажу, как создать исполняемый jar-файл для ручного запуска. Костыльно, но для периодического подсчёта из конкретного чата подходит, а далее можно масштабировать по своему усмотрению.

Инструкция по созданию jar-файла:

  1. File — Project Structure — Project Settings — Artifacts — Кликаем по кнопке + — Jar — From modules with dependencies.
    Создание jar-файла в Intellij IDEA
  2. Выбираем главный класс проекта и жмем ОK.
    jar файл в Intellij IDEA
  3. После этого собираем Jar файл: Build — Build Artifact.
  4. Это создаст .jar, который при двойном клике запустит JVM, если она установлена в ОС.

На первом же скрине вы можете посмотреть структуру проекта.

Выводы

Создание Telegram-бота на Java возможно благодаря специальному классу TelegramLongPollingBot, а Spring Boot и Lombok сильно упрощают этот процесс.

Но стоит отметить, что тот же бот, написанный на Python или PHP, обойдётся вам в меньшее количество строк кода, да и туториалов по таким Телеграм-ботам значительно больше. А вот в качестве практики Java и небольшого пет-проекта, который можно представить в своём резюме, такая программа вполне подойдёт.

Остались вопросы? Задавайте их в комментариях к этой статье.

Из песочницы, Программирование, Open source, Java, Системы обмена сообщениями, Из песочницы


Рекомендация: подборка платных и бесплатных курсов Smm — https://katalog-kursov.ru/

Каждый разработчик (и не только), который использует Telegram в повседневной жизни, хотя бы раз задумывался о том, каково это — создать своего бота, на сколько это сложно и какой язык программирования лучше использовать.

На все эти вопросы могу дать самый простой (и, наверно, самый правильный) ответ: все зависит от вас самих, ваших знаний и намерений.

… Но в этой небольшой статье, я покажу, как можно создать своего бота на языке Java и что это довольно интересно и несложно.

Мы будем использовать библиотеку для работы с Telegram Bots API и ее расширение, позволяющее создавать свои команды (‘/custom_cmd‘) и обрабатывать их простым способом.

Задачей бота будет являться регистрация пользователя и отправка сообщения от указанного им имени другим пользователям бота.

Создание нового проекта и подготовка

1. Добавление зависимостей в проект

Создадим новый maven-проект и отредактируем pom.xml, добавив в него необходимые зависимости:

pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>

        <groupId>io.example</groupId>
        <artifactId>anonymizerbot</artifactId>
        <version>1.0-SNAPSHOT</version>

        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
```8</source>
                        <target>8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>

        <dependencies>

            <!-- Telegram API -->
            <dependency>
                <groupId>org.telegram</groupId>
                <artifactId>telegrambots</artifactId>
                <version>LATEST</version>
            </dependency>
            <dependency>
                <groupId>org.telegram</groupId>
                <artifactId>telegrambotsextensions</artifactId>
                <version>LATEST</version>
            </dependency>

            ...

        </dependencies>

    </project>

Telegram API — библиотека для работы с Telegram Bots API, содержит в себе классы и методы для взаимодействия с сервисами Telegram и некоторые расширения этих классов.

2. Создание аккаунта для бота

Для этого нам необходимо обратиться за помощью к боту BotFather:

  • найдем бота в поиске;
  • выполним команду «/start»;
  • выполним команду «/newbot»;
  • зададим какое-нибудь имя нашему боту (должно заканчиваться на «Bot»). Я назвал его «ExampleOfAnonymizerBot».

После выполнения этих команд мы получим токен, который нам понадобится для использования Bot API. (7xxxxxxx2:Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0)



Реализация

1. Модель анонимного отправителя сообщений

Данные, необходимые нам от каждого пользователя:

  • User mUser — информация о пользователе Telegram;
  • Chat mChat — информация о чате пользователя и бота;
  • String mDisplayedName — имя, от которого пользователь будет посылать сообщения другим пользователям бота.

Anonymous.java

package io.example.anonymizerbot.model;

import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;

public final class Anonymous {

    private final User mUser;
    private final Chat mChat;
    private String mDisplayedName;

    public Anonymous(User user, Chat chat) {
        mUser = user;
        mChat = chat;
    }

    @Override
    public int hashCode() {
        return mUser.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Anonymous && ((Anonymous) obj).getUser().equals(mUser);
    }

    public User getUser() {
        return mUser;
    }

    public Chat getChat() {
        return mChat;
    }

    public String getDisplayedName() {
        return mDisplayedName;
    }

    public void setDisplayedName(String displayedName) {
        mDisplayedName = displayedName;
    }
}

Добавим сервис, содержащий часто используемые методы для манипулирования множеством анонимных пользователей.

AnonymousService.java

package io.example.anonymizerbot.service;

import io.example.anonymizerbot.model.Anonymous;
import org.telegram.telegrambots.meta.api.objects.User;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

public final class AnonymousService {

    private final Set<Anonymous> mAnonymouses;

    public AnonymousService() {
        mAnonymouses = new HashSet<>();
    }

    public boolean setUserDisplayedName(User user, String name) {

        if (!isDisplayedNameTaken(name)) {
            mAnonymouses.stream().filter(a -> a.getUser().equals(user)).forEach(a -> a.setDisplayedName(name));
            return true;
        }

        return false;
    }

    public boolean removeAnonymous(User user) {
        return mAnonymouses.removeIf(a -> a.getUser().equals(user));
    }

    public boolean addAnonymous(Anonymous anonymous) {
        return mAnonymouses.add(anonymous);
    }

    public boolean hasAnonymous(User user) {
        return mAnonymouses.stream().anyMatch(a -> a.getUser().equals(user));
    }

    public String getDisplayedName(User user) {

        Anonymous anonymous = mAnonymouses.stream().filter(a -> a.getUser().equals(user)).findFirst().orElse(null);

        if (anonymous == null) {
            return null;
        }
        return anonymous.getDisplayedName();
    }

    public Stream<Anonymous> anonymouses() {
        return mAnonymouses.stream();
    }

    private boolean isDisplayedNameTaken(String name) {
        return mAnonymouses.stream().anyMatch(a -> Objects.equals(a.getDisplayedName(), name));
    }
}

2. Интерфейс бота

Любая кастомная команда должна наследоваться от BotCommand и реализовывать метод
execute(AbsSender sender, User user, Chat chat, String[] strings), который используется для обработки команд пользователей.

После того как мы обработаем команду пользователя, мы можем послать ему ответ, используя метод execute класса AbsSender, который принимает на вход вышеупомянутый execute(AbsSender sender, User user, Chat chat, String[] strings).

Здесь и далее чтобы не оборачивать каждый раз метод AbsSender.execute, который может выбросить исключение TelegramApiException, в try-catch, и для того чтобы не прописывать в каждой команде вывод однообразных логов, создадим класс AnonymizerCommand, а наши кастомные команды будем уже наследовать от него (обработку исключений в этом примере оставим):

AnonymizerCommand.java

package io.example.anonymizerbot.command;

import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

abstract class AnonymizerCommand extends BotCommand {

    final Logger log = LogManager.getLogger(getClass());

    AnonymizerCommand(String commandIdentifier, String description) {
        super(commandIdentifier, description);
    }

    void execute(AbsSender sender, SendMessage message, User user) {
        try {
            sender.execute(message);
            log.log(Level.getLevel(LogLevel.SUCCESS.getValue()), LogTemplate.COMMAND_SUCCESS.getTemplate(), user.getId(), getCommandIdentifier());
        } catch (TelegramApiException e) {
            log.error(LogTemplate.COMMAND_EXCEPTION.getTemplate(), user.getId(), getCommandIdentifier(), e);
        }
    }
}

Определим команды, на которые наш бот будет реагировать:

  • /start — создаст нового Anonymous без имени и добавит его в коллекцию Anonymouses;

StartCommand.java

package io.example.anonymizerbot.command;

import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.model.Anonymous;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;

public final class StartCommand extends AnonymizerCommand {

    private final AnonymousService mAnonymouses;

    // обязательно нужно вызвать конструктор суперкласса,
    // передав в него имя и описание команды
    public StartCommand(AnonymousService anonymouses) {
        super("start", "start using botn");
        mAnonymouses = anonymouses;
    }

    /**
    * реализованный метод класса BotCommand, в котором обрабатывается команда, введенная пользователем
    * @param absSender - отправляет ответ пользователю
    * @param user - пользователь, который выполнил команду
    * @param chat - чат бота и пользователя
    * @param strings - аргументы, переданные с командой
    */
    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {

        log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());

        StringBuilder sb = new StringBuilder();

        SendMessage message = new SendMessage();
        message.setChatId(chat.getId().toString());

        if (mAnonymouses.addAnonymous(new Anonymous(user, chat))) {
            log.info("User {} is trying to execute '{}' the first time. Added to users' list.", user.getId(), getCommandIdentifier());
            sb.append("Hi, ").append(user.getUserName()).append("! You've been added to bot users' list!n")
            .append("Please execute command:n'/set_name <displayed_name>'nwhere &lt;displayed_name&gt; is the name you want to use to hide your real name.");
        } else {
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} has already executed '{}'. Is he trying to do it one more time?", user.getId(), getCommandIdentifier());
            sb.append("You've already started bot! You can send messages if you set your name (/set_name).");
        }

        message.setText(sb.toString());
        execute(absSender, message, user);
    }
}

  • /help — выведет пользователю информацию обо всех доступных командах (конструктор отличается от других тем, что в него необходимо передать ICommandRegistry, который содержит все кастомные команды);

HelpCommand.java

package io.example.anonymizerbot.command;

import io.example.anonymizerbot.logger.LogTemplate;
import org.telegram.telegrambots.extensions.bots.commandbot.commands.ICommandRegistry;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;

public final class HelpCommand extends AnonymizerCommand {

    private final ICommandRegistry mCommandRegistry;

    public HelpCommand(ICommandRegistry commandRegistry) {
        super("help", "list all known commandsn");
        mCommandRegistry = commandRegistry;
    }

    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {

        log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());

        StringBuilder helpMessageBuilder = new StringBuilder("&lt;b&gt;Available commands:&lt;/b&gt;nn");

        mCommandRegistry.getRegisteredCommands().forEach(cmd -> helpMessageBuilder.append(cmd.toString()).append("n"));

        SendMessage helpMessage = new SendMessage();
        helpMessage.setChatId(chat.getId().toString());
        helpMessage.enableHtml(true);
        helpMessage.setText(helpMessageBuilder.toString());

        execute(absSender, helpMessage, user);
    }
}

  • /set_name — задаст пользователю имя, от которого будут отправляться анонимные сообщения;

SetNameCommand.java

package io.example.anonymizerbot.command;

import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;

public final class SetNameCommand extends AnonymizerCommand {

    private final AnonymousService mAnonymouses;

    public SetNameCommand(AnonymousService anonymouses) {
        super("set_name", "set or change name that will be displayed with your messagesn");
        mAnonymouses = anonymouses;
    }

    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {

        log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());

        SendMessage message = new SendMessage();
        message.setChatId(chat.getId().toString());

        if (!mAnonymouses.hasAnonymous(user)) {
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without starting the bot!", user.getId(), getCommandIdentifier());
            message.setText("Firstly you should start the bot! Execute '/start' command!");
            execute(absSender, message, user);
            return;
        }

        String displayedName = getName(strings);

        if (displayedName == null) {
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to set empty name.", user.getId());
            message.setText("You should use non-empty name!");
            execute(absSender, message, user);
            return;
        }

        StringBuilder sb = new StringBuilder();

        if (mAnonymouses.setUserDisplayedName(user, displayedName)) {

            if (mAnonymouses.getDisplayedName(user) == null) {
                log.info("User {} set a name '{}'", user.getId(), displayedName);
                sb.append("Your displayed name: '").append(displayedName)
                .append("'. Now you can send messages to bot!");
            } else {
                log.info("User {} has changed name to '{}'", user.getId(), displayedName);
                sb.append("Your new displayed name: '").append(displayedName).append("'.");
            }
        } else {
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to set taken name '{}'", user.getId(), displayedName);
            sb.append("Name ").append(displayedName).append(" is already in use! Choose another name!");
        }

        message.setText(sb.toString());
        execute(absSender, message, user);
    }

    private String getName(String[] strings) {

        if (strings == null || strings.length == 0) {
            return null;
        }

        String name = String.join(" ", strings);
        return name.replaceAll(" ", "").isEmpty() ? null : name;
    }
}

  • /my_name — отобразит текущее имя пользователя;

MyNameCommand.java

package io.example.anonymizerbot.command;

import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;

public final class MyNameCommand extends AnonymizerCommand {

    private final AnonymousService mAnonymouses;

    public MyNameCommand(AnonymousService anonymouses) {
        super("my_name", "show your current name that will be displayed with your messagesn");
        mAnonymouses = anonymouses;
    }

    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {

        log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());

        StringBuilder sb = new StringBuilder();

        SendMessage message = new SendMessage();
        message.setChatId(chat.getId().toString());

        if (!mAnonymouses.hasAnonymous(user)) {

            sb.append("You are not in bot users' list! Send /start command!");
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without starting the bot.", user.getId(), getCommandIdentifier());

        } else if(mAnonymouses.getDisplayedName(user) == null) {

            sb.append("Currently you don't have a name.nSet it using command:n'/set_name &lt;displayed_name&gt;'");
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without having a name.", user.getId(), getCommandIdentifier());

        } else {

            log.info("User {} is executing '{}'. Name is '{}'.", user.getId(), getCommandIdentifier(), mAnonymouses.getDisplayedName(user));
            sb.append("Your current name: ").append(mAnonymouses.getDisplayedName(user));
        }

        message.setText(sb.toString());
        execute(absSender, message, user);
    }
}

  • /stop — удалит пользователя из коллекции анонимусов.

StopCommand.java

package io.example.anonymizerbot.command;

import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Chat;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.bots.AbsSender;

public final class StopCommand extends AnonymizerCommand {

    private final AnonymousService mAnonymouses;

    public StopCommand(AnonymousService anonymouses) {
        super("stop", "remove yourself from bot users' listn");
        mAnonymouses = anonymouses;
    }

    @Override
    public void execute(AbsSender absSender, User user, Chat chat, String[] strings) {

        log.info(LogTemplate.COMMAND_PROCESSING.getTemplate(), user.getId(), getCommandIdentifier());

        StringBuilder sb = new StringBuilder();

        SendMessage message = new SendMessage();
        message.setChatId(chat.getId().toString());

        if (mAnonymouses.removeAnonymous(user)) {
            log.info("User {} has been removed from users list!", user.getId());
            sb.append("You've been removed from bot's users list! Bye!");
        } else {
            log.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute '{}' without having executed 'start' before!", user.getId(), getCommandIdentifier());
            sb.append("You were not in bot users' list. Bye!");
        }

        message.setText(sb.toString());
        execute(absSender, message, user);
    }
}

3. Инициализация и запуск бота

Класс бота, в котором производится регистрация всех кастомных команд, обработчика сообщений-не команд и неизвестных команд.

AnonymizerBot.java

package io.example.anonymizerbot.bot;

import io.example.anonymizerbot.command.*;
import io.example.anonymizerbot.logger.LogLevel;
import io.example.anonymizerbot.logger.LogTemplate;
import io.example.anonymizerbot.model.Anonymous;
import io.example.anonymizerbot.service.AnonymousService;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.extensions.bots.commandbot.TelegramLongPollingCommandBot;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.api.objects.User;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

import java.util.stream.Stream;

public final class AnonymizerBot extends TelegramLongPollingCommandBot {

    private static final Logger LOG = LogManager.getLogger(AnonymizerBot.class);

    // имя бота, которое мы указали при создании аккаунта у BotFather
    // и токен, который получили в результате
    private static final String BOT_NAME = "AnonymizerBotExample";
    private static final String BOT_TOKEN = "7xxxxxxx2:Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0";

    private final AnonymousService mAnonymouses;

    public AnonymizerBot(DefaultBotOptions botOptions) {

        super(botOptions, BOT_NAME);

        LOG.info("Initializing Anonymizer Bot...");

        LOG.info("Initializing anonymouses list...");
        mAnonymouses = new AnonymousService();

        // регистрация всех кастомных команд
        LOG.info("Registering commands...");
        LOG.info("Registering '/start'...");
        register(new StartCommand( mAnonymouses));
        LOG.info("Registering '/set_name'...");
        register(new SetNameCommand(mAnonymouses));
        LOG.info("Registering '/stop'...");
        register(new StopCommand(mAnonymouses));
        LOG.info("Registering '/my_name'...");
        register(new MyNameCommand(mAnonymouses));
        HelpCommand helpCommand = new HelpCommand(this);
        LOG.info("Registering '/help'...");
        register(helpCommand);

        // обработка неизвестной команды
        LOG.info("Registering default action'...");
        registerDefaultAction(((absSender, message) -> {

            LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to execute unknown command '{}'.", message.getFrom().getId(), message.getText());

            SendMessage text = new SendMessage();
            text.setChatId(message.getChatId());
            text.setText(message.getText() + " command not found!");

            try {
                absSender.execute(text);
            } catch (TelegramApiException e) {
                LOG.error("Error while replying unknown command to user {}.", message.getFrom(), e);
            }

            helpCommand.execute(absSender, message.getFrom(), message.getChat(), new String[] {});
        }));
    }

    @Override
    public String getBotToken() {
        return BOT_TOKEN;
    }

    // обработка сообщения не начинающегося с '/'
    @Override
    public void processNonCommandUpdate(Update update) {

        LOG.info("Processing non-command update...");

        if (!update.hasMessage()) {
            LOG.error("Update doesn't have a body!");
            throw new IllegalStateException("Update doesn't have a body!");
        }

        Message msg = update.getMessage();
        User user = msg.getFrom();

        LOG.info(LogTemplate.MESSAGE_PROCESSING.getTemplate(), user.getId());

        if (!canSendMessage(user, msg)) {
            return;
        }

        String clearMessage = msg.getText();
        String messageForUsers = String.format("%s:n%s", mAnonymouses.getDisplayedName(user), msg.getText());

        SendMessage answer = new SendMessage();

        // отправка ответа отправителю о том, что его сообщение получено
        answer.setText(clearMessage);
        answer.setChatId(msg.getChatId());
        replyToUser(answer, user, clearMessage);

        // отправка сообщения всем остальным пользователям бота
        answer.setText(messageForUsers);
        Stream<Anonymous> anonymouses = mAnonymouses.anonymouses();
        anonymouses.filter(a -> !a.getUser().equals(user))
        .forEach(a -> {
            answer.setChatId(a.getChat().getId());
            sendMessageToUser(answer, a.getUser(), user);
        });
    }

    // несколько проверок, чтобы можно было отправлять сообщения другим пользователям
    private boolean canSendMessage(User user, Message msg) {

        SendMessage answer = new SendMessage();
        answer.setChatId(msg.getChatId());

        if (!msg.hasText() || msg.getText().trim().length() == 0) {
            LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to send empty message!", user.getId());
            answer.setText("You shouldn't send empty messages!");
            replyToUser(answer, user, msg.getText());
            return false;
        }

        if(!mAnonymouses.hasAnonymous(user)) {
            LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to send message without starting the bot!", user.getId());
            answer.setText("Firstly you should start bot! Use /start command!");
            replyToUser(answer, user, msg.getText());
            return false;
        }

        if (mAnonymouses.getDisplayedName(user) == null) {
            LOG.log(Level.getLevel(LogLevel.STRANGE.getValue()), "User {} is trying to send message without setting a name!", user.getId());
            answer.setText("You must set a name before sending messages.nUse '/set_name <displayed_name>' command.");
            replyToUser(answer, user, msg.getText());
            return false;
        }

        return true;
    }

    private void sendMessageToUser(SendMessage message, User receiver, User sender) {
        try {
            execute(message);
            LOG.log(Level.getLevel(LogLevel.SUCCESS.getValue()), LogTemplate.MESSAGE_RECEIVED.getTemplate(), receiver.getId(), sender.getId());
        } catch (TelegramApiException e) {
            LOG.error(LogTemplate.MESSAGE_LOST.getTemplate(), receiver.getId(), sender.getId(), e);
        }
    }

    private void replyToUser(SendMessage message, User user, String messageText) {
        try {
            execute(message);
            LOG.log(Level.getLevel(LogLevel.SUCCESS.getValue()), LogTemplate.MESSAGE_SENT.getTemplate(), user.getId(), messageText);
        } catch (TelegramApiException e) {
            LOG.error(LogTemplate.MESSAGE_EXCEPTION.getTemplate(), user.getId(), e);
        }
    }
}

Наконец, запуск бота:

BotInitializer.java

package io.example.anonymizerbot;

import io.example.anonymizerbot.bot.AnonymizerBot;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.telegram.telegrambots.ApiContextInitializer;
import org.telegram.telegrambots.bots.DefaultBotOptions;
import org.telegram.telegrambots.meta.ApiContext;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException;

public final class BotInitializer {

    private static final Logger LOG = LogManager.getLogger(BotInitializer.class);

    private static final String PROXY_HOST = "xx.xx.xxx.xxx";
    private static final int PROXY_PORT = 9999;

    public static void main(String[] args) {

        try {

            LOG.info("Initializing API context...");
            ApiContextInitializer.init();

            TelegramBotsApi botsApi = new TelegramBotsApi();

            LOG.info("Configuring bot options...");
            DefaultBotOptions botOptions = ApiContext.getInstance(DefaultBotOptions.class);

            botOptions.setProxyHost(PROXY_HOST);
            botOptions.setProxyPort(PROXY_PORT);
            botOptions.setProxyType(DefaultBotOptions.ProxyType.SOCKS4);

            LOG.info("Registering Anonymizer...");
            botsApi.registerBot(new AnonymizerBot(botOptions));

            LOG.info("Anonymizer bot is ready for work!");

        } catch (TelegramApiRequestException e) {
            LOG.error("Error while initializing bot!", e);
        }
    }
}

Вот и все! Бот готов к первым тестам.

Пример использования

В качестве примера рассмотрим следующий сценарий:

  1. ‘A’ начинает работу с ботом (/start);
  2. ‘A’ пытается отправить сообщение;
  3. ‘A’ пытается задать имя (/set_name);
  4. ‘A’ задает имя (/set_name Pendalf);
  5. ‘A’ посылает сообщение другим пользователям (которых нет);
  6. ‘B’ начинает работу с ботом (/start);
  7. ‘B’ задает имя (/set_name Chuck Norris);
  8. ‘B’ посылает сообщение другим пользователям;
  9. ‘A’ видит сообщение от ‘B’ и посылает сообщение в ответ;
  10. ‘B’ видит ответ от ‘A’ и больше ему не пишет…



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

Ботами (bot) и чат ботами (chat bots) называют специальные аккаунты в Телеграмм, используемые для автоматической обработки и отправки сообщений. На практике пользователи взаимодействуют с ботами посредством сообщений, которые они отправляют как через обычные, так и через групповые чаты. Бот работает по определенной логике — она контролируется с помощью HTTPS-запросов к специальному API для ботов от Телеграм.

Возможности ботов

Приведем несколько классических примеров применения ботов в Телеграмм:

  1. Утилиты и инструменты. Телеграм-бот может переводить тексты, отображать актуальную погоду, предупреждать о каких-либо предстоящих событиях, использоваться для проведения опросов.
  2. Интеграция с  сервисами. Бота можно использовать для отправки комментариев либо уведомлений, управления «умным домом».
  3. Игры (как одно-, так и многопользовательские). Бот без проблем поиграет с вами в шахматы/шашки, проведет викторину и т. п.
  4. Социальные сервисы. При необходимости специальный бот найдет вам собеседника, взяв за основу для поиска ваши интересы и увлечения.
  5. Все остальное. Это «все остальное» ограничивается лишь вашей фантазией. На деле вы можете запрограммировать бота практически для чего угодно. Однако стоит понимать, что он все равно останется ботом, а значит, не сможет помыть посуду вместо вас.

Если резюмировать вышеперечисленное одним предложением, то бот в Телеграм умеет оставлять комментарии к записям и постам, переводить тексты, искать информацию и аудио/видеоданные, спрашивать и отвечать на вопросы, подключаться к сети, обходить блокировки роутеров и сервисов, создавать чаты для общения, транслировать презентации и т. д. — всего не перечислишь. 

Как функционируют боты?

Боты — особые аккаунты, по сути, представляющие собой интерфейс к вашему сервису, работающему на удаленном сервере. Плюс в том, что для создания бота вам совершенно не обязательно изучать низкоуровневые технологии, так как все взаимодействие основано на обычном HTTPS-интерфейсе с упрощенными методами API — его называют Bot API.

В реальности вы можете создать бота в Телеграмм с помощью… бота. Для этого потребуется написать пользователю @BotFather , а потом следовать его инструкциям. После создания вы получите специальный ключ авторизации (токен). Выполнить необходимые настройки можно будет в разделе документации Bot API.

Если вы не ищете легких путей, хотите прокачаться в Java и привыкли все творить своими руками, вы можете написать бота, используя язык программирования Java («Джава», «Ява»). Ниже рассмотрим один из возможных алгоритмов действий.

Пишем бот на Java

На деле написать бота для Телеграмм, используя Java, не так уже сложно. Рассмотрим пример создания бота посредством Webhook.

Общая последовательность действий будет следующей:

  1. Открываем «Эклипс», создаем новый Java-проект.
  2. Находим и загружаем базу, необходимую для создания Telegram-ботов.
  3. Импортируем загруженную библиотеку в проект.
  4. Создаем класс test.SimpleBot со следующим содержимым:

— запуск мессенджера;

— открытие веб-браузера, переход по ссылке: https://telegram.me/botfather;

— нажатие кнопки «Send message»;

— выбор BotFather в Телеграме;

— команда /start;

— запуск /newbot;

— ввод имени бота на Webhook.

Также надо будет придумать имя пользователя для вновь созданного бота. Тут главное, чтобы это имя было уникальным. После ввода имени надо будет нажать кнопку подтверждения, в результате чего появится сообщение об успешной конфигурации. Обратите внимание, что после «Use this token to access the HTTP API:» выведется ваш токен, который надо будет ввести в требуемом месте.

Что дальше:

  1. Переходим в «Эклипс», запускаем бота.
  2. В адресной строке веб-браузера набираем https://telegram.me/имя_вашего_бота (это необходимо для тестирования работоспособности).
  3. Нажимаем «Send message».
  4. Возвращаемся в Телеграм, выбираем созданного бота.
  5. Кликаем «Старт».

Все, Telegram-bot Webhook, написанный на «Джава», готов. На данном этапе на любое обращение робот должен отвечать что-то в стиле «Я не знаю, что ответить на это», однако эту фразу можно поменять путем дополнения  базы.

Каковы плюсы Telegram-бота на Java

Можно перечислить ряд преимуществ такой реализации:

  1. Простота.
  2. Минимум выполняемых операций.
  3. Минимум требуемых знаний и умений.

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

По материалам:

  • https://stelegram.ru/faq/pravila-sozdaniya-telegramm-bota-na-java;
  • https://tlgrm.ru/docs/bots.

Cover image for Building a JAVA Telegram Bot and Deploying in heroku

VINU

VINU

Posted on Mar 5, 2022

• Updated on Mar 12, 2022

• Originally published at vinuxd.me



 



 



 



 



 

 

Overview

Here we are going to use Spring Boot Maven to Build a JAVA Telegram bot which (echoes) sends back the received text message to the user. Then we are going to deploy our bot on Heroku.
Let’s get in by assuming you have a basic knowledge in Java.


Prerequisites

  • A Code Editor
    • VSCode or Eclipse
  • JDK 11 or more
  • Patience and Time.

Initializing our code

  • First things first, Go to the following link which points to Spring Initializr’s official webpage and download the premade configuration I made to initialize our project.
  • Unzip the downloaded file to your preffered location.
  • Open your preferred code editor and hit Import existing project.

Adding dependencies

  • Find a file named pom.xml in the root of your project. Open the file and add the following dependencies.
  • If you look carefully you can see the following tags in our pom.xml file.
<dependencies>

  </dependencies>

Enter fullscreen mode

Exit fullscreen mode

  • And that’s the right place to add our dependencies.
  • We are going to use Rubenlagus TelegramBots library to build our bot.
  • Add the following contents in the <dependencies> section.
<dependencies>

     <dependency>
       <groupId>org.telegram</groupId>
       <artifactId>telegrambots-spring-boot-starter</artifactId>
       <version>5.7.1</version>
    </dependency>

</dependencies>

Enter fullscreen mode

Exit fullscreen mode

  • Save and Update the project by using Shift+Alt+U or with the Sync button and we are done adding dependencies.

Declaring Env Variables

  • To make our bot work we need to declare some environmental variables.
  • Here we are going to add two mandatory variables named,
    • BOT_TOKEN
    • BOT_USERNAME
  • Since it was a Spring Boot application, We need to add a variable named,
    • PORT
  • Navigate to srcmainresources and rename application.properties to applicaton.yml and add the following variables.
bot:
  BOT_TOKEN: <Super_Secret_Token_Here>
  BOT_USERNAME: <ExampleBot>
server:
  PORT: 5000

Enter fullscreen mode

Exit fullscreen mode

Programming Part

  • Navigate to src/main/java/com/echobot/echobotexample.
  • You can see there will be a file named EchobotexampleApplication.java and thats the main class of our project. Leave the file as it is.
  • Create a class file named EchoBot.javain the same directory and that’s the actual bot class.

Things to do

  • Importing Packages
  • Getting Variables
  • getBotToken() method
  • getbotUsername() method
  • onUpdateReceived() method
  • Procfile

Importing Packages

  • Lets import the necessary packages and make our EchoBot class extending TelegramLongPollingBot. (We aren’t using Webhook here)
// Defining Package name
package com.echobot.echobotexample;

// @Value annotation
import org.springframework.beans.factory.annotation.Value;
// @Component annotation
import org.springframework.stereotype.Component;
// LongPollingBot class
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
// Update Method
import org.telegram.telegrambots.meta.api.objects.Update;
// SendMessage method
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;

@Component
class EchoBot extends TelegramLongPollingBot {

}

Enter fullscreen mode

Exit fullscreen mode

Getting Variables

  • We are going to get the BOT_TOKEN and BOT_USERNAME from application.yml to our java file.
String BOT_TOKEN;
String BOT_USERNAME;

EchoBot(@Value("${bot.BOT_TOKEN}") String BOT_TOKEN, @Value("${bot.BOT_USERNAME}") String BOT_USERNAME) {
        this.BOT_TOKEN = BOT_TOKEN;
        this.BOT_USERNAME = BOT_USERNAME;
    }

Enter fullscreen mode

Exit fullscreen mode

Returning Token and Username

  • To make our bot work we need to return the BOT_TOKEN and BOT_USERNAME from the getBotToken() and getbotUsername() methods.
  • And It is necessary to @Override these getBotToken() and getBotUsername() methods.
    @Override
    public String getBotToken() {
        return BOT_TOKEN;
    }

    @Override
    public String getBotUsername() {
        return BOT_USERNAME;
    }

Enter fullscreen mode

Exit fullscreen mode

Actual Bot

  • After a long time of coding we are now ready to code our actual bot.
  • So, lets create a method named onUpdateReceived() with Update update as a parameter and @Override it.
  • As we know, Our bot will echo back the received text message.
  • Lets Start with the following code.
// onUpdateReceived method
@Override
public void onUpdateReceived(Update update) {
  // Checking if the update has message and it has text
  if (update.hasMessage() && update.getMessage().hasText()) {
      // Creating object of SendMessage
      SendMessage message = new SendMessage();
      // Setting chat id
      message.setChatId(update.getMessage().getChatId().toString());
      // Setting reply to message id
      message.setReplyToMessageId(update.getMessage().getMessageId());
      // Getting and setting received message text
      message.setText(update.getMessage().getText());
      try {
        // Sending message
        execute(message);
    } catch (Exception e) {
        e.printStackTrace();
    }
  }
}

Enter fullscreen mode

Exit fullscreen mode

  • And Thats it! Programming part is finished!

Procfile

  • Since We are deploying in Heroku, We need a Procfile to tell How to Build and Run our app.
  • We are going to create this file in root of our project.
  • The Procfile should look something like this,
worker: java -Dserver.port=$PORT $JAVA_OPTS -jar target/*.jar

Enter fullscreen mode

Exit fullscreen mode

  • Now We are ready to deploy our bot in cloud.

Deployment

  • If you are new to this, Learn more from official docs.
  • So, Lets start by creating a heroku account.
  • Go to dashboard and Click Create new app.
  • Navigate to Deploy panel and Connect Github.
  • Search for your repository and Click enter.
  • On Manual Deploy section, Select the Branch and click Deploy.
  • The most awaited final results are comming!
  • Go to LOGS and Wait until Build finishes.
  • If things gone good? Sit back and relaxx.
  • Then go to your telegram bot and send any text and watch it echo back.

Conclusion

  • So, In this blog we developed and deployed a Java telegram bot.
  • And Guess What? The example I used here is Open Source! You can get the whole source code in Github. Click me to grab and don’t forgot to ⭐
  • In case of any errors, Please Open a issue on Github.
  • Still having doubts? Feel free to reach me in Telegram.

Понравилась статья? Поделить с друзьями:
  • Как написать текст описание 3 класс литературное чтение
  • Как написать текст об авторе
  • Как написать текст ниже строки
  • Как написать текст некролога
  • Как написать текст нейросетью