用Discord4J + Spring Boot創建一個Discord Bot

1.概述

Discord4J是一個開源Java庫,主要可用於快速訪問Discord Bot API它與Project Reactor高度集成,以提供完全無阻塞的反應式API。

在本教程中,我們將使用Discord4J創建一個能夠響應預定義命令的簡單Discord機器人。我們將在Spring Boot的基礎上構建該bot,以演示在Spring Boot啟用的許多其他功能上擴展我們的bot是多麼容易。

完成後,該機器人將能夠偵聽名為“!todo”的命令,並將打印出靜態定義的待辦事項列表。

2.創建一個Discord的應用程序

為了使我們的機器人能夠接收來自Discord的更新並在通道中發布響應,我們需要在Discord開發人員門戶中創建一個Discord應用程序並將其設置為機器人。這是一個簡單的過程。由於Discord允許在一個開發人員帳戶下創建多個應用程序或漫遊器,因此可以使用不同的設置多次嘗試。

以下是創建新應用程序的步驟:

  • 登錄到Discord開發人員門戶
  • 在“應用程序”選項卡中,單擊“新應用程序”
  • 輸入我們的機器人的名稱,然後點擊“創建”
  • 上載應用程序圖標和說明,然後單擊“保存更改”

用Discord4J

現在已經存在一個應用程序,我們只需要向它添加漫遊器功能即可。這將生成Discord4J所需的機器人令牌。

以下是將應用程序轉換為機器人程序的步驟:

  • 在“應用程序”選項卡中,選擇我們的應用程序(如果尚未選擇)。
  • 在Bot標籤中,點擊“添加Bot”,然後確認我們要這樣做。

用Discord4J

既然我們的應用程序已經成為真正的機器人,請複制令牌,以便可以將其添加到我們的應用程序屬性中。注意不要公開共享此令牌,因為其他人將能夠在模仿我們的機器人的同時執行惡意代碼。

現在我們準備編寫一些代碼!

3.創建一個Spring Boot應用

構建新的Spring Boot應用程序後,我們需要確保包括Discord4J核心依賴項:

<dependency>

 <groupId>com.discord4j</groupId>

 <artifactId>discord4j-core</artifactId>

 <version>3.1.1</version>

 </dependency>

Discord4J的工作方式是使用我們之前創建的機器人令牌GatewayDiscordClient這個客戶端對象允許我們註冊事件偵聽器並配置許多東西,但至少必須至少調用login()方法。這會將我們的漫遊器顯示為在線。

首先,讓我們將bot令牌添加到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.添加事件監聽器

聊天機器人的最常見功能是命令。這是在CLI中看到的一種抽象,其中用戶鍵入一些文本以觸發某些功能。我們可以在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 EventListener

@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;

 }

現在,註冊事件偵聽器所需要做的就是實現我們的接口,並使用@Component的構造型註釋對其進行註釋。現在,註冊將自動為我們進行!

我們可以選擇分別顯式地註冊每個事件。但是,通常最好採用模塊化的方法,以實現更好的代碼可伸縮性。

現在我們的事件偵聽器設置已完成,但該漫遊器仍未執行任何操作,因此讓我們添加一些事件以進行偵聽。

4.1 命令處理

要接收用戶的命令,我們可以偵聽兩種不同的事件類型: MessageCreateEvent用於新消息,而MessageUpdateEvent用於更新消息。我們可能只想听新消息,但是作為學習的機會,讓我們假設我們要為我們的機器人同時支持兩種事件。這將提供一層額外的健壯性,我們的用戶可能會喜歡。

這兩個事件對像都包含有關每個事件的所有相關信息。特別是,我們對消息的內容,消息的作者以及發佈到的渠道感興趣。幸運的是,所有這些數據點都存在於這兩種事件類型都提供Message

收到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()以更傳統的命令式方式編寫此功能。

跨多個bot命令擴展,調用不同的服務或數據存儲庫,甚至使用Discord角色作為對某些命令的授權,都是良好的bot命令體系結構的常見部分。由於我們的監聽器是Spring管理的@Service ,因此我們可以輕鬆地註入其他Spring管理的bean來完成這些任務。但是,在本文中我們不會解決任何問題。

4.2 EventListener<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 EventListener<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服務器並對其進行測試。

要創建邀請鏈接,我們必須指定漫遊器正常運行所需的權限。流行的第三方“不和諧權限計算器”通常用於生成具有所需權限的邀請鏈接。儘管不建議將其用於生產,但我們可以僅出於測試目的選擇“管理員”,而不必擔心其他權限。只需提供我們的機器人的客戶端ID(可在Discord Developer Portal中找到),然後使用生成的鏈接邀請我們的機器人到服務器即可

如果我們不向該機器人授予管理員權限,則可能需要調整通道權限,以便該機器人可以在通道中進行讀寫。

現在,機器人會響應消息“!todo”,並且在編輯消息時說“!todo”:

用Discord4J

6.概述

本教程描述了使用Discord4J庫和Spring Boot創建Discord bot的所有必要步驟。最後,它描述瞭如何為機器人設置基本的可伸縮命令和響應結構。

對於完整且有效的機器人,請在GitHub上查看源代碼。需要有效的機器人令牌才能運行它。