DEV Community

Swislok-Dev
Swislok-Dev

Posted on

Discord bot with JavaScript!

Build a Discord bot using JavaScript!

To keep things tidy here, if you have never setup a Discord bot before, follow this guide for the first half.

When you get to setting up the Initial Files section stop there and continue from this point on.

Once you have you bot application setup and token at the ready, we can begin!

Initial Files

Create the home directory where all the files for the bot to run will live.

Get started with the following commands:

  • npm init
    Follow each prompt and complete.

  • npm install discord.js discord-api-types dotenv

  • npm install nodemon -D

Now in the package.json file have a "start" and "server" under the script object. This is just a snippet of what should be in the file, but there should be more.

"main": "index.js",
"scipts": {
  "start": "node .",
  "server": "nodemon ."
}
Enter fullscreen mode Exit fullscreen mode

The,"main", in this case will be the file that starts the bot where the bulk of the code will go. Later, we will refactor to have it looking clean.

  • touch index.js
  • touch .env
  • .gitignore

To establish as a git repo run git init

.gitignore

In the .gitignore file place:

  • node_modules
  • .env
  • config.json

.env

In .env is where the bot token will go

  • TOKEN=

index.js

// './index.js'

const { Client, Intents } = require('discord.js')
require('dotenv').config()

// Create a new client instance
const client = new Client({
  intents: [Intents.FLAG.GUILDS, Intents.FLAHS.GUILD_MESSAGES],
})

// When the client is ready, run this code only once
client.once('ready', () => {
  console.log(`Logged in as ${client.user.tag}`)
})

// Login to Discord with your client's token
client.login(process.env.TOKEN)
Enter fullscreen mode Exit fullscreen mode

Test the bot

Running the bot through node directly or using one of the scripts that we wrote earlier to get the bot online.

npm start or npm server

Alternatively, you can run the scripts manually by node index.js.

control + c to kill the bot to add more code.

Do some stuff

Within the index.js file we'll have the bot respond to messages with a message and be able to get a random quote.

// './index.js'

// require 'node-fetch'
const fetch = require('node-fetch')

...

// Get random quote
function getQuote() {
  return fetch('https://zenquotes.io/api/random')
    .then((res) => {
      return res.json()
    })
    .then((data) => {
      return data[0]['q'] + ' -' + data[0]['a']
    })
}

// Respond to message
client.on('messageCreate', (msg) => {
// Do not allow a bot to reply to this
if (msg.author.bot) return

// Switch statement to check for incoming messages that match
  switch (msg.content) {
    case 'hello':
      msg.channel.send(`Hello ${msg.author.username}`)
      break
    case 'ping':
      msg
        .reply('pong')
        .then((msg) => {
          setTimeout(() => msg.delete(), 5000)
        })
        .then(console.log('We just got pinged!!!'))
      break
    case '$inspire':
      getQuote().then((quote) => msg.channel.send(quote))
      break
    default:
      break
  }
}) 

...
Enter fullscreen mode Exit fullscreen mode

Make sure to have the break in each case otherwise the bot will run through the statement and the output will not make sense as it will return everything in the switch.

Also the "inspire" message with the "$" will act as a pseudo-command in this case if you are wanting to avoid some words from being checked.

Refactor Time

The index.js is pretty cluttered now so time for some clean up. This practice will help maintain the understanding of how the bot should be acting rather than needing to scope through the entire file for a some problem that may have occurred.

index.js

// './index.js'
// add external file
const listener = require('./listener')

...

// After "client.once" 
// Respond to message (refactored)
client.on('messageCreate', (message) => {
  listener.onMessage(message)
})

...
Enter fullscreen mode Exit fullscreen mode

listener.js

  • touch listener.js
// './listener.js'

const Functions = require('./Functions')

function onMessage(message, client) {
// Ignore other bots
  if (message.author.bot) return

  message.content = message.content.toLowerCase()
  switch (message.content) {
    case 'hello':
      message.channel.send(`Hello ${message.author.username}`)
      break
    case 'ping':
      message
        .reply(Functions.getPing(message))
        .then((message) => {
          setTimeout(() => message.delete(), 5000)
        })
        .then(console.log('We just got pinged!!!'))
      break
    case '$inspire':
      Functions.getQuote().then((quote) => message.channel.send(quote))
      break
    default:
      break
  }
}

module.exports = { onMessage }
Enter fullscreen mode Exit fullscreen mode

We have moved the messageCreate listener to a new file and will be called and return when matching a single message.

Functions.js

  • touch Functions.js
// Require 'node-fetch' for quotes
const fetch = require('node-fetch')

// Get random quote
function getQuote() {
  return fetch('https://zenquotes.io/api/random')
    .then((res) => {
      return res.json()
    })
    .then((data) => {
      return data[0]['q'] + ' -' + data[0]['a']
    })
}

function getPing(message) {
  let latency = `Latency is ${message.createdTimestamp - Date.now()}ms.`
  console.log(`Latency is ${message.createdTimestamp - Date.now()}ms.`)
  return latency
}

module.exports = {
  getQuote,
  getPing,
}
Enter fullscreen mode Exit fullscreen mode

My main purpose with Functions.js is to add random functions/methods for a localized place to test other functionality.

Later on I may add slash commands for the bot to perform actions and be able to show a context menu for commands that a user will have access to per access level/guild.

Until then the bot will remain a backend handler for server management for use on select Discord servers.

Discussion (0)