Membuat Bot Perselisihan dengan Discord4J + Spring Boot

1. Ikhtisar

Discord4J adalah pustaka Java open-source yang terutama dapat digunakan untuk mengakses Discord Bot API dengan cepat. Ini sangat terintegrasi dengan Project Reactor untuk menyediakan API reaktif yang sepenuhnya tidak memblokir.

Kami akan menggunakan Discord4J dalam tutorial ini untuk membuat bot Discord sederhana yang mampu merespons perintah yang telah ditentukan sebelumnya. Kami akan membangun bot di atas Spring Boot untuk menunjukkan betapa mudahnya menskalakan bot kami di banyak fitur lain yang diaktifkan oleh Spring Boot.

Ketika kita selesai, bot ini akan dapat mendengarkan perintah yang disebut "! Todo" dan akan mencetak daftar tugas yang didefinisikan secara statis.

2. Buat Aplikasi Discord

Agar bot kami menerima pembaruan dari Discord dan memposting tanggapan di saluran, kami perlu membuat Aplikasi Perselisihan di Portal Pengembang Perselisihan dan mengaturnya menjadi bot. Ini adalah proses yang sederhana. Karena Discord memungkinkan pembuatan beberapa aplikasi atau bot di bawah satu akun pengembang, silakan coba ini beberapa kali dengan pengaturan yang berbeda.

Berikut langkah-langkah membuat aplikasi baru:

  • Masuk ke Portal Pengembang Perselisihan
  • Di tab Aplikasi, klik "Aplikasi Baru"
  • Masukkan nama untuk bot kami dan klik "Buat"
  • Unggah Ikon Aplikasi dan deskripsi, lalu klik "Simpan Perubahan"

Sekarang aplikasi sudah ada, kita hanya perlu menambahkan fungsionalitas bot ke dalamnya. Ini akan menghasilkan token bot yang dibutuhkan Discord4J.

Berikut langkah-langkah untuk mengubah aplikasi menjadi bot:

  • Pada tab Applications, pilih aplikasi kita (jika belum dipilih).
  • Di tab Bot, klik "Add Bot" dan konfirmasikan bahwa kami ingin melakukannya.

Sekarang aplikasi kita telah menjadi bot yang sebenarnya, salin tokennya sehingga kita dapat menambahkannya ke properti aplikasi kita. Berhati-hatilah untuk tidak membagikan token ini secara publik karena orang lain dapat mengeksekusi kode berbahaya saat menyamar sebagai bot kami.

Kami sekarang siap untuk menulis beberapa kode!

3. Buat Aplikasi Spring Boot

Setelah membuat aplikasi Spring Boot baru, kita perlu memastikan untuk menyertakan dependensi inti Discord4J:

 com.discord4j discord4j-core 3.1.1 

Discord4J bekerja dengan menginisialisasi GatewayDiscordClient dengan token bot yang kita buat sebelumnya. Objek klien ini memungkinkan kita untuk mendaftarkan event listener dan mengkonfigurasi banyak hal, tetapi minimal, kita setidaknya harus memanggil metode login () . Ini akan menampilkan bot kita sedang online.

Pertama, mari tambahkan token bot kita ke file application.yml :

token: 'our-token-here'

Selanjutnya, mari kita masukkan ke dalam kelas @Configuration tempat kita dapat membuat instance GatewayDiscordClient kita :

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

Pada titik ini, bot kami akan terlihat online, tetapi belum melakukan apa pun. Mari tambahkan beberapa fungsi.

4. Tambahkan Pendengar Acara

Fitur paling umum dari chatbot adalah perintahnya. Ini adalah abstraksi yang terlihat di CLI di mana pengguna mengetik beberapa teks untuk memicu fungsi tertentu. Kami dapat mencapai ini di bot Discord kami dengan mendengarkan pesan baru yang dikirim pengguna dan membalas dengan tanggapan cerdas bila sesuai.

Ada banyak jenis acara yang bisa kita dengarkan. Namun, mendaftarkan listener sama untuk semuanya, jadi pertama-tama mari kita buat antarmuka untuk semua event listener kita:

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 dapat mengimplementasikan antarmuka ini untuk ekstensi discord4j.core.event.domain.Event sebanyak yang kita inginkan.

Sebelum kita mengimplementasikan event listener pertama kita, mari kita ubah konfigurasi @Bean klien kita untuk mengharapkan daftar EventListener sehingga bisa mendaftarkan semua yang ditemukan 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 event listener adalah mengimplementasikan antarmuka kita dan menganotasinya dengan anotasi stereotip berbasis @Component Spring . Pendaftaran sekarang akan terjadi secara otomatis untuk kami!

Kami dapat memilih untuk mendaftarkan setiap acara secara terpisah dan eksplisit. Namun, secara umum lebih baik untuk mengambil pendekatan yang lebih modular untuk skalabilitas kode yang lebih baik.

Penyiapan pendengar acara kami sekarang selesai, tetapi bot masih belum melakukan apa pun, jadi mari tambahkan beberapa acara untuk didengarkan.

4.1. Pemrosesan Perintah

Untuk menerima perintah pengguna, kita dapat mendengarkan dua jenis peristiwa yang berbeda: MessageCreateEvent untuk pesan baru dan MessageUpdateEvent untuk pesan yang diperbarui. Kami mungkin hanya ingin mendengarkan pesan baru, tetapi sebagai kesempatan belajar, anggap saja kami ingin mendukung kedua jenis acara untuk bot kami. Ini akan memberikan lapisan kekuatan ekstra yang mungkin dihargai oleh pengguna kami.

Kedua objek acara berisi semua informasi yang relevan tentang setiap acara. Secara khusus, kami tertarik dengan konten pesan, penulis pesan, dan saluran tempat pesan tersebut diposting. Untungnya, semua titik data ini berada di objek Message yang disediakan oleh kedua jenis peristiwa 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 menjelaskan semua langkah yang diperlukan untuk membuat bot Discord menggunakan pustaka Discord4J dan Spring Boot. Terakhir, dijelaskan cara menyiapkan perintah skalabel dasar dan struktur respons untuk bot.

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