Discord Bot

Dette eksempel viser hvordan man kan lave en Discord bot, som kan reagere på kommandoer i en tekstkanal.

Oprettelse af Discord Application og Bot

Start med at oprette en application i Discord udvikler portalen.

Derefter skal der tilføjes en Bot til din application.

For at give din bot adgang til din discord server, kan du bruge denne URL:

https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&scope=bot

Bemærk at YOUR_CLIENT_ID skal erstattes med det relevante id for din specifikke bot. Du kan også få en korrekt URL genereret ved at gå ind under OAuth2 i menuen, og vælge bot under scopes.

Efter dette er forarbejdet gjort og du er klar til at starte på koden til din bot.

Der er mere information om de tekniske detaljer på Discord Developer Portalen.

Opsætning af node projekt

Først skal der laves et projekt så node kan finde ud af at køre programmet, og har en package.json fil til at holde styr på projektet og afhængigheder af biblioteksmoduler.

Start med at lave en mappe, som kan indeholde dit projekt. Kald den f.eks. discord-bot. I denne mappe skal du køre følgende kommando, for at oprette projekt filen package.json.

npm init

Udfyld passende værdier som svar på de spørgsmål programmet stiller. Jeg foreslår at ændre din main fil til bot.js.

Dernæst har du mulighed for at installere de pakker, der skal bruges i projektet som afhængigheder. Dette gøres med disse kommandoer.

npm install discord.js
npm install dotenv
npm install axios
npm install cowsay
npm install random-fortune

Når programmet skal startes kan det gøres med kommandoen.

node bot.js

For at undgå at huske på hvilken fil der skal køres for at starte programmet, kan man tilføje en start action i scripts sectionen i package.json. Derefter kan du køre din bot, med denne kommando.

npm start

Her er indholdet den fulde af package.json.

{
  "name": "discordbot",
  "version": "1.0.0",
  "description": "",
  "main": "bot.js",
  "scripts": {
    "start": "node bot.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.26.1",
    "cowsay": "^1.5.0",
    "discord.js": "^13.6.0",
    "dotenv": "^16.0.0",
    "random-fortune": "^0.2.0"
  }
}

Bot koden

Hovedprogrammet til robotten benyttes sig af pakken discord.js. Koden er forholdsvis kortfattet, og placeres i filen bot.js. Bemærk hvordan kommandohåndteringen er uddelegeret til et andet modul.

require('dotenv').config();
const TOKEN = process.env.TOKEN;

const {Client, Intents} = require('discord.js');
const config = {intents:[Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES]};
const client = new Client(config);
client.login(TOKEN);

client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
});

const commandHandler = require('./commands');
client.on('message', commandHandler);

console.log('Beep beep! 🤖');

Kommandofortolker

Håndtering af de indkomne kommandoer er placeret i filen commands.js. Bemærk hvordan der uden større ændringer kan tilføjes flere kommandoer til systemet.

const serverID = process.env.SERVERID;
const channelID = process.env.CHANNELID;

const help = (msg, args) => {
  const reply = `Avaliable commands are:
  !help  : Show this help message
  !ping  : Responds with pong
  !hmm   : Get a random response
  !dog   : Shows a dog
  !toast : Get a quote from the toaster
  `;
  msg.channel.send(reply);
};

const dog = require('./commands/dog.js');
const hmm = require('./commands/hmm.js');
const ping = require('./commands/ping.js');
const toast = require('./commands/toast.js');

const commands = { help, hmm, dog, ping, toast };

module.exports = async function (msg) {
  // Only for this server and this channel
  if (msg.guild.id === serverID && msg.channel.id === channelID) {
    // Handle command
    let tokens = msg.content.split(' ');
    let command = tokens.shift();
    if (command.charAt(0) === '!') {
      command = command.substring(1);
      // check that command exists
      if (commands.hasOwnProperty(command)) {
        commands[command](msg, tokens);
        console.log(`got valid command, '${command}'`);
      } else {
        console.log(`got invalid command: '${command}', showing help`);
        help(msg, tokens);
      }
    }
  }
};

Brug af miljøvariabler

For at undgå at kode login tokens, server og kanal id’er ind i kildeteksten til programmet, benyttes pakken dotenv, således at disse kan hentes fra miljøvariable i stedet. Konkrete værdier placeres i en hjælpefil sammen med koden i dette format. Filen skal navngives .env.

SERVERID=replace_with_server_id
CHANNELID=replace_with_channel_id
TOKEN=replace_with_secret_bot_token

