Een Discord Bot maken met Discord4J + Spring Boot

1. Overzicht

Discord4J is een open-source Java-bibliotheek die voornamelijk kan worden gebruikt om snel toegang te krijgen tot de Discord Bot API. Het integreert sterk met Project Reactor om een ​​volledig niet-blokkerende reactieve API te bieden.

We gebruiken Discord4J in deze tutorial om een ​​eenvoudige Discord-bot te maken die kan reageren op een vooraf gedefinieerd commando. We zullen de bot bovenop Spring Boot bouwen om te demonstreren hoe gemakkelijk het zou zijn om onze bot te schalen over vele andere functies die door Spring Boot mogelijk worden gemaakt.

Als we klaar zijn, kan deze bot luisteren naar een commando genaamd "! Todo" en een statisch gedefinieerde takenlijst afdrukken.

2. Maak een Discord-applicatie

Om ervoor te zorgen dat onze bot updates van Discord ontvangt en reacties in kanalen plaatst, moeten we een Discord-applicatie maken in de Discord Developer Portal en deze instellen als een bot. Dit is een eenvoudig proces. Omdat Discord de creatie van meerdere applicaties of bots onder één ontwikkelaarsaccount toestaat, kun je dit gerust meerdere keren proberen met verschillende instellingen.

Hier zijn de stappen om een ​​nieuwe applicatie te maken:

  • Log in op de Discord Developer Portal
  • Klik op het tabblad Toepassingen op 'Nieuwe toepassing'
  • Voer een naam in voor onze bot en klik op "Maken"
  • Upload een app-pictogram en een beschrijving en klik op "Wijzigingen opslaan"

Nu een applicatie bestaat, hoeven we er alleen maar botfunctionaliteit aan toe te voegen. Dit genereert het bot-token dat Discord4J nodig heeft.

Hier zijn de stappen om een ​​applicatie in een bot te veranderen:

  • Selecteer op het tabblad Toepassingen onze toepassing (als deze nog niet is geselecteerd).
  • Klik op het tabblad Bot op "Bot toevoegen" en bevestig dat we het willen doen.

Nu onze applicatie een echte bot is geworden, kopieert u het token zodat we het kunnen toevoegen aan onze applicatie-eigenschappen. Pas op dat u dit token niet openbaar deelt, aangezien iemand anders schadelijke code kan uitvoeren terwijl hij zich voordoet als onze bot.

We zijn nu klaar om wat code te schrijven!

3. Maak een Spring Boot-app

Na het bouwen van een nieuwe Spring Boot-app, moeten we er zeker van zijn dat we de Discord4J-kernafhankelijkheid opnemen:

 com.discord4j discord4j-core 3.1.1 

Discord4J werkt door het initialiseren van een GatewayDiscordClient met het bot-token dat we eerder hebben gemaakt. Dit client-object stelt ons in staat om gebeurtenislisteners te registreren en veel dingen te configureren, maar we moeten op zijn minst de Log in() methode. Hierdoor wordt onze bot weergegeven als zijnde online.

Laten we eerst ons bot-token toevoegen aan ons application.yml het dossier:

token: 'ons-token-hier'

Laten we het vervolgens in een @Configuratie klasse waar we onze kunnen instantiëren GatewayDiscordClient:

@Configuration openbare klasse BotConfiguration {@Value ("$ {token}") privé String-token; @Bean openbare GatewayDiscordClient gatewayDiscordClient () {retourneer DiscordClientBuilder.create (token) .build () .login () .block (); }}

Op dit punt zou onze bot als online worden gezien, maar hij doet nog niets. Laten we wat functionaliteit toevoegen.

4. Voeg Event Listeners toe

Het meest voorkomende kenmerk van een chatbot is het commando. Dit is een abstractie die wordt gezien in CLI's waarbij een gebruiker wat tekst typt om bepaalde functies te activeren. We kunnen dit bereiken in onze Discord-bot door te luisteren naar nieuwe berichten die gebruikers verzenden en indien nodig te antwoorden met intelligente antwoorden.

