Membuat Bot Discord dengan Discord4J + Spring Boot

1. Gambaran keseluruhan

Discord4J adalah perpustakaan Java sumber terbuka yang terutama dapat digunakan untuk mengakses Discord Bot API dengan cepat. Ia sangat berintegrasi dengan Project Reactor untuk menyediakan API reaktif yang sepenuhnya tidak menyekat.

Kami akan menggunakan Discord4J dalam tutorial ini untuk membuat bot Discord ringkas yang mampu bertindak balas terhadap perintah yang telah ditentukan. Kami akan membina bot di atas Spring Boot untuk menunjukkan betapa mudahnya skala bot kami melintasi banyak ciri lain yang diaktifkan oleh Spring Boot.

Setelah selesai, bot ini akan dapat mendengar perintah yang disebut "! Todo" dan akan mencetak senarai tugas yang mesti ditentukan secara statik.

2. Buat Aplikasi Discord

Agar bot kami menerima kemas kini dari Discord dan menghantar respons di saluran, kami perlu membuat Aplikasi Discord di Discord Developer Portal dan menyiapkannya untuk menjadi bot. Ini adalah proses yang mudah. Oleh kerana Discord membenarkan pembuatan beberapa aplikasi atau bot di bawah satu akaun pembangun, jangan ragu untuk mencuba ini dengan pelbagai tetapan.

Berikut adalah langkah-langkah untuk membuat aplikasi baru:

  • Log masuk ke Discord Developer Portal
  • Di tab Aplikasi, klik "Aplikasi Baru"
  • Masukkan nama bot kami dan klik "Buat"
  • Muat naik Ikon Aplikasi dan keterangan dan klik "Simpan Perubahan"

Sekarang aplikasi sudah ada, kita hanya perlu menambahkan fungsi bot padanya. Ini akan menghasilkan token bot yang diperlukan oleh Discord4J.

Berikut adalah langkah-langkah untuk mengubah aplikasi menjadi bot:

  • Di tab Aplikasi, pilih aplikasi kami (jika belum dipilih).
  • Di tab Bot, klik "Tambah Bot" dan sahkan bahawa kami ingin melakukannya.

Sekarang aplikasi kami telah menjadi bot nyata, salin token tersebut sehingga kami dapat menambahkannya ke sifat aplikasi kami. Berhati-hatilah untuk tidak membagikan token ini secara terbuka kerana orang lain dapat melaksanakan kod jahat semasa menyamar sebagai bot kami.

Kami kini bersedia untuk menulis beberapa kod!

3. Buat Aplikasi Spring Boot

Setelah membina aplikasi Spring Boot baru, kita perlu memastikan pergantungan teras Discord4J:

 com.discord4j discord4j-core 3.1.1 

Discord4J berfungsi dengan memulakan GatewayDiscordClient dengan token bot yang kami buat sebelumnya. Objek pelanggan ini membolehkan kita mendaftarkan pendengar acara dan mengkonfigurasi banyak hal, tetapi minimum, kita sekurang-kurangnya mesti memanggil kaedah log masuk () . Ini akan memperlihatkan bot kami sebagai dalam talian.

Pertama, mari tambah token bot kami ke fail application.yml kami :

token: 'our-token-here'

Seterusnya, mari kita memasukkannya ke kelas @Configuration di mana kita dapat mewujudkan GatewayDiscordClient kami :

@Configuration public class BotConfiguration { @Value("${token}") private String token; @Bean public GatewayDiscordClient gatewayDiscordClient() { return DiscordClientBuilder.create(token) .build() .login() .block(); } }

Pada ketika ini, bot kami akan dilihat sebagai dalam talian, tetapi belum melakukan apa-apa. Mari tambahkan beberapa fungsi.

4. Tambahkan Pendengar Acara

Ciri yang paling umum dari chatbot adalah perintah. Ini adalah abstraksi yang dilihat di CLI di mana pengguna menaip beberapa teks untuk mencetuskan fungsi tertentu. Kami dapat mencapainya di bot Discord kami dengan mendengarkan mesej baru yang dihantar dan dibalas oleh pengguna dengan respons pintar apabila sesuai.

Terdapat banyak jenis acara yang boleh kita dengarkan. Walau bagaimanapun, mendaftarkan pendengar adalah sama untuk mereka semua, jadi mari kita buat pertama kali antara muka untuk semua pendengar acara kami:

import discord4j.core.event.domain.Event; public interface EventListener { Logger LOG = LoggerFactory.getLogger(EventListener.class); Class getEventType(); Mono execute(T event); default Mono handleError(Throwable error) { LOG.error("Unable to process " + getEventType().getSimpleName(), error); return Mono.empty(); } }

Sekarang kita boleh melaksanakan antara muka ini untuk seberapa banyak sambungan discord4j.core.event.domain. Acara yang kita mahukan.

Sebelum kita melaksanakan pendengar acara pertama kita, mari ubah suai konfigurasi pelanggan kami @Bean untuk mengharapkan senarai EventListener supaya dapat mendaftarkan setiap yang terdapat di Spring ApplicationContext :

