1. 概述
Discord4J 是一個開源的 Java 庫,主要用於快速訪問 Discord Bot API。它深度集成了 Project Reactor,以提供完全非阻塞的響應式 API。
在本教程中,我們將使用 Discord4J 創建一個簡單的 Discord 機器人,該機器人能夠響應預定義的命令。我們將使用 Spring Boot 構建該機器人,以演示 Spring Boot 如何輕鬆地擴展該機器人的功能。
完成之後,該機器人將能夠監聽一個名為“!todo”的命令,並打印出靜態定義的待辦事項列表。
2. 創建一個 Discord 應用
為了讓我們的機器人從 Discord 接收更新並向頻道發佈響應,我們需要在 Discord 開發人員門户中創建一個 Discord 應用並將其設置為機器人。這是一個簡單的過程。由於 Discord 允許在單個開發者帳户下創建多個應用程序或機器人,因此請隨時多次嘗試,使用不同的設置。
以下是創建新應用程序的步驟:
- 登錄到 Discord 開發人員門户
- 在“應用”選項卡中,單擊“新建應用”
- 為我們的機器人輸入名稱並單擊“創建”
- 上傳 App 圖標和描述,然後單擊“保存更改”
現在一個應用已經存在,我們只需要將其轉換為機器人。這將生成 Discord4J 所需的機器人令牌。
以下是將應用程序轉換為機器人的步驟:
- 在“應用”選項卡中,選擇我們的應用程序(如果它尚未選擇)。
- 在“機器人”選項卡中,單擊“添加機器人”並確認我們想要這樣做。
現在我們的應用程序已經成為真正的機器人,請複製令牌,以便將其添加到應用程序屬性中。 請勿在公共場合分享此令牌,因為其他人將能夠執行惡意代碼,冒充我們的機器人。
現在我們準備好編寫代碼了!
3. 創建 Spring Boot 應用
在構建新的 Spring Boot 應用後,我們需要確保包含 Discord4J 核心 依賴項:
<dependency>
<groupId>com.discord4j</groupId>
<artifactId>discord4j-core</artifactId>
<version>3.3.0-RC1</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. 添加事件監聽器
最常見的聊天機器人功能是命令。這在 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 中的所有對象。
@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. 命令處理
為了接收用户的命令,我們可以監聽兩種不同的事件類型:<a href="https://javadoc.io/doc/com.discord4j/discord4j-core/3.1.1/discord4j/core/event/domain/message/MessageCreateEvent.html"><em >MessageCreateEvent</em></em > 用於新消息,以及 <a href="https://javadoc.io/doc/com.discord4j/discord4j-core/3.1.1/discord4j/core/event/domain/message/MessageUpdateEvent.html"><em >MessageUpdateEvent</em></em > 用於更新的消息。我們可能只想要監聽新消息,但為了學習目的,讓我們假設我們要支持這兩種事件類型,以便於我們的機器人。這將提供額外的魯棒性,用户可能會欣賞。
這兩個事件對象都包含有關每個事件的所有相關信息。特別是,我們對消息內容、消息的作者和消息發佈的頻道感興趣。 幸運的是,所有這些數據點都存在於這兩個事件類型提供的 <em >Message</em> 對象中。
一旦我們有了 <em >Message</em> 對象,我們就可以檢查作者以確保它不是機器人,可以檢查消息內容以確保它與我們的命令匹配,並且可以使用消息的頻道發送響應。
由於我們可以通過它們的 <em >Message</em> 對象完全操作這兩個事件,所以我們應該將所有下游邏輯放在一個共同的位置,以便這兩個事件監聽器都可以使用它:
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 角色作為某些命令的授權方面,構建良好的機器人命令架構是很常見的。由於我們的監聽器是 Spring 管理的 @Service,我們可以輕鬆地注入其他 Spring 管理的 Bean 來處理這些任務。但是,本文將不涉及這些內容。
4.2. `EventListener<MessageCreateEvent>
為了接收用户發的新消息,我們需要監聽 <em >MessageCreateEvent</em ></h3 >。由於命令處理邏輯已經存在於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 服務器並進行測試。
要創建邀請鏈接,必須指定機器人所需的權限,以便其正常運行。 一個流行的第三方 Discord 權限計算器 通常用於生成帶有所需權限的邀請鏈接。 雖然不建議用於生產環境,但為了測試目的,我們可以簡單地選擇“管理員”權限,不必擔心其他權限。只需提供我們的機器人 Client ID(在 Discord 開發人員門户中找到)並使用生成的鏈接邀請我們的機器人到 一個服務器。
如果未授予機器人管理員權限,則可能需要調整頻道權限,以便機器人可以在頻道中讀取和寫入。
機器人現在響應“!todo”消息,並且當消息被編輯為“!todo”時:
6. 結論
本教程詳細介紹了使用 Discord4J 庫和 Spring Boot 創建 Discord 機器人所需的所有步驟。 此外,它還描述瞭如何設置基本可擴展的命令和響應結構,以供機器人使用。
一個有效的機器人令牌對於運行機器人是必需的。