Er zijn veel soorten evenementen waar we naar kunnen luisteren. Het registreren van een luisteraar is echter voor iedereen hetzelfde, dus laten we eerst een interface maken voor al onze evenementluisteraars:

importeer discord4j.core.event.domain.Event; openbare interface EventListener {Logger LOG = LoggerFactory.getLogger (EventListener.class); Klasse getEventType (); Mono uitvoeren (T-evenement); standaard mono handleError (Throwable-fout) {LOG.error ("Kan niet verwerken" + getEventType (). getSimpleName (), fout); retourneer Mono.empty (); }}

Nu kunnen we deze interface voor zo veel mogelijk implementeren discord4j.core.event.domain.Event extensies zoals we willen.

Voordat we onze eerste event-listener implementeren, moeten we onze client aanpassen @Boon configuratie om een ​​lijst met EventListener zodat het iedereen kan registreren die in het voorjaar wordt gevonden ApplicationContext:

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

Het enige wat we nu hoeven te doen om luisteraars van gebeurtenissen te registreren, is onze interface te implementeren en deze te annoteren met Spring's @Component-gebaseerde stereotype annotaties. De registratie gebeurt nu automatisch voor ons!

We hadden ervoor kunnen kiezen om elk evenement apart en expliciet te registreren. Het is echter over het algemeen beter om een ​​meer modulaire aanpak te volgen voor een betere schaalbaarheid van de code.

De setup van onze event listener is nu voltooid, maar de bot doet nog steeds niets, dus laten we wat events toevoegen om naar te luisteren.

4.1. Opdrachtverwerking

Om een ​​gebruikerscommando te ontvangen, kunnen we naar twee verschillende soorten gebeurtenissen luisteren: MessageCreateEvent voor nieuwe berichten en MessageUpdateEvent voor bijgewerkte berichten. We willen misschien alleen naar nieuwe berichten luisteren, maar laten we als leermogelijkheid aannemen dat we beide soorten evenementen voor onze bot willen ondersteunen. Dit zorgt voor een extra robuustheid die onze gebruikers wellicht zullen waarderen.

Beide gebeurtenisobjecten bevatten alle relevante informatie over elke gebeurtenis. We zijn in het bijzonder geïnteresseerd in de inhoud van het bericht, de auteur van het bericht en het kanaal waarop het is gepost. Gelukkig bevinden al deze gegevenspunten zich in het Bericht object dat beide gebeurtenistypen bieden.

Zodra we de Bericht, we kunnen de auteur controleren om er zeker van te zijn dat het geen bot is, we kunnen de inhoud van het bericht controleren om er zeker van te zijn dat het overeenkomt met onze opdracht, en we kunnen het kanaal van het bericht gebruiken om een ​​antwoord te sturen.

Omdat we volledig vanuit beide evenementen kunnen opereren via hun Bericht objecten, laten we alle stroomafwaartse logica op een gemeenschappelijke locatie plaatsen, zodat beide gebeurtenisluisteraars het kunnen gebruiken:

importeer discord4j.core.object.entity.Message; openbare abstracte klasse MessageListener {openbare 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 ("Dingen om vandaag te doen: \ n - schrijf een bot \ n - eet lunch \ n - speel een spelletje ")). dan (); }}

Er gebeurt hier veel, maar dit is de meest basale vorm van een commando en reactie. Deze benadering maakt gebruik van een reactief functioneel ontwerp, maar het is mogelijk om dit op een meer traditionele imperatieve manier te schrijven met blok().

Schalen over meerdere botopdrachten, het aanroepen van verschillende services of gegevensopslagplaatsen, of zelfs het gebruik van Discord-rollen als autorisatie voor bepaalde opdrachten, zijn veelvoorkomende onderdelen van een goede botopdrachtarchitectuur. Omdat onze luisteraars Spring-managed zijn @Onderhouds, zouden we gemakkelijk andere door Spring beheerde bonen kunnen injecteren om voor die taken te zorgen. We zullen daar in dit artikel echter niets van doen.