Pladsholderne skal erstattes med konkrete værdier fra den server bot’en skal være tilgængelig på.

Udelad private indstillinger i git

For at undgå utilsigtet deling af disse oplysninger er det en god ide at undlade at tilføje filen i git. Derfor tilføjes den til filen .gitignore, der f.eks kan se således ud.

node_modules/
.env

Discord specifikke værdier

De omtalte miljøvariabler skal som nævnt sættes til konkrete værdier, der afhænger af hvilken server/guild bot’en skal fungere sammen med.

For at kunne find de oplysninger, der skal indsættes i .env filen, er det nødvendigt at aktivere udviklertilstanden i discord.

  • Gå til brugerindstillinger > udseende
  • Scroll ned til avanceret
  • Tænd for udviklertilstand

Server ID kan nu tilgås sådan: højre-klik på serverens icon, vælg Kopiér ID i menuen.

Ligeledes kan Kanal ID fås sådan: højre-klik på tekst-kanalen, vælg Kopiér ID i menuen.

Bot Kommandoer

For at få en overskuelig struktur i koden er de enkelte kommandoer, som bot’en kan reagere på, implementeret i hver sin fil i mappen commands/.

!ping

Denne kommando svarer tilbage med pong. Den er implementeret i filen commands/ping.js. Bemærk at der ved at bruge funktionen reply(), automatisk insættes en @mention til den bruger der har aktiveret bot’en.

module.exports = (msg, args) => {
  msg.reply('pong!');
};

!hmm

Denne kommando svarer tilbage med en besked tilfældigt udvalgt fra en liste med svarmuligheder. Den er implementeret i filen commands/hmm.js.

const ufoUrl = 'https://i.pinimg.com/originals/39/b2/34/39b234d75c67da28abdcb38b1b4cf649.png';
const replies = [
  ufoUrl,
  '🤖 says go 🏄 and ⛷️',
  '(╯°□°)╯︵ ┻━┻',
  '¯\_(ツ)_/¯',
  'Så er der 🍰'
];

module.exports = (msg, args) => {
  const index = Math.floor(Math.random() * replies.length);
  console.log(replies[index]);
  msg.channel.send(replies[index]);
};

!dog

Denne kommando svarer tilbage med et tilfældigt udvalgt billede af en hund, som hentes fra et web-api. Den er implementeret i filen commands/dog.js. Kommandoen benytter pakken axios til at udføre kald til web-api’et.

const axios = require('axios');

const url = "https://dog.ceo/api/breeds/image/random";

module.exports = async (msg, args) => {

  const response = await axios.get(url);
  const dogImgUrl = response.data.message;
  console.log(`dog image url: ${dogImgUrl}`);

  if ("success" === response.data.status) {
    msg.channel.send(dogImgUrl);
  }
};

!toast

Denne kommando svarer tilbage med et tilfældigt udvalgt citat, præsenteret af en toaster. Kommandoen benytter pakken random-fortune til at generere tilfældigt udvalgte citater. Desuden benyttes pakken cowsay til den visuelle præsentation af tekst output som ascii-art. Kommandoen er implementeret i filen commands/toast.js.

const fortune = require('random-fortune');
const cowsay = require("cowsay");
 
module.exports = (msg, args) => {

  const fortuneText = fortune.fortune();

  const options = {
    text : fortuneText,
    f: 'toaster',
    W: 50
  };

  const res = cowsay.say(options);

  msg.channel.send(`\n\`${res}\``);
};

Her er et eksempelt på et svar fra robotten.

 _________________________________________
/ "Buy land.  They've stopped making it." \
\ -- Mark Twain                           /
 -----------------------------------------
   \                     .___________.
    \                    |           |
     \    ___________.   |  |    /~\ |
         / __   __  /|   | _ _   |_| |
        / /:/  /:/ / |   !________|__!
       / /:/  /:/ /  |            |
      / /:/  /:/ /   |____________!
     / /:/  /:/ /    |
    / /:/  /:/ /     |
   /  ~~   ~~ /      |
   |~~~~~~~~~~|      |
   |    ::    |     /
   |    ==    |    /
   |    ::    |   /
   |    ::    |  /
   |    ::  @ | /
   !__________!/

Afprøvning

Nu burde bot’en være funktionel. Den kan som tidligere nævnt startes med kommandoen npm start. Husk at det er nødvendigt at genstarte programmet, hvis der laves ændringer i det.

Materiale

Coding train discord bot series

Javascript - MDN Web Docs

NPM pakker

Json API