Creazione di un Discord Bot con Discord4J + Spring Boot

1. Panoramica

Discord4J è una libreria Java open source che può essere utilizzata principalmente per accedere rapidamente all'API Discord Bot. Si integra fortemente con Project Reactor per fornire un'API reattiva completamente non bloccante.

Useremo Discord4J in questo tutorial per creare un semplice Discord bot in grado di rispondere a un comando predefinito. Costruiremo il bot sopra Spring Boot per dimostrare quanto sarebbe facile scalare il nostro bot su molte altre funzionalità abilitate da Spring Boot.

Quando avremo finito, questo bot sarà in grado di ascoltare un comando chiamato "! Todo" e stamperà un elenco di cose da fare definito staticamente.

2. Crea un'applicazione Discord

Affinché il nostro bot riceva aggiornamenti da Discord e pubblichi risposte nei canali, dobbiamo creare un'applicazione Discord nel Portale per sviluppatori Discord e configurarla come bot. Questo è un processo semplice. Poiché Discord consente la creazione di più applicazioni o bot con un unico account sviluppatore, sentiti libero di provarlo più volte con impostazioni diverse.

Ecco i passaggi per creare una nuova applicazione:

  • Accedi al portale per sviluppatori Discord
  • Nella scheda Applicazioni, fai clic su "Nuova applicazione"
  • Inserisci un nome per il nostro bot e fai clic su "Crea"
  • Carica un'icona dell'app e una descrizione e fai clic su "Salva modifiche"

Ora che esiste un'applicazione, dobbiamo semplicemente aggiungervi la funzionalità del bot. Questo genererà il token del bot richiesto da Discord4J.

Ecco i passaggi per trasformare un'applicazione in un bot:

  • Nella scheda Applicazioni, seleziona la nostra applicazione (se non è già selezionata).
  • Nella scheda Bot, fare clic su "Aggiungi Bot" e confermare che si desidera farlo.

Ora che la nostra applicazione è diventata un vero bot, copia il token in modo che possiamo aggiungerlo alle proprietà dell'applicazione. Fai attenzione a non condividere questo token pubblicamente poiché qualcun altro potrebbe eseguire codice dannoso mentre impersona il nostro bot.

Ora siamo pronti per scrivere del codice!

3. Creare un'app Spring Boot

Dopo aver costruito una nuova app Spring Boot, dobbiamo essere sicuri di includere la dipendenza principale di Discord4J:

 com.discord4j discord4j-core 3.1.1 

Discord4J funziona inizializzando un GatewayDiscordClient con il token del bot che abbiamo creato in precedenza. Questo oggetto client ci permette di registrare ascoltatori di eventi e configurare molte cose, ma come minimo, dobbiamo almeno chiamare il metodo login () . Questo mostrerà il nostro bot come online.

Per prima cosa, aggiungiamo il nostro token bot al nostro file application.yml :

token: 'our-token-here'

Successivamente, inseriamolo in una classe @Configuration dove possiamo istanziare il nostro GatewayDiscordClient :

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

A questo punto, il nostro bot sarebbe visto come online, ma non fa ancora nulla. Aggiungiamo alcune funzionalità.

4. Aggiungi listener di eventi

La caratteristica più comune di un chatbot è il comando. Questa è un'astrazione vista nelle CLI in cui un utente digita del testo per attivare determinate funzioni. Possiamo ottenere questo risultato nel nostro Discord bot ascoltando i nuovi messaggi che gli utenti inviano e rispondendo con risposte intelligenti quando appropriato.

Ci sono molti tipi di eventi per i quali possiamo ascoltare. Tuttavia, la registrazione di un listener è la stessa per tutti loro, quindi creiamo prima un'interfaccia per tutti i nostri listener di eventi:

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(); } }

Ora possiamo implementare questa interfaccia per tutte le estensioni discord4j.core.event.domain.Event che vogliamo.

Prima di implementare il nostro primo listener di eventi, modifichiamo la configurazione del nostro client @Bean per aspettarci un elenco di EventListener in modo che possa registrare tutti quelli trovati in 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; }
    

Ora, tutto ciò che dobbiamo fare per registrare i listener di eventi è implementare la nostra interfaccia e annotarla con le annotazioni stereotipiche basate su @Component di Spring . La registrazione ora avverrà automaticamente per noi!

Avremmo potuto scegliere di registrare ogni evento separatamente ed esplicitamente. Tuttavia, è generalmente meglio adottare un approccio più modulare per una migliore scalabilità del codice.

La nostra configurazione del listener di eventi è ora completa, ma il bot ancora non fa nulla, quindi aggiungiamo alcuni eventi da ascoltare.

4.1. Elaborazione dei comandi

Per ricevere il comando di un utente, possiamo ascoltare due diversi tipi di eventi: MessageCreateEvent per i nuovi messaggi e MessageUpdateEvent per i messaggi aggiornati. Potremmo voler solo ascoltare nuovi messaggi, ma come opportunità di apprendimento, supponiamo di voler supportare entrambi i tipi di eventi per il nostro bot. Ciò fornirà un ulteriore livello di robustezza che i nostri utenti potrebbero apprezzare.

Entrambi gli oggetti evento contengono tutte le informazioni rilevanti su ciascun evento. In particolare, siamo interessati al contenuto del messaggio, all'autore del messaggio e al canale in cui è stato pubblicato. Fortunatamente, tutti questi punti dati risiedono nell'oggetto Message fornito da entrambi questi tipi di evento.

Una volta che abbiamo il messaggio , possiamo controllare l'autore per assicurarci che non sia un bot, possiamo controllare il contenuto del messaggio per assicurarci che corrisponda al nostro comando e possiamo usare il canale del messaggio per inviare una risposta.

Poiché possiamo operare completamente da entrambi gli eventi tramite i loro oggetti Message , inseriamo tutta la logica a valle in una posizione comune in modo che entrambi i listener di eventi possano usarla:

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(); } }

Qui stanno succedendo molte cose, ma questa è la forma più semplice di comando e risposta. Questo approccio utilizza un design funzionale reattivo, ma è possibile scriverlo in un modo imperativo più tradizionale utilizzando 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

Questo tutorial ha descritto tutti i passaggi necessari per creare un bot Discord utilizzando la libreria Discord4J e Spring Boot. Infine, ha descritto come impostare un comando scalabile di base e una struttura di risposta per il bot.

Per un bot completo e funzionante, visualizza il codice sorgente su GitHub. Per eseguirlo è necessario un token bot valido.