4.2. EventListener

Om nieuwe berichten van een gebruiker te ontvangen, moeten we luisteren naar het MessageCreateEvent. Omdat de logica voor het verwerken van commando's al aanwezig is MessageListener, kunnen we het uitbreiden om die functionaliteit over te nemen. We moeten ook onze EventListener interface om te voldoen aan ons registratieontwerp:

@Service openbare klasse MessageCreateListener breidt uit MessageListener implementeert EventListener {@Override openbare klasse getEventType () {return MessageCreateEvent.class; } @Override openbare Mono uitvoeren (MessageCreateEvent-gebeurtenis) {return processCommand (event.getMessage ()); }}

Door overerving wordt de boodschap aan ons doorgegeven processCommand () methode waarbij alle verificatie en reacties plaatsvinden.

Op dit punt zal onze bot het "! Todo" -commando ontvangen en erop reageren. Als een gebruiker echter zijn verkeerd getypte commando corrigeert, reageert de bot niet. Laten we deze use case ondersteunen met een andere gebeurtenislistener.

4.3. EventListener

De MessageUpdateEvent wordt verzonden wanneer een gebruiker een bericht bewerkt. We kunnen naar deze gebeurtenis luisteren om commando's te herkennen, net zoals we luisteren naar de MessageCreateEvent.

Voor onze doeleinden geven we alleen om deze gebeurtenis als de inhoud van het bericht is gewijzigd. We kunnen andere gevallen van deze gebeurtenis negeren. Gelukkig kunnen we de isContentChanged () methode om dergelijke gevallen uit te filteren:

@Service openbare klasse MessageUpdateListener breidt uit MessageListener implementeert EventListener {@Override openbare klasse getEventType () {return MessageUpdateEvent.class; } @Override openbare Mono uitvoeren (MessageUpdateEvent event) {return Mono.just (event) .filter (MessageUpdateEvent :: isContentChanged) .flatMap (MessageUpdateEvent :: getMessage) .flatMap (super :: processCommand); }}

In dit geval, sinds getMessage () geeft terug Mono in plaats van een rauw Bericht, we moeten gebruiken flatMap () om het naar onze superklasse te sturen.

5. Test Bot in Discord

Nu we een werkende Discord-bot hebben, kunnen we deze uitnodigen op een Discord-server en deze testen.

Om een ​​uitnodigingslink te maken, moeten we specificeren welke machtigingen de bot nodig heeft om correct te functioneren. Een populaire Discord Permissions Calculator van derden wordt vaak gebruikt om een ​​uitnodigingslink met de benodigde rechten te genereren. Hoewel het niet wordt aanbevolen voor productie, kunnen we eenvoudig "Administrator" kiezen voor testdoeleinden en hoeven we ons geen zorgen te maken over de andere machtigingen. Geef gewoon de client-ID voor onze bot op (te vinden in de Discord Developer Portal) en gebruik de gegenereerde link om onze bot uit te nodigen voor een server.

Als we de bot geen beheerdersrechten geven, moeten we mogelijk de kanaalrechten aanpassen zodat de bot kan lezen en schrijven in een kanaal.

De bot reageert nu op het bericht "! Todo" en wanneer een bericht wordt bewerkt om te zeggen "! Todo":

6. Overzicht

Deze tutorial beschrijft alle noodzakelijke stappen voor het maken van een Discord-bot met behulp van de Discord4J-bibliotheek en Spring Boot. Ten slotte werd beschreven hoe u een eenvoudige schaalbare opdracht- en antwoordstructuur voor de bot instelt.

Bekijk de broncode op GitHub voor een complete en werkende bot. Een geldig bot-token is vereist om het uit te voeren.