Вітаю вас, мої любі друзі. Так-так, саме друзі. Я вже так зрісся з цією серією статей, що ті люди, які регулярно пишуть у коментарях свою подяку та/або показують, що прочитали та зрозуміли матеріал, стали вже близькими. Ми з вами йдемо із двох сторін до однієї мети. Ви хочете зрозуміти, а я хочу пояснити. І кінцева мета у нас одна - написана програма, яка зрозуміла вам від початку до кінця. Багато про те, що опишу в цій статті ви, можливо, вже чули. Не думаю, що я розповім щось нове та неординарне (але в рамках проекту знати/повторити це необхідно).
Навесні я писав робота для себе, так що будемо спиратися на його "лекала".
Далі нас просять дати ім'я цьому роботу. Так як це бот для тестових завдань, то і ім'я у нього буде відповідне: [TEST] JavarushBot
Тепер настав час дати унікальне ім'я, за яким його завжди можна буде знайти - його username: test_codegym_community
Як я й говорив вище, потрібно додавати суфікс _bot для username, тому пишемо ще раз: test_codegym_community_bot
І все! Бот створено. Тепер за username і token його можна підключити до нашого проекту. Зрозуміло для безперебійної роботи тестового сервера я не виставлятиму token (по суті це пароль до доступу до бота) цього бота на загальний огляд.
Виходячи з опису, нам потрібно просто створити новий клас, успадкуватись від TelegramLongPollingBot і додати цей клас до Application Context нашого SpringBoot. Application Context – це місце, де зберігаються створені об'єкти для роботи проекту. Щоб додати якийсь клас, потрібно використовувати одну з анотацій: @ Component, @ Service, @ Repository, @ Controller. Або інструкцію @Bean, якщо створюється через спосіб в конфігураційному класі (тобто в класі, який позначений інструкцією Configuration). Я розумію, що все це поки що здається незрозумілим. Але коли ви почнете розумітися, побачите, що нічого складного там немає. Щоб швидко розібратися зі Spring Boot, раджу круту книжку Spring In Action 5th edition. Якщо буде бажання, я можу написати серію статей з цієї книги. Повертаємось назад. У пакеті, в якому лежить JavarushTelegramBotApplication, створюємо пакет bot , в якому лежатиме наш телеграм-бот. Ім'я у нього буде CodeGymTelegramBot :
У SpringBoot є відмінна анотація - @Value. Якщо її правильно використовувати, вона підтягне нам значення з application.properties файлу. Оновлюємо проект під це:
З логів бачимо, що бот запустився. Значить, настав час йти в Телеграм і написати боту:
Натискаємо почати і нам відразу ж надходить відповідь:
Напишемо ще якусь лабуду, щоб перевірити:
І все, на цьому моменті можна сказати, що завдання наше JRTB-2 завершено. Тут поки що особливо тестів не напишеш, тож залишимо все як є. Далі потрібно створити новий комміт:
Зверніть увагу на ім'я комміта: знову загострюю вашу увагу на цьому. Коміт спочатку містить ім'я завдання, а потім вже детальніший опис, що зроблено. Натискаємо Commit and Push ... і підтверджуємо, ще раз натиснувши Push :
Переходимо в наш проект . Як і раніше, GitHub вже побачив нову гілку та пропонує створити пул-реквест на main. Не чинимо опір і створюємо його:
Вже як зазвичай вибрали лейбу, проект і призначабо її на мене. Наприкінці натискаємо Create Pull Request. Трохи зачекаємо, доки пройде білд — і все, пул-реквест готовий до злиття:
Варто версія 0.0.1-SNAPSHOT . Це чергова версія. А ми почнемо з того, що оновлюватимемо версію проекту з кожним новим вирішеним завданням. Поки ми не вийшли в MVP, версія йтиме з суфіксом -SNAPSHOT. Якою буде схема версіонування? XYZ-SNAPSHOT Де:
Йдемо у файл RELEASE_NOTES, де описуватимемо зміни проекту з кожною новою версією:
Наш перший запис. Тепер при кожному наступному оновленні версії тут описуватимемо що саме сталося. Робимо коміт цієї справи, пишемо опис: JRTB-2: updated project version and added to RELEASE_NOTES Все точно так, як і раніше. Чекаємо, поки білд пройде, і ми зможемо смердити наші зміни. Тільки тут буде дещо інакше. Я хочу зробити так, щоб кожне завдання в main гілці було окремим коммітом, тому просто натиснути в пул-реквестMerge pull request нам не підійде. У гіті є опція git squash, яка збирає всі комміти на один і мержить. Вибираємо цю опцію:
Натискаємо Squash and Merge, і нам пропонують ще відредагувати повідомлення, яке буде в результаті:
Дуже зручно та головне, що потрібне. До речі, на бітбакеті такої фічі я не бачив = / Підтверджуємо мерж. Залишилася зовсім небагато - перекласти завдання в статус Done у нас у дошці, написати коментар з посиланням на пул-реквест і закрити його:
Наша дошка тепер виглядає так:
Дякую їм за це! Що ж тепер робити? Видалити з гіта вже не вийде, тому що навіть якщо я накачу новий коміт вже без цього токена, він все одно залишиться в старому. А видаляти та відкочувати назад коміт я не хочу. Тому я пішов і деактивував токен у згаданого BotFather. Тепер токен є, але він уже недійсний. Підписуйтесь на мій аккаунт гітхаб , щоб раніше публікації статті побачити весь код по ній. Дякую всім за читання, до зустрічі.

