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

Створення Telegram-бота на Java: від ідеї до деплоя

Стаття з групи Random UA
Що ж таке взагалі боти? Детально почитати про це можна тут . Для початку вам необхідно ознайомитись з офіційною документацією до бібліотеки для розробки роботів на Telegram(далі API). Лежить вона тут . Створення Telegram-бота на Java: від ідеї до деплою - 1Там все дуже доступно та зрозуміло. Здавалося б, пиши та радуйся! Але не все так просто. Провівши багато часу в пошукових системах я знаходив уривки знань з розробки роботів, наприклад, як зробити клавіатуру, обробити CallbackQuery тощо. Повного та вичерпного посібника для розробки ботів на Java я так і не знайшов. Це й наштовхнуло мене на написання цієї статті. В Інтернеті багато веб-сайтів, де можна зробити власного робота з вже готовим деплоєм. Але річ у тому. що у своїй більшості створюються боти, які можуть надати довідкову інформацію та інше. Наш бот - повноцінний веб-додаток, до якого можна прив'язати базу даних, виконувати запити на різні API, парсувати сайти, виконувати складні обчислення та інше. Справа обмежена лише вашою фантазією. Я сподіваюся, що у цих рядках я трохи роз'яснив вам про що збираюся писати. Бота в Telegram зареєструвати дуже просто, цей процес докладно описаний у документації на посилання вище. Для нашого додатка необхідно знати лише ім'я бота та токен, який ви отримаєте під час реєстрації. По суті бот - просто консольна веб-додаток. Жодного фронтенду, чиста обробка команд. Якщо ви бажаєте добре освоїти Hibernate або навчитися парсити JSON, такий проект для вас. Почнемо з того, щоб підключити залежність у pom.xml (маємо на увазі що ви використовуєте Maven). Зробити це можна так: Якщо ви бажаєте добре освоїти Hibernate або навчитися парсити JSON, такий проект для вас. Почнемо з того, щоб підключити залежність у pom.xml (маємо на увазі що ви використовуєте Maven). Зробити це можна так: Якщо ви бажаєте добре освоїти 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()ми викликаємо цей метод, передаючи йому повідомлення, встановлюючи для такого повідомлення клавіатуру. Коли ми надішлемо це повідомлення користувачу, то він побачить текст повідомлення, який ми встановабо, а також дві кнопки, на яких буде написано Привіт і Допомога, один під одним. Після натискання на ці кнопки боту буде відправлено повідомлення, текст якого є те, що написано на кнопці. Тобто, якщо клієнт натисне «Допомога», то боту прийде повідомлення з текстом «Допомога». Для нього це начебто клієнт сам написав текст "Допомога" і відправив би йому. Ну, а потім ви обробляєте такі повідомлення. 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
І все, ваш додаток запущено. Якщо ж цього не сталося, уважно перегляньте логи, швидше за все виникає помилка у вашому додатку, через яку воно впало. Дякую за прочитання такої довгої статті, сподіваюся комусь це виявиться корисним і заощадить багато часу в тих місцях, де я спотикався при розробці.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