@Bean public  GatewayDiscordClient gatewayDiscordClient(List
    
      eventListeners) { GatewayDiscordClient client = DiscordClientBuilder.create(token) .build() .login() .block(); for(EventListener listener : eventListeners) { client.on(listener.getEventType()) .flatMap(listener::execute) .onErrorResume(listener::handleError) .subscribe(); } return client; }
    

Sekarang, yang harus kita lakukan untuk mendaftarkan pendengar acara adalah dengan melaksanakan antara muka kita dan memberi anotasi dengan anotasi stereotaip berasaskan Spring @Component . Pendaftaran sekarang akan berlaku secara automatik untuk kami!

Kita boleh memilih untuk mendaftarkan setiap acara secara berasingan dan eksplisit. Walau bagaimanapun, umumnya lebih baik mengambil pendekatan yang lebih modular untuk skalabiliti kod yang lebih baik.

Persiapan pendengar acara kami kini selesai, tetapi bot masih belum melakukan apa-apa, jadi mari tambahkan beberapa acara untuk didengarkan.

4.1. Pemprosesan Perintah

Untuk menerima arahan pengguna, kita dapat mendengarkan dua jenis peristiwa yang berbeza: MessageCreateEvent untuk mesej baru dan MessageUpdateEvent untuk mesej yang dikemas kini. Kami mungkin hanya ingin mendengar pesan baru, tetapi sebagai peluang belajar, anggaplah kami ingin menyokong kedua-dua jenis acara untuk bot kami. Ini akan memberikan lapisan ketahanan tambahan yang mungkin dihargai oleh pengguna kami.

Kedua-dua objek acara mengandungi semua maklumat yang relevan mengenai setiap peristiwa. Khususnya, kami berminat dengan kandungan mesej, pengarang mesej, dan saluran yang disiarkan. Nasib baik, semua titik data ini tinggal di objek Mesej yang disediakan oleh kedua-dua jenis acara ini.

Once we have the Message, we can check the author to make sure it is not a bot, we can check the message contents to make sure it matches our command, and we can use the message's channel to send a response.

Since we can fully operate from both events through their Message objects, let's put all downstream logic into a common location so that both event listeners can use it:

import discord4j.core.object.entity.Message; public abstract class MessageListener { public Mono 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(); } }

A lot is going on here, but this is the most basic form of a command and response. This approach uses a reactive functional design, but it is possible to write this in a more traditional imperative way using block().

Scaling across multiple bot commands, invoking different services or data repositories, or even using Discord roles as authorization for certain commands are common parts of a good bot command architecture. Since our listeners are Spring-managed @Services, we could easily inject other Spring-managed beans to take care of those tasks. However, we won't tackle any of that in this article.

4.2. EventListener

To receive new messages from a user, we must listen to the MessageCreateEvent. Since the command processing logic already lives in MessageListener, we can extend it to inherit that functionality. Also, we need to implement our EventListener interface to comply with our registration design:

@Service public class MessageCreateListener extends MessageListener implements EventListener { @Override public Class getEventType() { return MessageCreateEvent.class; } @Override public Mono execute(MessageCreateEvent event) { return processCommand(event.getMessage()); } }

Through inheritance, the message is passed off to our processCommand() method where all verification and responses occur.

At this point, our bot will receive and respond to the “!todo” command. However, if a user corrects their mistyped command, the bot would not respond. Let's support this use case with another event listener.

4.3. EventListener

The MessageUpdateEvent is emitted when a user edits a message. We can listen for this event to recognize commands, much like how we listen for the MessageCreateEvent.

For our purposes, we only care about this event if the message contents were changed. We can ignore other instances of this event. Fortunately, we can use the isContentChanged() method to filter out such instances:

@Service public class MessageUpdateListener extends MessageListener implements EventListener { @Override public Class getEventType() { return MessageUpdateEvent.class; } @Override public Mono execute(MessageUpdateEvent event) { return Mono.just(event) .filter(MessageUpdateEvent::isContentChanged) .flatMap(MessageUpdateEvent::getMessage) .flatMap(super::processCommand); } }

In this case, since getMessage() returns Mono instead of a raw Message, we need to use flatMap() to send it to our superclass.

5. Test Bot in Discord

Now that we have a functioning Discord bot, we can invite it to a Discord server and test it.

To create an invite link, we must specify which permissions the bot requires to function properly. A popular third-party Discord Permissions Calculator is often used to generate an invite link with the needed permissions. Although it's not recommended for production, we can simply choose “Administrator” for testing purposes and not worry about the other permissions. Simply supply the Client ID for our bot (found in the Discord Developer Portal) and use the generated link to invite our bot to a server.

If we do not grant Administrator permissions to the bot, we might need to tweak channel permissions so that the bot can read and write in a channel.

The bot now responds to the message “!todo” and when a message is edited to say “!todo”:

6. Overview

Tutorial ini menerangkan semua langkah yang diperlukan untuk membuat bot Discord menggunakan perpustakaan Discord4J dan Spring Boot. Akhirnya, ia menerangkan cara menyiapkan struktur arahan dan respons asas untuk skala bot.

Untuk bot yang lengkap dan berfungsi, lihat kod sumber di GitHub. Token bot yang sah diperlukan untuk menjalankannya.