JavaRush/Java блог/Random/Создание Telegram-бота на Java: от идеи до деплоя
John Watson
27 уровень

Создание Telegram-бота на Java: от идеи до деплоя

Статья из группы Random
участников
Что же такое вообще боты? Подробно почитать об этом можно здесь. Для начала вам необходимо ознакомиться с официальной документацией к библиотеке для разработки ботов на Telegram(далее API). Лежит она здесь. Создание Telegram-бота на Java: от идеи до деплоя - 1Там все очень доступно и понятно. Казалось бы, пиши да радуйся! Но не все так просто. Проведя много времени в поисковиках я находил отрывки знаний по разработке ботов, например, как сделать клавиатуру, обработать CallbackQuery и тому подобное. Полного и исчерпывающего руководства для разработки ботов на Java я так и не нашел. Это и натолкнуло меня на написание этой статьи. В Интернете много сайтов, где можно создать своего бота с уже готовым деплоем. Но дело в том. что в своем большинстве создаются боты, которые могут предоставить справочную информацию и прочее. Наш бот - полноценное веб-приложение, к которому можно привязать базу данных, выполнять запросы на различные API, парсить сайты, выполнять сложные вычисления и прочее. Дело ограничено только вашей фантазией. Я надеюсь что в этих строках я немного разъяснил вам о чем собираюсь писать. Бота в Telegram зарегистрировать очень просто, этот процесс подробно описан в документации по ссылке выше. Для нашего приложение необходимо знать только имя бота и токен, который вы получите при регистрации. По сути бот — просто консольное веб-приложение. Никакого фронтенда, чистая обработка команд. Если вы желаете хорошо освоить Hibernate или научиться парсить JSON, то такой проект для вас. Начнем с того чтобы подключить зависимость в pom.xml (подразумеваем что вы используете Maven). Сделать это можно так:
<dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambots</artifactId>
            <version>3.5</version>
</dependency>
Затем создаем класс Bot, унаследуем его от класса TelegramLongPollingBot, переопределив его методы:
public class Bot extends TelegramLongPollingBot {

    /**
     * Метод для приема сообщений.
     * @param update Содержит сообщение от пользователя.
     */
    @Override
    public void onUpdateReceived(Update update) {
	String message = update.getMessage().getText();
	sendMsg(update.getMessage().getChatId().toString(), message);
    }

    /**
     * Метод для настройки сообщения и его отправки.
     * @param chatId id чата
     * @param s Строка, которую необходимот отправить в качестве сообщения.
     */
    public synchronized void sendMsg(String chatId, String s) {
        SendMessage sendMessage = new SendMessage();
        sendMessage.enableMarkdown(true);
        sendMessage.setChatId(chatId);
        sendMessage.setText(s);
        try {
            sendMessage(sendMessage);
        } catch (TelegramApiException e) {
            log.log(Level.SEVERE, "Exception: ", e.toString());
        }
    }

    /**
     * Метод возвращает имя бота, указанное при регистрации.
     * @return имя бота
     */
    @Override
    public String getBotUsername() {
        returnBotName;
    }

