[Java, Git, DevOps] Пишем Telegram Bota для оповещения о коммите в git репозитарий на базе Gitea и разворачиваем его в Google Cloud Platform
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Здравствуйте как и обещал в продолжение моей статьи о Автоматической публикации приложения в Google Play , рассмотрю в деталях процесс написания Telegram Bot`a для оповещения команды тестировщиков о выпуске новой версии.Регистрация Bota в Telegram и получение IDПросто напишите пользователю @BotFather и следуйте его инструкциям. Выполните последовательно следующий команды
/start
/newbot
bot_name
В итоге вы должны получить сообщение
Из этого сообщение нам понадобятся собственно
- t.me/bot_name - Имя бота по которому мы будем добавлять его в каналы или писать в ЛС
- token - это наш ключ для доступа к API
Подготовка проекта и подключение необходимых библиотекНаш бот будет написан на Java и будет представлять из себя Spring Boot Web приложение, в качестве системы сборки будет использоваться maven1) Создайте обычный Spring Boot проект, проще всего это сделать через встроенный конфигуратор в IntelliJ IDEA , либо используя Spring Initializr. Выберите те зависимости которые посчитаете нужными, но для начала нам подойдёт минимальный наборpom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Минимальный проект будет иметь примерно такую структуру :
А теперь детальней по каждому классу : BotConfig - Конфигурация подтягивающая настройки бота из application.properties
@Configuration
@Data
@PropertySource("classpath:application.properties")
public class BotConfig {
// Имя бота заданное при регистрации
@Value("${botUserName}")
String botUserName;
// Токен полученный при регистрации
@Value("${token}")
String token;
}
BotInitializer - Component регистрации/инициализации бота в системе Telegram
@Component
@Slf4j
public class BotInitializer {
@Autowired
Bot bot;
@EventListener({ContextRefreshedEvent.class})
public void Init() throws TelegramApiException {
TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class);
try {
telegramBotsApi.registerBot(bot);
} catch (TelegramApiRequestException e) {
log.error(exceptionStackTraceToString(e));
}
}
}
Телеграм API должно быть зарегистрировано в системе и делать это нужно уже после поднятия контекста Spring, по этому вешаем слушатель на ContextRefreshedEventBot - Сервис инкапсулирующий в себе реализацию TelegramLongPollingBot,
@Component
@Slf4j
/**
* Касс является основным пулом взаимодействия с Telegram
* получение, обработка, отправка сообщений
*/
public class Bot extends TelegramLongPollingBot {
final
BotConfig config;
public Bot(BotConfig config) {
this.config = config;
}
public void onUpdateReceived(Update update) {
update.getUpdateId();
SendMessage.SendMessageBuilder builder =SendMessage.builder();
String messageText;
String chatId;
if (update.getMessage() != null) {
chatId = update.getMessage().getChatId().toString();
builder.chatId(chatId);
messageText = update.getMessage().getText();
} else {
chatId = update.getChannelPost().getChatId().toString();
builder.chatId(chatId);
messageText = update.getChannelPost().getText();
}
if (messageText.contains("/hello")) {
builder.text("Привет");
try {
execute(builder.build());
} catch (TelegramApiException e) {
log.debug(e.toString());
}
}
if (messageText.contains("/chartId")) {
builder.text("ID Канала : " + chatId);
try {
execute(builder.build());
} catch (TelegramApiException e) {
log.debug(e.toString());
}
}
}
public String getBotUsername() {
return config.getBotUserName();
}
public String getBotToken() {
return config.getToken();
}
}
Данный класс представляет из себя основной функционал для взаимодействия с Telegram
- метод onUpdateReceived принимает и обрабатывает сообщения пришедшие в личку или в канал где бот администратор
WebHook - RestController обслуживающий API для реакции на события WebHook Gitea
@Slf4j
@RestController
@RequestMapping("/api/public/gitea")
@RequiredArgsConstructor
@PropertySource("classpath:application.properties")
public class WebHook {
Bot bot;
// Канал в который будем слать уведомления
@Value("${chartId}")
String chartId;
// Секретный ключ который придёт в нутри JSON от Gitea,
// что бы левые люди не имели доступа к боту т.к. API публичное без авторизации
@Value("${secret}")
String secret;
@Autowired
public WebHook(Bot bot) {
this.bot = bot;
}
@PostMapping(value = "/webhook")
public ResponseEntity<?> webhook(@RequestBody String json){
Gson gson = new Gson();
GiteaWebHook giteaWebHook = null;
try {
giteaWebHook = gson.fromJson(json, GiteaWebHook.class);
} catch (JsonSyntaxException e) {
log.error(Utils.exceptionStackTraceToString(e));
return new ResponseEntity<>(Utils.exceptionStackTraceToString(e), HttpStatus.BAD_REQUEST);
}
if (validationWebHookContent(giteaWebHook)) {
SendMessage.SendMessageBuilder messageBuilder =SendMessage.builder();
messageBuilder.chatId(chartId);
messageBuilder.parseMode(ParseMode.HTML);
StringBuilder builder = new StringBuilder();
builder.append("<b>Проект</b> : " + giteaWebHook.getRepository().getName()+"\n");
for (Commit commit : giteaWebHook.getCommits()) {
builder.append("<b>Автор</b> : " + commit.getAuthor().getName()+"\n");
builder.append("<b>Комментарий</b> : " + commit.getMessage()+"\n");
}
builder.append("<a href="https://play.google.com/store/apps/details?id=URL_ВАШЕГО_ПРИЛАЖЕНИЯ">Обновление будет доступно в Play Market через пару минут</a>\n");
messageBuilder.text(buildToCorrectString(builder));
try {
bot.execute(messageBuilder.build());
} catch (TelegramApiException e) {
log.error(Utils.exceptionStackTraceToString(e));
return new ResponseEntity<>(Utils.exceptionStackTraceToString(e), HttpStatus.INTERNAL_SERVER_ERROR);
}
} else return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
return new ResponseEntity<>(headers, HttpStatus.OK);
}
/**
* Проверка пришедших JSON данных на валидность
* @param giteaWebHook - GiteaWebHook
* @return true - если не null, PUSH в master, совпал секретный ключ
*/
private boolean validationWebHookContent(GiteaWebHook giteaWebHook){
return giteaWebHook != null && // Если вообще что то есть
giteaWebHook.getRef().contains(giteaWebHook.getRepository().getDefaultBranch()) && // Есть был PUSH в /master
giteaWebHook.getSecret().equals(secret); // Если совпал секретный ключ
}
private String buildToCorrectString(StringBuilder builder){
return builder.toString()
.replace("_", "\\_")
.replace("*", "\\*")
.replace("[", "\\[")
.replace("`", "\\`")
.replace(" ", " ")
.replace("&frac", " ")
.replaceAll(" \\u003c","");
}
}
Данный RestController обслуживает RestAPI с точкой входа http://you_ip:port/api/public/gitea/webhook , сюда наша система контроля версий Gitea будет делать PUSH запрос с JSON данными WebHook возникающего при различных событиях происходящих с вашим репозитарием.TelegramBotGiteaApplication - Стартовый метод нашего Spring Boot проекта
@SpringBootApplication
@AutoConfigurationPackage
public class TelegramBotGiteaApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
new SpringApplicationBuilder(TelegramBotGiteaApplication.class)
.run(args);
}
}
Все те классы что вы наблюдаете в пакете Model , представляют сгенерированную модель GiteaWebHook по JSON-Schema взятому из оф документации по GiteaWebHookApi , удобнее всего это делать при помощи http://www.jsonschema2pojo.org/Полный исходный код вы можете взять ЗДЕСЬНастройка Gitea для выполнения WebHook к нашему ботуДанная статья рассматривает вариант обслуживания API предоставляемое системой контроля версий Gitea но это не значит что вы не сможете сделать оповещение и без неё. Проявив некую долю усердия всё те-же WebHook можно реализовать через .git\hooks\post-update и curl , или некое API GitHub но эти реализации я доверяю вам, и здесь мы рассмотрим лишь вариант с Gitea1) И так, перейдите в репозитарий вашего проекта и войдите в его Настройки в раздел Автоматическое обновление . Нажмите на кнопку Добавить Webhook , выберете вариант Gitea2) В качестве URL обработчика укажите URL на котором у нас висит RestController http://you_ip:port/api/public/gitea/webhook3) Тип содержимого - application/json3) Секретный ключ - любой набор символов который мы в последующем внесём в наш application.properties4) На какие события этот webhook должен срабатывать? - выберите PUSH5) Галочку Активности оставьте включённой.Всё это хорошо бот написан, Gitea настроена но нашему боту нужно где-то жить. Публикация нашего бота в Google Cloud Platform, бесплатноNew customers get $300 in free credits to spend on Google Cloud. All customers get free usage of 20+ products. See offer details.Такое сообщение мы видим на главной страницы этого сервиса, а именно они дают 300$ бесплатно на год для функционирования нашего приложения у них в облаке, и нам этого вполне себе хватит.И так приступим, авторизуйтесь в Google Cloud Platform1) В боковом меню перейдите в раздел Сompute Engine, и дождитесь его инициализации2) В разделе Экземпляры ВМ, нажмите создать
Выберете конфигурацию инстанса к примеру вот такую, это будет оптимальным решением для нашего не требовательного приложения
Давайте зададим сразу правило Брандмауэра разрешающего трафик по порту 8080Зайдите Cеть VPS - Брандмауэр
Создайте правило для порта 8080 по аналогии с 80 портомПодключитесь к VM через SSH , непосредственно через браузер
Теперь мы может перейти к настройке нашей виртуальной машиныДля обновления информации об новейших версиях пакетов вводим
sudo apt update
Установим Java
sudo apt install default-jdk
Проверьте это выполнив
java - version
Если всё прошло хорошо вы должны наблюдать версию Java в консоли SSH
Теперь установим maven
sudo apt install maven
Сделаем clone репозитария с нашим ботом заготовкой
git clone https://legan.by/gitea/leganas/TelegramBotGiteaLesson.git
Сделайте необходимые манипуляции по настройке appliation.properties , укажите валидный bot_name и token
nano ./TelegramBotGiteaLesson/src/main/resources/application.properties
Зайдём в папку с нашим ботом и соберём его при помощи maven
cd TelegramBotGiteaLesson/
mvn package
После того как maven соберёт наш проект
Бот полностью готов к запуску, выполните команду
java -jar ./target/telegrambotgitea-0.0.1-SNAPSHOT.jar
Используя внешний IP указанный в web консоли управления виртуальными машинами, проверьте его работоспособность открыв в браузере страницу http://YOU_IP:8080/Если наш бот успешно подключился к Telegram мы можем написать ему проверочное сообщение в ЛС /hello , на что он должен ответить нам Привет
И так теперь у нас всё готово для того что бы добавить нашего бота в Telegram канал, дать ему права администратора (что бы он мог читать сообщения) и протестировать оповещение !Добавил бота в канал и дав ему права , вы можете написать
/chartId
Теперь это ID можно добавить в application.properties и либо пересобрать проект , либо подкинуть этот файл рядом с jar. Внесите необходимые правки IP адреса в WebHook в настройках Gitea и опробуйте вашего бота.
У кого получилось вот так , тот молодец, о том как сделать jar сервисом писать уже не буду, уж очень много про это написано на хабре, а если не найдёте пишите в личку.Надеюсь моё повествование было достаточно подробным что бы любой смог запустить свою зверушку. :)
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, JavaScript, Программирование] Углублённое руководство по JavaScript: генераторы. Часть 2, простой пример использования (перевод)
- [Системное администрирование, Сетевые технологии, DevOps] В пятницу, 29 января 2020, в 17.00 MSK состоится Online Monitoring Meetup: Нейросети в мониторинге
- [Программирование, .NET, C#] Провайдер логирования для Telegram (.NET 5 / .NET Core)
- [JavaScript, Программирование, Тестирование веб-сервисов] Тестирование с использованием Puppeteer
- [Git, Управление разработкой, DevOps] GitLab изменила тарифы платной подписки и убрала Bronze/Starter
- [Исследования и прогнозы в IT, DevOps] Контейнеры взлетели, но невысоко: результаты исследования применения контейнерных технологий в России
- [Open source, GitHub, Разработка под AR и VR, AR и VR, IT-компании] Google открыла код Tilt Brush, инструмента для рисования в ВР
- [Системное администрирование, Программирование, IT-инфраструктура, DevOps] Создание современных процессов CI/CD для бессерверных приложений с Red Hat OpenShift Pipelines и Argo CD. Часть 2 (перевод)
- [.NET, Облачные сервисы] Переход с Azure на GCP, с ASP.NET MVC на ASP.NET Core 3.1
- [Программирование, Java, Микросервисы] Spring Cloud и Spring Boot. Часть 1: использование Eureka Server (перевод)
Теги для поиска: #_java, #_git, #_devops, #_telegram, #_google_cloud_platform, #_gtea, #_java, #_java, #_git, #_devops
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:21
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Здравствуйте как и обещал в продолжение моей статьи о Автоматической публикации приложения в Google Play , рассмотрю в деталях процесс написания Telegram Bot`a для оповещения команды тестировщиков о выпуске новой версии.Регистрация Bota в Telegram и получение IDПросто напишите пользователю @BotFather и следуйте его инструкциям. Выполните последовательно следующий команды /start
/newbot bot_name Из этого сообщение нам понадобятся собственно
<parent>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.telegram</groupId> <artifactId>telegrambots</artifactId> <version>5.0.0</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> А теперь детальней по каждому классу : BotConfig - Конфигурация подтягивающая настройки бота из application.properties @Configuration
@Data @PropertySource("classpath:application.properties") public class BotConfig { // Имя бота заданное при регистрации @Value("${botUserName}") String botUserName; // Токен полученный при регистрации @Value("${token}") String token; } @Component
@Slf4j public class BotInitializer { @Autowired Bot bot; @EventListener({ContextRefreshedEvent.class}) public void Init() throws TelegramApiException { TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); try { telegramBotsApi.registerBot(bot); } catch (TelegramApiRequestException e) { log.error(exceptionStackTraceToString(e)); } } } @Component
@Slf4j /** * Касс является основным пулом взаимодействия с Telegram * получение, обработка, отправка сообщений */ public class Bot extends TelegramLongPollingBot { final BotConfig config; public Bot(BotConfig config) { this.config = config; } public void onUpdateReceived(Update update) { update.getUpdateId(); SendMessage.SendMessageBuilder builder =SendMessage.builder(); String messageText; String chatId; if (update.getMessage() != null) { chatId = update.getMessage().getChatId().toString(); builder.chatId(chatId); messageText = update.getMessage().getText(); } else { chatId = update.getChannelPost().getChatId().toString(); builder.chatId(chatId); messageText = update.getChannelPost().getText(); } if (messageText.contains("/hello")) { builder.text("Привет"); try { execute(builder.build()); } catch (TelegramApiException e) { log.debug(e.toString()); } } if (messageText.contains("/chartId")) { builder.text("ID Канала : " + chatId); try { execute(builder.build()); } catch (TelegramApiException e) { log.debug(e.toString()); } } } public String getBotUsername() { return config.getBotUserName(); } public String getBotToken() { return config.getToken(); } }
@Slf4j
@RestController @RequestMapping("/api/public/gitea") @RequiredArgsConstructor @PropertySource("classpath:application.properties") public class WebHook { Bot bot; // Канал в который будем слать уведомления @Value("${chartId}") String chartId; // Секретный ключ который придёт в нутри JSON от Gitea, // что бы левые люди не имели доступа к боту т.к. API публичное без авторизации @Value("${secret}") String secret; @Autowired public WebHook(Bot bot) { this.bot = bot; } @PostMapping(value = "/webhook") public ResponseEntity<?> webhook(@RequestBody String json){ Gson gson = new Gson(); GiteaWebHook giteaWebHook = null; try { giteaWebHook = gson.fromJson(json, GiteaWebHook.class); } catch (JsonSyntaxException e) { log.error(Utils.exceptionStackTraceToString(e)); return new ResponseEntity<>(Utils.exceptionStackTraceToString(e), HttpStatus.BAD_REQUEST); } if (validationWebHookContent(giteaWebHook)) { SendMessage.SendMessageBuilder messageBuilder =SendMessage.builder(); messageBuilder.chatId(chartId); messageBuilder.parseMode(ParseMode.HTML); StringBuilder builder = new StringBuilder(); builder.append("<b>Проект</b> : " + giteaWebHook.getRepository().getName()+"\n"); for (Commit commit : giteaWebHook.getCommits()) { builder.append("<b>Автор</b> : " + commit.getAuthor().getName()+"\n"); builder.append("<b>Комментарий</b> : " + commit.getMessage()+"\n"); } builder.append("<a href="https://play.google.com/store/apps/details?id=URL_ВАШЕГО_ПРИЛАЖЕНИЯ">Обновление будет доступно в Play Market через пару минут</a>\n"); messageBuilder.text(buildToCorrectString(builder)); try { bot.execute(messageBuilder.build()); } catch (TelegramApiException e) { log.error(Utils.exceptionStackTraceToString(e)); return new ResponseEntity<>(Utils.exceptionStackTraceToString(e), HttpStatus.INTERNAL_SERVER_ERROR); } } else return new ResponseEntity<>(HttpStatus.BAD_REQUEST); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json; charset=utf-8"); return new ResponseEntity<>(headers, HttpStatus.OK); } /** * Проверка пришедших JSON данных на валидность * @param giteaWebHook - GiteaWebHook * @return true - если не null, PUSH в master, совпал секретный ключ */ private boolean validationWebHookContent(GiteaWebHook giteaWebHook){ return giteaWebHook != null && // Если вообще что то есть giteaWebHook.getRef().contains(giteaWebHook.getRepository().getDefaultBranch()) && // Есть был PUSH в /master giteaWebHook.getSecret().equals(secret); // Если совпал секретный ключ } private String buildToCorrectString(StringBuilder builder){ return builder.toString() .replace("_", "\\_") .replace("*", "\\*") .replace("[", "\\[") .replace("`", "\\`") .replace(" ", " ") .replace("&frac", " ") .replaceAll(" \\u003c",""); } } @SpringBootApplication
@AutoConfigurationPackage public class TelegramBotGiteaApplication extends SpringBootServletInitializer { public static void main(String[] args) { new SpringApplicationBuilder(TelegramBotGiteaApplication.class) .run(args); } } Выберете конфигурацию инстанса к примеру вот такую, это будет оптимальным решением для нашего не требовательного приложения Давайте зададим сразу правило Брандмауэра разрешающего трафик по порту 8080Зайдите Cеть VPS - Брандмауэр Создайте правило для порта 8080 по аналогии с 80 портомПодключитесь к VM через SSH , непосредственно через браузер Теперь мы может перейти к настройке нашей виртуальной машиныДля обновления информации об новейших версиях пакетов вводим sudo apt update
sudo apt install default-jdk
java - version
Теперь установим maven sudo apt install maven
git clone https://legan.by/gitea/leganas/TelegramBotGiteaLesson.git
nano ./TelegramBotGiteaLesson/src/main/resources/application.properties
cd TelegramBotGiteaLesson/
mvn package Бот полностью готов к запуску, выполните команду java -jar ./target/telegrambotgitea-0.0.1-SNAPSHOT.jar
И так теперь у нас всё готово для того что бы добавить нашего бота в Telegram канал, дать ему права администратора (что бы он мог читать сообщения) и протестировать оповещение !Добавил бота в канал и дав ему права , вы можете написать /chartId
Теперь это ID можно добавить в application.properties и либо пересобрать проект , либо подкинуть этот файл рядом с jar. Внесите необходимые правки IP адреса в WebHook в настройках Gitea и опробуйте вашего бота. У кого получилось вот так , тот молодец, о том как сделать jar сервисом писать уже не буду, уж очень много про это написано на хабре, а если не найдёте пишите в личку.Надеюсь моё повествование было достаточно подробным что бы любой смог запустить свою зверушку. :) =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:21
Часовой пояс: UTC + 5