Пишемо JRTB-2
Робитимемо так само, як робабо у статті із завданням JRTB-0 :- Оновлюємо main гілку в локальному проекті через комбінацію ctrl + t .
- На основі main гілки створюємо:
- Додаємо бота.
- Створюємо новий коміт з описом зробленого та пушим на гітхаб.
- Створюємо пул-реквест на main гілку і ще раз перевіряємо. Чекаємо, щоб пройшов білд (github actions), мерже в мейн гілку.
- Закриваємо відповідне завдання.
Що таке телеграм-бот
Ми, розробники, можемо подати роботу з телеграм-ботом так: ми використовуємо їх клієнт для роботи з ними. Ми маємо готову бібліотеку для роботи. Є набір дій, після яких телеграм-бот знатиме, що він пов'язаний з нашою програмою. А вже всередині програми ми навчимося отримувати листи, команди та якось їх опрацьовувати. Є така річ як команда в телеграм-ботах : вона починається зі сліша "/". Після неї відразу разом пишемо слово, і це буде вважатися командою. Наприклад, є дві команди, які всі мають знати:- /start - початок роботи з ботом;
- / stop - кінець роботи з ботом.
Створюємо бота у BotFather
Щоб підключити бота, спочатку потрібно створити. Телеграма має підхід — створення бота зі своїм унікальним ім'ям. До нього додаватиметься ще токен (великий рядок, який працює як пароль). Я вже створив бота для JavaRush - @codegym_community_bot . Цей робот ще пустий і нічого не вміє. Головне, щоб наприкінці імені було _bot. Щоб показати, як це зробити, я створю бота, на якому ми будемо тестувати наш функціонал. У термінах реальних проектів це буде test environment (тестове оточення). А наш основний буде prod environment (prod — production, тобто реальне оточення, на якому виконуватиметься проект). Звичайно, можна було б додати ще одне оточення — sandbox environment: спільну пісочницю, що змінюється більш доступно всім учасникам розробки. Але це лише ускладнить ситуацію на етапі створення проекту. Поки що створимо ще два боти для test та для sandbox оточення. Перший крок – створити (зареєструвати) бота у самому Телеграмі. Потрібно знайти бота: @BotFather та написати йому команду: /newbot



Підключаємо робота в проект
Ми не підключатимемо бібліотеку як завжди, а відразу скористаємося перевагами нашого скелета — 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>
Додаємо її до нашого пам'ятника. Виносимо версію як належить і оновлюємо проект. 
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() — а це відповідно токен бота.
- bot.username ;
- bot.token .

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 і шукаємо нашого бота в Телеграмі: 







Версіонування
Я якось упустив момент, що нам потрібно вести версіонування. Для цього зробимо ще кілька змін у нашій гілці. Заходимо назад до IDEA і дивимося на версію проекту у пам'ятнику:
- X - мажорне оновлення версії, часто містить проблеми зі зворотною сумісністю з попередньою версією;
- Y — невеликі зміни, повністю сумісні з попередньою версією;
- Z - лічильник дефектів, які ми знайшли і відремонтували.






Висновок
Сьогодні ми крок за кроком створабо телеграм-бота та впровадабо його у наш SpringBoot проект. Робот працює, дає відповіді. Зробабо відразу ж доступ до даних робота через проперти. Далі більше: робитимемо великий шматок – виконувати JRTB-3 – додавання Command Pattern для нашого проекту. Ох, ще один нюанс. Я ж казав, що не публікуватиму token, щоб ним не скористалися. Але так як я писав статтю вже ближче до півночі і після робочого дня, то вийшло, що я виклав-таки в репозиторій токен, що діє, а сказала мені про це GitGuardian в листі:
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