    /**
     * Метод возвращает token бота для связи с сервером Telegram
     * @return token для бота
     */
    @Override
    public String getBotToken() {
        returnBotToken;
    }
}
Ну и содержимое метода main:
public static void main(String[] args) {
        ApiContextInitializer.init();
        TelegramBotsApi telegramBotsApi = new TelegramBotsApi();
        try {
            telegramBotsApi.registerBot(Bot.getBot());
        } catch (TelegramApiRequestException e) {
            e.printStackTrace();
        }
}
Вписав в методы getBotUsername() и getBotToken() мы запускаем бота. Пока он только перенаправляет нам любые сообщения, которые мы отправим ему, этакое «зеркало». Работает это все следующим образом: когда вы запускаете приложение, оно начинает раз в n количество секунд отправлять на сервер Telegram GET запрос по следующему URL: https://api.telegram.org/BotToken/getMe, где BotToken – токен вашего бота, получая в ответ JSON, в котором находятся все сообщения. Каждое такое сообщение обрабатывается библиотекой и приходит в метод OnUpdateReceived(Update update) объектом Update. С ним то мы и работаем. В этом вся прелесть Telegram-ботов, они могут работать на любом компьютере, для тестирования нужно просто запустить приложение, не нужно деплоить его на хостинг после каждого изменения. Это очень удобно. Само собой бота можно настроить на работу по вебхуку, руководство можно найти на просторах Интернета, мы будем для простоты работать по LongPolling. То как обрабатывать сообщения и что отправлять в ответ ограничено только лишь средствами языка и библиотекой, все остальное на ваше усмотрение. Вы можете сделать бота, который будет искать для вас видео на YouTube, можете сделать бота, который каждый день будет присылать вам то, что вы отправите себе, к примеру, за год, эдакую капсулу времени. А можете научиться интегрироваться к CRM-системам и делать ботов для малого бизнеса, все ограничено вашей фантазией. Идем дальше. Те, кто пользовался ботами знают, что с ними удобно взаимодействовать командами, начинающимися со знака «/», например /start. Но есть способ удобнее — кнопки. Есть два вида кнопок: те, что появляются под полем ввода, ReplyKeyboardMarkup и кнопки, которые находятся непосредственно под сообщением, к которому привязаны, InlineKeyboardMarkup. В документации вы можете поверхностно ознакомиться с их описанием. ReplyKeyboardMarkup. По сути это — массив массивов кнопок, List<KeyboardRow<KeyboardButton>>. Вот пример кода, который создает клавиатуру
public synchronized void setButtons(SendMessage sendMessage) {
        // Создаем клавиуатуру
        ReplyKeyboardMarkup replyKeyboardMarkup = new ReplyKeyboardMarkup();
        sendMessage.setReplyMarkup(replyKeyboardMarkup);
        replyKeyboardMarkup.setSelective(true);
        replyKeyboardMarkup.setResizeKeyboard(true);
        replyKeyboardMarkup.setOneTimeKeyboard(false);

        // Создаем список строк клавиатуры
        List<KeyboardRow> keyboard = new ArrayList<>();

        // Первая строчка клавиатуры
        KeyboardRow keyboardFirstRow = new KeyboardRow();
        // Добавляем кнопки в первую строчку клавиатуры
        keyboardFirstRow.add(new KeyboardButton(“Привет”));

        // Вторая строчка клавиатуры
        KeyboardRow keyboardSecondRow = new KeyboardRow();
        // Добавляем кнопки во вторую строчку клавиатуры
        keyboardSecondRow.add(new KeyboardButton(“Помощь”);

        // Добавляем все строчки клавиатуры в список
        keyboard.add(keyboardFirstRow);
        keyboard.add(keyboardSecondRow);
        // и устанваливаем этот список нашей клавиатуре
        replyKeyboardMarkup.setKeyboard(keyboard);
    }
В методе sendMsg() мы вызываем этот метод, передавая ему сообщение, таким образом устанавливая для такого сообщения клавиатуру. Когда мы отправим это сообщение пользователю, то он увидит текст сообщения, который мы установили, а также 2 кнопки, на которых будет написано Привет и Помощь, друг под дружкой. По нажатию на эти кнопки боту будет отправлено сообщение, текст которого представляет собой то, что написано на кнопке. То есть если клиент нажмет «Помощь», то боту придет сообщение с текстом “Помощь“. Для него это как будто бы клиент сам написал текст “Помощь“ и отправил бы ему. Ну а затем вы обрабатываете такие сообщения. InlineKeyboardMarkup Это тоже массив массивов, он похож на предыдущий Markup, но логика работы здесь немного другая. Такая клавиатура привязывается к определенному сообщению и существует только для него. Вот метод для установки Inline-клавиатуры
private void setInline() {
        List<List<InlineKeyboardButton>> buttons = new ArrayList<>();
        List<InlineKeyboardButton> buttons1 = new ArrayList<>();
        buttons1.add(new InlineKeyboardButton().setText(“Кнопка“).setCallbackData(17));
        buttons.add(buttons1);

        InlineKeyboardMarkup markupKeyboard = new InlineKeyboardMarkup();
        markupKeyboard.setKeyboard(buttons);
    }
Создаем List в List, добавляем в первую строку Inline-кнопку. Такая кнопка может содержать URL, ссылку на канал или же CallbackQuery, о которой я напишу чуть позже. Здесь мы устанавливаем текст для нашей кнопки, который будет видеть пользователь, а затем устанавливаем данные, которые будут отправлены боту. В нашем примере пользователь видит «Привет», а боту при нажатии отправится число 17, это и есть наш CallbackQuery. Пару слов о CallbackQuery. Для получения таких данных из объекта Update нужно выполнить update.getCallbackQuery(), этот метод возвращает CallbackQuery, из которого уже можно получить данные, переданные боту. Не нужно пытаться получить эти данные через метод update.getMessage().getText(), получите NullPointerException.
@Override
    public void onUpdateReceived(Update update) {
        if(update.hasMessage()) {
            ThreadClass thread = new ThreadClass(update.getMessage());
        } else  if(update.hasCallbackQuery()) {
            AnswerCallbackThread answerThread = new AnswerCallbackThread(update.getCallbackQuery());
        }
    }
Если есть сообщение, отправляем на обработку в новый поток сообщение, если есть CallbackQuery, отправляем его на обработку в соответствующий поток. На CallbackQuery можно отправлять ответ. У каждого объекта в Telegram есть свой id. Для отправки ответа на определенный CallbackQuery нужно знать лишь его id, который мы получим из соответствующего объекта. Для отправки ответа вызовем такой метод:
public synchronized void answerCallbackQuery(String callbackId, String message) {
        AnswerCallbackQuery answer = new AnswerCallbackQuery();
        answer.setCallbackQueryId(callbackId);
        answer.setText(message);
        answer.setShowAlert(true);
        try {
            answerCallbackQuery(answer);
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }
ВАЖНО: Текст в ответе на CallbackQuery должен быть не длиннее 200 символов! При отправке такого ответа клиент получит всплывающее окно, в котором будет написано сообщение. Такое окно может исчезнуть через несколько секунд само после появления, а может висеть до тех пор, пока пользователь не нажмет ок. Для переключения этих режимов мы вызываем метод answer.setShowAlert(true). При true окошко висит до нажатия ок, при false исчезает через 5 секунд. В принципе это все базовые фишки библиотеки Telegram bot. Такие вещи как отправка мультимедиа, геолокации и тд вы при желании сможете освоить из документации. Давайте перейдем к деплою нашего бота на хостинге. Для своего проекта я выбрал Heroku, тк по моему мнению это достаточно удобный хостинг, который имеет свой CLI. Он бесплатен, но на таком тарифе ваш бот при отсутствии запросов будет уходить в спячку через 30 минут. Когда же к нему будет отправлен запрос, он просыпается. Это происходит довольно быстро, вы даже не заметите(если конечно коннект к БД не поднимается заново). Ограничение на бесплатный тариф - 5MБ база данных, 100МБ дисковое пространство, 2ТБ траффика в месяц, 1 дино. Дино — это ваше запущенное приложение. Скажу сразу, именно стадия деплоя вызвала у меня трудности, так как я до этого никогда не разворачивал свои приложения. Heroku при деплое требует наличия файла с именем Procfile(без расширения). Создаем его в корне проекта, пишем туда worker: sh target/bin/workerBot workerBot – имя, которое мы указываем в pom.xml Будет запускаться sh скрипт, генерируемый с помощью Maven плагина appassembler-maven-plugin. В скрипте описан запуск скомпилированного jar. Имя запускаемого класса указывается между <mainClass></mainClass>, имя скрипта между <name></name> pom.xml:
...
<build>
    <plugins>
        ...
       <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>appassembler-maven-plugin</artifactId>
            <version>1.1.1</version>
            <configuration>
                <assembleDirectory>target</assembleDirectory>
                <programs>
                    <program>
                        <mainClass>com.home.server.TelegramBot</mainClass>
                        <name>workerBot</name>
                    </program>
                </programs>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>assemble</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
Перед началом этого процесса вам следует зарегистрироваться на Heroku, установить Git и Heroku CLI. Если вашему приложению необходима БД, то при оформлении нового приложения не забудьте добавить нужную вам БД. Далее вам необходимо узнать host, username, password и port вашей БД, а после указать в своем приложении. Далее перед деплоем выполните сборку вашего проекта с помощью Maven.
mvn clean install
Для начала мы переходим в каталог нашего проекта, инициализируем репозиторий командой git init Затем добавляем в этот репозиторий свой проект
git add .
После коммитим изменения
git commit -m “First commit in project”
Далее вам нужно залогиниться на heroku, пишем в командной строке
heroku login
Вводим свои данные, указаные при регистрации. После вам нужно узнать URL вашего репозитория на heroku, делается это в настройках. Затем пишем
git remote add heroku [url]
Для вашего репозитория добавится удаленный репозиторий heroku. Далее пишем
git push heroku master
Ждем… При успешном деплое приложения выполняем команду
heroku ps:scale worker=1
И все, ваше приложение запущено. Если же этого не произошло, просмотрите внимательно логи, скорее всего возникает ошибка в вашем приложении, из-за которой оно упало. Спасибо за прочтение такой длинной статьи, надеюсь кому-нибудь это окажется полезным и сэкономит массу времени в тех местах, где я спотыкался при разработке.
Комментарии (92)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Андрей
Уровень 42
20 сентября 2023, 11:44
тут как всегда: читаешь, вроде все ок, потом херак и ничего не понятно
Aleksey Работает в DжаваРаш
8 января 2023, 13:55
да что тут происходит с этим е..учим log.log(Level.SEVERE, "Exception: ", e.toString()); какую библиотеку использовали, что подгружали ? Как это работает ? где это прочитать ? госпааааааааадиииии
DanyaKz
Уровень 2
18 января 2023, 14:46
Статья устарела. Я случайно наткнулся сейчас на эту, недавно писал бота и деплоил его.В чем ваша проблема ? Сам на этой неделе через многое прошел. Могу помочь
Aleksey Работает в DжаваРаш
20 января 2023, 12:00
Привет! Я пытаюсь просто разобраться как писать ботов. А именно где найти нормальную документацию, и самое главное, как эту документацию использовать (в этом самая большая проблема) В данный момент я уже смог написать бота зеркало. Пока что разбираюсь потихоньку дальше.. Например непонятно откуда он взял в своей статье объект "log", вот где его найти в доках ?
Руслан
Уровень 43
24 января 2023, 19:42
Можете помочь с деплоем? Есть альтернативы хероку?
NextGenSeafarer Курьер в JavaCode
31 января 2023, 12:19
сервер на том же timeweb стоит 180р в месяц (1 CPU, 1 RAM, 15 Gb NVME)
Alex T
Уровень 35
26 мая 2022, 21:44
"Полного и исчерпывающего руководства для разработки ботов на Java я так и не нашел. Это и натолкнуло меня на написание этой статьи." - создал еще одно неисчерпывающее руководство? ) "Если вашему приложению необходима БД, то при оформлении нового приложения не забудьте добавить нужную вам БД." - ваш кэп ) "Если вы желаете хорошо освоить Hibernate или научиться парсить JSON, то такой проект для вас." - как много вопросов к тексту...
Алексей
Уровень 28
23 февраля 2022, 06:39
Статья устарела! Рабочий пример я нашел вот здесь: https://github.com/rubenlagus/TelegramBots/wiki/Getting-Started pom.xml :
<dependencies>
        <dependency>
            <groupId>org.telegram</groupId>
            <artifactId>telegrambots</artifactId>
            <version>5.7.1</version>
        </dependency>
    </dependencies>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
