JavaRush /Java блог /Random UA /Додаємо телеграм-бота на проект - "Java-проект від А до Я...
Roman Beekeeper
35 рівень

Додаємо телеграм-бота на проект - "Java-проект від А до Я"

Стаття з групи Random UA
Вітаю вас, мої любі друзі. Так-так, саме друзі. Я вже так зрісся з цією серією статей, що ті люди, які регулярно пишуть у коментарях свою подяку та/або показують, що прочитали та зрозуміли матеріал, стали вже близькими. Ми з вами йдемо із двох сторін до однієї мети. Ви хочете зрозуміти, а я хочу пояснити. І кінцева мета у нас одна - написана програма, яка зрозуміла вам від початку до кінця. Багато про те, що опишу в цій статті ви, можливо, вже чули. Не думаю, що я розповім щось нове та неординарне (але в рамках проекту знати/повторити це необхідно). "Java-проект від А до Я": додаємо телеграм-бота на проект - 1Навесні я писав робота для себе, так що будемо спиратися на його "лекала".

Пишемо JRTB-2

Робитимемо так само, як робабо у статті із завданням JRTB-0 :
  1. Оновлюємо main гілку в локальному проекті через комбінацію ctrl + t ."Java-проект від А до Я": додаємо телеграм-бота на проект - 2
  2. На основі main гілки створюємо:"Java-проект від А до Я": додаємо телеграм-бота на проект - 3
  3. Додаємо бота.
  4. Створюємо новий коміт з описом зробленого та пушим на гітхаб.
  5. Створюємо пул-реквест на main гілку і ще раз перевіряємо. Чекаємо, щоб пройшов білд (github actions), мерже в мейн гілку.
  6. Закриваємо відповідне завдання.

Що таке телеграм-бот

Ми, розробники, можемо подати роботу з телеграм-ботом так: ми використовуємо їх клієнт для роботи з ними. Ми маємо готову бібліотеку для роботи. Є набір дій, після яких телеграм-бот знатиме, що він пов'язаний з нашою програмою. А вже всередині програми ми навчимося отримувати листи, команди та якось їх опрацьовувати. Є така річ як команда в телеграм-ботах : вона починається зі сліша "/". Після неї відразу разом пишемо слово, і це буде вважатися командою. Наприклад, є дві команди, які всі мають знати:
  • /start - початок роботи з ботом;
  • / stop - кінець роботи з ботом.
Решту ми зробимо самі. Відразу обмовлюся: ми робитимемо саме те й таким способом, яким навчився я. І в роботі з ботом я впевнений, що можна буде зробити краще. І якщо хтось захоче це зробити — я буду радий і всіляко підтримаю цей почин. До речі, найперше, що було б круто, якби мені хтось пояснив, як запрограмувати опис команд через код, а не через налаштування бота в телеграмі. Цього я не навчився. У нас на ресурсі є кілька статей, в яких є опис, як зробити елементарний бот: ми сьогодні зробимо щось схоже. Якщо будуть ще питання, рекомендую швидко пробігтися за цією статтею.

Створюємо бота у BotFather

Щоб підключити бота, спочатку потрібно створити. Телеграма має підхід — створення бота зі своїм унікальним ім'ям. До нього додаватиметься ще токен (великий рядок, який працює як пароль). Я вже створив бота для JavaRush - @codegym_community_bot . Цей робот ще пустий і нічого не вміє. Головне, щоб наприкінці імені було _bot. Щоб показати, як це зробити, я створю бота, на якому ми будемо тестувати наш функціонал. У термінах реальних проектів це буде test environment (тестове оточення). А наш основний буде prod environment (prod — production, тобто реальне оточення, на якому виконуватиметься проект). Звичайно, можна було б додати ще одне оточення — sandbox environment: спільну пісочницю, що змінюється більш доступно всім учасникам розробки. Але це лише ускладнить ситуацію на етапі створення проекту. Поки що створимо ще два боти для test та для sandbox оточення. Перший крок – створити (зареєструвати) бота у самому Телеграмі. Потрібно знайти бота: @BotFather та написати йому команду: /newbot"Java-проект від А до Я": додаємо телеграм-бота на проект - 4Далі нас просять дати ім'я цьому роботу. Так як це бот для тестових завдань, то і ім'я у нього буде відповідне: [TEST] JavarushBot"Java-проект від А до Я": додаємо телеграм-бота на проект - 5 Тепер настав час дати унікальне ім'я, за яким його завжди можна буде знайти - його username: test_codegym_community"Java-проект від А до Я": додаємо телеграм-бота на проект - 6 Як я й говорив вище, потрібно додавати суфікс _bot для username, тому пишемо ще раз: test_codegym_community_bot"Java-проект від А до Я": додаємо телеграм-бота на проект - 7 І все! Бот створено. Тепер за username і token його можна підключити до нашого проекту. Зрозуміло для безперебійної роботи тестового сервера я не виставлятиму token (по суті це пароль до доступу до бота) цього бота на загальний огляд.

