1. Обзор
Discord4J — это библиотека Java с открытым исходным кодом, которую в первую очередь можно использовать для быстрого доступа к Discord Bot API . Он тесно интегрируется с Project Reactor , чтобы предоставить полностью неблокирующий реактивный API.
В этом руководстве мы будем использовать Discord4J для создания простого бота Discord, способного реагировать на предопределенную команду. Мы создадим бота поверх Spring Boot, чтобы продемонстрировать, насколько легко будет масштабировать нашего бота для многих других функций, доступных в Spring Boot.
Когда мы закончим, этот бот сможет прослушать команду под названием «! todo» и распечатать статически определенный список дел.
2. Создайте приложение Discord
Чтобы наш бот мог получать обновления от Discord и публиковать ответы в каналах, нам нужно создать приложение Discord на портале разработчиков Discord и настроить его как бота. Это простой процесс. Поскольку Discord позволяет создавать несколько приложений или ботов под одной учетной записью разработчика, не стесняйтесь пробовать это несколько раз с разными настройками.
Вот шаги для создания нового приложения:
- Войдите на портал разработчиков Discord
- Во вкладке «Приложения» нажмите «Новое приложение».
- Введите имя для нашего бота и нажмите «Создать»
- Загрузите значок приложения и описание и нажмите «Сохранить изменения».
Теперь, когда приложение существует, нам просто нужно добавить к нему функциональность бота. Это сгенерирует токен бота, который требуется Discord4J.
Вот шаги для преобразования приложения в бота:
- Во вкладке Applications выбираем наше приложение (если оно еще не выбрано).
- Во вкладке Bot нажимаем «Добавить бота» и подтверждаем, что хотим это сделать.
Теперь, когда наше приложение стало настоящим ботом, скопируйте токен, чтобы мы могли добавить его в свойства нашего приложения. Будьте осторожны, не делитесь этим токеном публично, так как кто-то другой сможет выполнить вредоносный код, выдавая себя за нашего бота.
Теперь мы готовы написать код!
3. Создайте приложение Spring Boot
После создания нового приложения Spring Boot мы должны обязательно включить основную зависимость Discord4J :
<dependency>
<groupId>com.discord4j</groupId>
<artifactId>discord4j-core</artifactId>
<version>3.1.1</version>
</dependency>
Discord4J работает, инициализируя GatewayDiscordClient
с токеном бота, который мы создали ранее. Этот клиентский объект позволяет нам регистрировать прослушиватели событий и настраивать многие вещи, но, как минимум, мы должны вызвать метод login()
. Это покажет, что наш бот находится в сети.
Во-первых, давайте добавим наш токен бота в наш файл application.yml :
token: 'our-token-here'
Затем давайте внедрим его в класс @Configuration
, где мы можем создать экземпляр нашего GatewayDiscordClient
:
@Configuration
public class BotConfiguration {
@Value("${token}")
private String token;
@Bean
public GatewayDiscordClient gatewayDiscordClient() {
return DiscordClientBuilder.create(token)
.build()
.login()
.block();
}
}
В этот момент наш бот будет виден как онлайн, но он пока ничего не делает. Добавим немного функциональности.
4. Добавьте слушателей событий
Наиболее распространенной функцией чат-бота является команда. Это абстракция, встречающаяся в интерфейсах командной строки , где пользователь вводит некоторый текст для запуска определенных функций. Мы можем добиться этого в нашем боте Discord, прослушивая новые сообщения, которые отправляют пользователи, и отвечая умными ответами, когда это необходимо.
Есть много типов событий, которые мы можем прослушивать. Однако регистрация прослушивателя одинакова для всех них, поэтому давайте сначала создадим интерфейс для всех наших прослушивателей событий:
import discord4j.core.event.domain.Event;
public interface EventListener<T extends Event> {
Logger LOG = LoggerFactory.getLogger(EventListener.class);
Class<T> getEventType();
Mono<Void> execute(T event);
default Mono<Void> handleError(Throwable error) {
LOG.error("Unable to process " + getEventType().getSimpleName(), error);
return Mono.empty();
}
}
Теперь мы можем реализовать этот интерфейс для любого количества расширений discord4j.core.event.domain.Event .
Прежде чем мы реализуем наш первый прослушиватель событий, давайте изменим конфигурацию нашего клиента @Bean
, чтобы ожидать список EventListener
, чтобы он мог зарегистрировать каждый, найденный в Spring ApplicationContext
:
@Bean
public <T extends Event> GatewayDiscordClient gatewayDiscordClient(List<EventListener<T>> eventListeners) {
GatewayDiscordClient client = DiscordClientBuilder.create(token)
.build()
.login()
.block();
for(EventListener<T> listener : eventListeners) {
client.on(listener.getEventType())
.flatMap(listener::execute)
.onErrorResume(listener::handleError)
.subscribe();
}
return client;
}
Теперь все, что нам нужно сделать, чтобы зарегистрировать прослушиватели событий, — это реализовать наш интерфейс и аннотировать его с помощью аннотаций стереотипов Spring на основе @Component
. Регистрация теперь будет происходить автоматически для нас!
Мы могли бы зарегистрировать каждое событие отдельно и явно. Однако, как правило, лучше использовать более модульный подход для лучшей масштабируемости кода.
Настройка прослушивателя событий завершена, но бот еще ничего не делает, поэтому давайте добавим несколько событий для прослушивания.
4.1. Обработка команд
Чтобы получить команду пользователя, мы можем прослушивать два разных типа событий: MessageCreateEvent
для новых сообщений и MessageUpdateEvent
для обновленных сообщений. Мы можем хотеть только прослушивать новые сообщения, но в качестве возможности обучения предположим, что мы хотим поддерживать оба типа событий для нашего бота. Это обеспечит дополнительный уровень надежности, который могут оценить наши пользователи.
Оба объекта событий содержат всю необходимую информацию о каждом событии. В частности, нас интересует содержание сообщения, автор сообщения и канал, на котором оно было опубликовано. К счастью, все эти точки данных находятся в объекте Message
, который предоставляют оба этих типа событий.
Получив сообщение
, мы можем проверить автора, чтобы убедиться, что это не бот, мы можем проверить содержимое сообщения, чтобы убедиться, что оно соответствует нашей команде, и мы можем использовать канал сообщения для отправки ответа.
Поскольку мы можем полностью работать с обоими событиями через их объекты Message
, давайте поместим всю нижестоящую логику в общее место, чтобы оба прослушивателя событий могли ее использовать:
import discord4j.core.object.entity.Message;
public abstract class MessageListener {
public Mono<Void> processCommand(Message eventMessage) {
return Mono.just(eventMessage)
.filter(message -> message.getAuthor().map(user -> !user.isBot()).orElse(false))
.filter(message -> message.getContent().equalsIgnoreCase("!todo"))
.flatMap(Message::getChannel)
.flatMap(channel -> channel.createMessage("Things to do today:\n - write a bot\n - eat lunch\n - play a game"))
.then();
}
}
Здесь происходит многое, но это самая основная форма команды и ответа. Этот подход использует реактивный функциональный дизайн , но его можно написать более традиционным императивным способом, используя block()
.
Масштабирование нескольких команд бота, вызов различных служб или репозиториев данных или даже использование ролей Discord в качестве авторизации для определенных команд — общие элементы хорошей архитектуры команд бота. Поскольку наши слушатели — это @Service
, управляемые Spring , мы могли бы легко внедрить другие bean-компоненты, управляемые Spring, чтобы позаботиться об этих задачах. Тем не менее, мы не будем касаться ни того, ни другого в этой статье.
4.2. Прослушиватель событий<MessageCreateEvent>
Чтобы получать новые сообщения от пользователя, мы должны прослушивать MessageCreateEvent
. Поскольку логика обработки команд уже находится в MessageListener
, мы можем расширить ее, чтобы наследовать эту функциональность. Кроме того, нам нужно реализовать наш интерфейс EventListener
, чтобы он соответствовал нашему дизайну регистрации:
@Service
public class MessageCreateListener extends MessageListener implements EventListener<MessageCreateEvent> {
@Override
public Class<MessageCreateEvent> getEventType() {
return MessageCreateEvent.class;
}
@Override
public Mono<Void> execute(MessageCreateEvent event) {
return processCommand(event.getMessage());
}
}
По наследству сообщение передается нашему методу processCommand()
, где происходят все проверки и ответы.
В этот момент наш бот получит и ответит на команду «!todo». Однако, если пользователь исправит свою опечатку, бот не ответит. Давайте поддержим этот вариант использования другим прослушивателем событий.
4.3. Прослушиватель событий<MessageUpdateEvent>
MessageUpdateEvent генерируется
, когда пользователь редактирует сообщение. Мы можем прослушивать это событие, чтобы распознавать команды, подобно тому, как мы прослушиваем MessageCreateEvent
.
Для наших целей это событие важно только в том случае, если содержимое сообщения было изменено. Мы можем игнорировать другие экземпляры этого события. К счастью, мы можем использовать метод isContentChanged()
для фильтрации таких экземпляров:
@Service
public class MessageUpdateListener extends MessageListener implements EventListener<MessageUpdateEvent> {
@Override
public Class<MessageUpdateEvent> getEventType() {
return MessageUpdateEvent.class;
}
@Override
public Mono<Void> execute(MessageUpdateEvent event) {
return Mono.just(event)
.filter(MessageUpdateEvent::isContentChanged)
.flatMap(MessageUpdateEvent::getMessage)
.flatMap(super::processCommand);
}
}
В этом случае, поскольку getMessage()
возвращает Mono<Message>
вместо необработанного Message
, нам нужно использовать flatMap()
, чтобы отправить его в наш суперкласс.
5. Тестовый бот в Discord
Теперь, когда у нас есть работающий бот Discord, мы можем пригласить его на сервер Discord и протестировать.
Чтобы создать ссылку-приглашение, мы должны указать, какие разрешения требуются боту для правильной работы. Популярный сторонний калькулятор разрешений Discord часто используется для создания ссылки-приглашения с необходимыми разрешениями. Хотя это не рекомендуется для производства, мы можем просто выбрать «Администратор» для целей тестирования и не беспокоиться о других разрешениях. Просто укажите идентификатор клиента для нашего бота (находится на портале разработчиков Discord) и используйте сгенерированную ссылку, чтобы пригласить нашего бота на сервер .
Если мы не предоставим боту разрешения администратора, нам может потребоваться настроить разрешения канала, чтобы бот мог читать и писать в канале.
Теперь бот отвечает на сообщение «!todo» и когда сообщение редактируется, чтобы сказать «!todo»:
6. Обзор
В этом руководстве описаны все необходимые шаги для создания бота Discord с использованием библиотеки Discord4J и Spring Boot. Наконец, в нем описано, как настроить базовую масштабируемую структуру команд и ответов для бота.
Для полного и работающего бота просмотрите исходный код на GitHub . Для его запуска требуется действительный токен бота.