Discord bot with JavaScript!
Tags: discord, javascript, node, bot
Published: 2022-05-30 02:40:41

## Build a Discord bot using JavaScript!

To keep things tidy here, if you have never setup a Discord bot before, [follow this guide](https://dev.to/swislokdev/discord-bot-with-python-1i7n) 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.

```JSON
"main": "index.js",
"scipts": {
  "start": "node .",
  "server": "nodemon ."
}
```

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=<your-token-here>

### index.js

```JavaScript
// './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)
```

## 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.

```JavaScript
// './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
  }
}) 

...
```

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

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

...

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

...
```

### listener.js

- `touch listener.js`

```JavaScript
// './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 }
```

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`

```JavaScript
// 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,
}
```

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.