Підключаємо робота в проект

Ми не підключатимемо бібліотеку як завжди, а відразу скористаємося перевагами нашого скелета — SpringBoot. Він має таку річ, як Starter. Підключивши бібліотеку, за його допомогою можна дати знати SpringBoot'у, що ми хочемо налаштувати проект правильно. Якби ми пішли звичайним шляхом, який описаний у безлічі місць, нам би потрібно було десь створити конфігурацію, в якій було б щось таке:
ApiContextInitializer.init();
TelegramBotsApi telegramBotsApi = new TelegramBotsApi();
try {
  telegramBotsApi.registerBot(Bot.getBot());
} catch (TelegramApiRequestException e) {
  e.printStackTrace();
}
Тут створюється об'єкт, з якого можна встановити зв'язок з ботом. У нашому випадку стартер, який ми захочемо підключити, зробить усе за нас десь під капотом (це теж переклад часто використовуваної фрази в IT — under the hood). Ось посилання на цей стартер . Відразу ж по файлу README.md видно, що це, навіщо і як його використовувати. Щоб його підключити, потрібно просто додати цю залежність до пам'ятника. І все :) Ось потрібна залежність:
<dependency>
        <groupId>org.telegram</groupId>
        <artifactId>telegrambots-spring-boot-starter</artifactId>
        <version>5.0.1</version>
    </dependency>
Додаємо її до нашого пам'ятника. Виносимо версію як належить і оновлюємо проект. "Java-проект від А до Я": додаємо телеграм-бота на проект - 8Виходячи з опису, нам потрібно просто створити новий клас, успадкуватись від TelegramLongPollingBot і додати цей клас до Application Context нашого SpringBoot. Application Context – це місце, де зберігаються створені об'єкти для роботи проекту. Щоб додати якийсь клас, потрібно використовувати одну з анотацій: @ Component, @ Service, @ Repository, @ Controller. Або інструкцію @Bean, якщо створюється через спосіб в конфігураційному класі (тобто в класі, який позначений інструкцією Configuration). Я розумію, що все це поки що здається незрозумілим. Але коли ви почнете розумітися, побачите, що нічого складного там немає. Щоб швидко розібратися зі Spring Boot, раджу круту книжку Spring In Action 5th edition. Якщо буде бажання, я можу написати серію статей з цієї книги. Повертаємось назад. У пакеті, в якому лежить JavarushTelegramBotApplication, створюємо пакет bot , в якому лежатиме наш телеграм-бот. Ім'я у нього буде CodeGymTelegramBot :
package com.github.codegymcommunity.jrtb.bot;

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

/**
* Telegrambot for Javarush Community from Javarush community.
*/
@Component
public class JavarushTelegramBot extends TelegramLongPollingBot {

   @Override
   public void onUpdateReceived(Update update) {

   }

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

   @Override
   public String getBotToken() {
       return null;
   }
}
Цей клас був абстрактний і потрібно було реалізувати три методи. Поговоримо про них докладніше:
  • onUpdateReceived(Update update) - це і є точка входу, куди надходитимуть повідомлення від користувачів. Звідси йтиме вся нова логіка;
  • getBotUsername() — тут потрібно додати username нашого бота, до якого з'єднуватимемося;
  • getBotToken() — а це відповідно токен бота.