Андрей Dungeon Master
22 ноября 2021, 14:27
Дизлайк за ссылку на неоф документацию Официальная документация здесь
Dmitry
Уровень 27
23 марта 2023, 17:38
что-то я не нашел нормальных примеров java-кода в твоей официальной документации.
Konstantin Medical Interpreter в Hospital
14 ноября 2021, 16:36
Еще не когда не программировал нечего кроме задач на ДжаваРаш, решил по пробовать с телеграм бота наткнулся на эту статью вообще не помогло((
Oleg Viktorovich
Уровень 41
22 февраля 2021, 20:23
Зачем содержать мертвые статьи, которые уже не работают или устарели, кладбище статей растет.
Тимур
Уровень 37
8 февраля 2021, 18:22
Кто знает, как получить через клиента сообщения с каналов и ботов? Вопрос может не по теме, но может кто сталкивался. Получил я api id и api hash от своего телеграма, а куда копать дальше?
Babusya
Уровень 35
1 февраля 2023, 04:15
//создать папку resources //потом application.properties // и в application.properties bot.name= bot.token= bot.owner=
Никита Ростов
Уровень 8
30 ноября 2020, 10:22
У одного меня проблема на проблеме? Теперь проблема с "setCallbackData", кто сталкивался?
Никита Ростов
Уровень 8
13 ноября 2020, 22:43
С ApiContextInitializer.init(); и с log, то что ниже в коментариях не помогает Были проблемы у кого -то?
Пальван Розыев
Уровень 4
17 ноября 2020, 07:08
Думаю в новой версии TelegramBots 5.0.0 инициализировать надо следующим образом:
public class Main {
    public static void main(String[] args) {
        try {
            TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
            botsApi.registerBot(new MyAmazingBot());
        } catch (TelegramApiException e) {
            e.printStackTrace();
        }
    }
}
Getting Started with TelegramBots
Никита Ростов
Уровень 8
22 ноября 2020, 13:47
Да я уже пробовал, код с этой ссылки ранее, но тут проблема возникала в этом " .setChatId".
Пальван Розыев
Уровень 4
22 ноября 2020, 19:43
Пробовал так? String chatId = update.getMessage().getChatId().toString(); message.setChatId(chatId);
Никита Ростов
Уровень 8
23 ноября 2020, 17:20
нет, но на удивление этот код заработал. " .setChatId(update.getMessage().getChatId()) .setText(update.getMessage().getText());" Но теперь у меня проблема с DefaultBotSession.class Спасибо вам большое за ответ.