По суті це як логін та пароль до сайту. На даний момент ми не будемо явно записувати це значення. Це називається "захардкодити" (тобто прив'язати якесь приватне значення - як завжди, калька з англійської hard code). Так не варто робити. Ми підемо іншим шляхом – запишемо ці дані в application.properties клас і звідси їх зчитуватимемо. Навіщо це потрібно? Потім, щоб при запуску програми, ми могли ці значення задати ззовні. Це гнучко, правильно. Йдемо у файл src/main/resources/application.properties. Там вигадаємо імена цим змінним. Файли з розширенням .properties зчитуються як структура ключ-значення з роздільником "=", кожна пара це окремий рядок. Тому я вигадав такі змінні:
  • bot.username ;
  • bot.token .
Ось так це виглядатиме: "Java-проект від А до Я": додаємо телеграм-бота на проект - 9У SpringBoot є відмінна анотація - @Value. Якщо її правильно використовувати, вона підтягне нам значення з application.properties файлу. Оновлюємо проект під це:
package com.github.codegymcommunity.jrtb.bot;

import org.springframework.beans.factory.annotation.Value;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.objects.Update;

/**
* Telegram bot for Javarush Community from Javarush community.
*/
@Component
public class JavarushTelegramBot extends TelegramLongPollingBot {

   @Value("${bot.username}")
   private String username;

   @Value("${bot.token}")
   private String token;

   @Override
   public void onUpdateReceived(Update update) {

   }

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

   @Override
   public String getBotToken() {
       return token;
   }
}
Видно, що ми передали в інструкцію значення змінної. І ось коли SpringBoot буде створювати об'єкт нашого робота, то і значення будуть взяті з пропертів (знову калька з англійської – properties). Ми вже майже мету. Потрібно зробити так, щоби бот щось відповідав. Тому оновимо метод onUpdateReceived . Потрібно, щоб ми отримали повідомлення, яке прийшло до боту, і передали його назад. Так ми будемо знати, що робота працює. Для цього ми грубо та швидко напишемо те, що потрібно:
@Override
public void onUpdateReceived(Update update) {
   if(update.hasMessage() && update.getMessage().hasText()) {
       String message = update.getMessage().getText().trim();
       String chatId = update.getMessage().getChatId().toString();

       SendMessage sm = new SendMessage();
       sm.setChatId(chatId);
       sm.setText(message);

       try {
           execute(sm);
       } catch (TelegramApiException e) {
           //todo add logging to the project.
           e.printStackTrace();
       }
   }
}
Тут все гранично просто: ми перевіряємо, що повідомлення реально існує, тому витягуємо саме повідомлення ( message ) і чат ( чатId ), в якому йде листування. Далі ми створюємо об'єкт для відправлення повідомлення SendMessage , передаємо в нього саме повідомлення і чат чата - тобто те, що відправити боту і куди. Цього нам уже вистачає. Далі запускаємо main метод у клас JavarushTelegramBotApplication і шукаємо нашого бота в Телеграмі: "Java-проект від А до Я": додаємо телеграм-бота на проект - 10З логів бачимо, що бот запустився. Значить, настав час йти в Телеграм і написати боту: "Java-проект від А до Я": додаємо телеграм-бота на проект - 11Натискаємо почати і нам відразу ж надходить відповідь: "Java-проект від А до Я": додаємо телеграм-бота на проект - 12Напишемо ще якусь лабуду, щоб перевірити:"Java-проект від А до Я": додаємо телеграм-бота на проект - 13І все, на цьому моменті можна сказати, що завдання наше JRTB-2 завершено. Тут поки що особливо тестів не напишеш, тож залишимо все як є. Далі потрібно створити новий комміт: "Java-проект від А до Я": додаємо телеграм-бота на проект - 14Зверніть увагу на ім'я комміта: знову загострюю вашу увагу на цьому. Коміт спочатку містить ім'я завдання, а потім вже детальніший опис, що зроблено. Натискаємо Commit and Push ... і підтверджуємо, ще раз натиснувши Push : "Java-проект від А до Я": додаємо телеграм-бота на проект - 15Переходимо в наш проект . Як і раніше, GitHub вже побачив нову гілку та пропонує створити пул-реквест на main. Не чинимо опір і створюємо його: "Java-проект від А до Я": додаємо телеграм-бота на проект - 16Вже як зазвичай вибрали лейбу, проект і призначабо її на мене. Наприкінці натискаємо Create Pull Request. Трохи зачекаємо, доки пройде білд — і все, пул-реквест готовий до злиття:"Java-проект від А до Я": додаємо телеграм-бота на проект - 17

Версіонування

Я якось упустив момент, що нам потрібно вести версіонування. Для цього зробимо ще кілька змін у нашій гілці. Заходимо назад до IDEA і дивимося на версію проекту у пам'ятнику: "Java-проект від А до Я": додаємо телеграм-бота на проект - 18Варто версія 0.0.1-SNAPSHOT . Це чергова версія. А ми почнемо з того, що оновлюватимемо версію проекту з кожним новим вирішеним завданням. Поки ми не вийшли в MVP, версія йтиме з суфіксом -SNAPSHOT. Якою буде схема версіонування? XYZ-SNAPSHOT Де:
  • X - мажорне оновлення версії, часто містить проблеми зі зворотною сумісністю з попередньою версією;
  • Y — невеликі зміни, повністю сумісні з попередньою версією;
  • Z - лічильник дефектів, які ми знайшли і відремонтували.
Тому у нас буде перша версія - 0.1.0-SNAPSHOT - тобто, у нас не було ще мажорних оновлень, всього потроху, і ми ще не вийшли в MVP, тому є суфікс -SNAPSHOT. Змінюємо в пам'ятці цю справу: "Java-проект від А до Я": додаємо телеграм-бота на проект - 19Йдемо у файл RELEASE_NOTES, де описуватимемо зміни проекту з кожною новою версією: "Java-проект від А до Я": додаємо телеграм-бота на проект - 20Наш перший запис. Тепер при кожному наступному оновленні версії тут описуватимемо що саме сталося. Робимо коміт цієї справи, пишемо опис: JRTB-2: updated project version and added to RELEASE_NOTES Все точно так, як і раніше. Чекаємо, поки білд пройде, і ми зможемо смердити наші зміни. Тільки тут буде дещо інакше. Я хочу зробити так, щоб кожне завдання в main гілці було окремим коммітом, тому просто натиснути в пул-реквестMerge pull request нам не підійде. У гіті є опція git squash, яка збирає всі комміти на один і мержить. Вибираємо цю опцію: "Java-проект від А до Я": додаємо телеграм-бота на проект - 21Натискаємо Squash and Merge, і нам пропонують ще відредагувати повідомлення, яке буде в результаті: "Java-проект від А до Я": додаємо телеграм-бота на проект - 22Дуже зручно та головне, що потрібне. До речі, на бітбакеті такої фічі я не бачив = / Підтверджуємо мерж. Залишилася зовсім небагато - перекласти завдання в статус Done у нас у дошці, написати коментар з посиланням на пул-реквест і закрити його: "Java-проект від А до Я": додаємо телеграм-бота на проект - 23Наша дошка тепер виглядає так:"Java-проект від А до Я": додаємо телеграм-бота на проект - 24

Висновок

Сьогодні ми крок за кроком створабо телеграм-бота та впровадабо його у наш SpringBoot проект. Робот працює, дає відповіді. Зробабо відразу ж доступ до даних робота через проперти. Далі більше: робитимемо великий шматок – виконувати JRTB-3 – додавання Command Pattern для нашого проекту. Ох, ще один нюанс. Я ж казав, що не публікуватиму token, щоб ним не скористалися. Але так як я писав статтю вже ближче до півночі і після робочого дня, то вийшло, що я виклав-таки в репозиторій токен, що діє, а сказала мені про це GitGuardian в листі:"Java-проект від А до Я": додаємо телеграм-бота на проект - 25Дякую їм за це! Що ж тепер робити? Видалити з гіта вже не вийде, тому що навіть якщо я накачу новий коміт вже без цього токена, він все одно залишиться в старому. А видаляти та відкочувати назад коміт я не хочу. Тому я пішов і деактивував токен у згаданого BotFather. Тепер токен є, але він уже недійсний. Підписуйтесь на мій аккаунт гітхаб , щоб раніше публікації статті побачити весь код по ній. Дякую всім за читання, до зустрічі.

Список всіх матеріалів серії на початку цієї статті.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