Dovahzul Bot in Node


Creating the Bots on Developer Portal

As I mentioned last week, the dragon translators shall be our first bots. Before coding the bots, we need to establish discord applications using the Discord Developer Portal. You can connect to the portal at https://discord.com/developers/applications. On the developer portal, there should be a button to create a new application. The application is more of a container for the bot, as we need to actually add our bot to the application we created. 




After selecting your application you should see a bot tab. Within this tab there is a button to add a bot to your application. 

Warning! Discord refuses to create bots with generic names! 



After a successful bot is created you will now have access to a bot token. KEEP  THIS TOKEN SAFE AND PRIVATE! As shown in the picture above, you can regenerate the token if compromised. While I am not an expert on security, someone with malicious intent could repurpose your bot should they have the token. We will be storing all secret API keys and tokens inside ".env" files. The ".env" approach avoids exposing sensitive data in your code. 

Designing Dovahzul 

At this point we can begin designing our bot. This is technically an optional section if you are a design-as-you-go type of person. Before writing any code, I prefer to answer a few questions about my bot. 

Who is my audience? (What communities/servers will the bot be invited too) 

Members of the Discord Bot Army discord server.

What actions will my bot perform?

There will be one command to translate English messages to Dovahzul using a translator API. The bot will reply to the user which submitted the commanded using discord mentions.

What is the identity of my bot?

Given Dovahzul is from Elder Scrolls, the bot will portray itself as a non-player character from that fictional universe.

What is the intended scale of the application?  

Small and solely developed for learning purposes.

There are more questions that can be asked, but these 4 will generally suffice. The design for the dovahzul bot is rather simple. There is going to be a single command for translating message. Commands are typically entered with a single character prefix such as "!" or "$". Discord already as its own set of "slash" commands that are available to any users. 

Note: Permission to use slash commands are generally restricted in discord servers.     


Rather than implementing slash commands, Dovahzul and the rest of my bots will use the "!" prefix. Designing the bot to be configurable is an option, but this would add significant overhead to the project. Should the bot be intended for public use, configurability is quite important. The ability to change prefixes reduces the conflictions with other bots which use the same prefix.

Node: The Workspace

Project File Tree

Assuming you have NodeJS installed on your machine, you run the npm init -y command in your terminal to initialize the node project. Make sure you are in your desired root directory before running the command. A package.json should be visible in the root directory of your project. My root directory is "dovahzul-node".  In the root I created a .env file to store our environment variables as well as a "src" folder.  Within the "src" folder I created a "bot.js" file which will server as the entry point of the application. 

There are a few packages I needed to install before really beginning to code the bot. The dependencies are "discord.js", "axios", and "dotenv". Optionally, "nodemon" is another package I installed to quickly debug code. You can install packages with the command npm i <package_name>

After successfully installing the packages, your package.json should include key-value pairs for your dependencies.  You should also see a new node_modules folder in your root directory. 

package.json

Looking closely at the scripts section of the package.json, you may notice mine is different. There are two custom scripts which allow me to run the project with either nodemon or node. The command npm run dev, or if you have yarn installed, yarn dev, will run the project in nodemon mode. This restarts the bot after any lines of code are changed, which makes for a better workflow. 

In the ".env" file, you should have your variables formatted as follows:

.env
If using GitHub or another VCS, avoid publicly uploading the .env file with sensitive data. In GitHub you can specify ".env" in your git ignore.

Node: discord.js

Because the project is setup, I can now program the Dovahzul bot using the discord.js library. You can find the documentation for the library at https://discord.js.org/#/docs/main/stable/general/welcome
The discord.js library acts as an interface between discord and your bot. Using this library you can control all your bot's actions. We can see in the documentation there are many events to our disposal. The key event for this bot will be the messageCreate event, which you can see is in the Client class here: https://discord.js.org/#/docs/main/stable/class/Client?scrollTo=e-messageCreate. For those proficient with JavaScript, you may recognize the Client class is an EventEmitter. As an event emitter, Client has access to two new methods not articulated in the discord.js documentation. Those being the "on" and "off" methods. While I could write an entire post about emitters, I will spare you the details since this is not a "Learn JavaScript Blog". Using the "on" method we create a listener to the event being emitted. With listeners, we can handle any events that are emitted with an anonymous function. For reference, the events tab in the documentation for the Client shows every event. 


In the first few lines of code, we need to configure our Client and other libraries. 

lines 1-8, bot.js


The latest version of discord.js requires an Intents object. Intents specify the bots permissions. For the Dovahzul Client, I specified the GUILDS and GUILD_MESSAGES intents as shown above.

Using the "on" method, I can define an action for the messageCreate event. The callback for the messageCreate event uses a parameter of type Message. All the properties of message are in the discord.js documentation.   

line 41-44, bot.js


A bot will be unable to function unless it is logged into discord. This is where we pass in the bot token and make use of the "dotenv" package. The login method in my case is called in the very last line of "bot.js". 

line 49, bot.js

Node: Parsing Commands

lines 41-47, bot.js

Parsing strings are easy in JavaScript compared to other languages. (Obligatory foreshadowing) 

This one-liner deconstructs any message a user sends into a command string and argument array.

const [cmd, ...args] = msg.content.trim().substring(prefix.length).split(/\s+/);

  • msg.content returns a String of the user's message
  • Trim to "cut out" white-space
  • Substring to "cut out" prefix character
  • Split to separate each word by white space (regex \s+ refers to one or more whitespace)

 
The parsed message is then sent to a function I created called handleCommand.

lines 16-22, bot.js

The handleCommand function has to check which command was entered and then execute x action. For those familiar with the command design pattern, this appears to be a perfect opportunity to refactor. The reply method instructs the bot to send a new message "mentioning" the author of the previous message.  Mentions in discord are denoted by an "@" prefix before someones username. Discord automatically highlights any messages where you are mentioned. 

Node: POST Request

The API for the translation logic requires an HTTP POST request. We can use the axios library we installed earlier to send requests to the translator's server. The translation service I used is "Fun Translations" at https://funtranslations.com/thuum. They offer 1000 requests per day for $5 a month, however there is also a free version for 5 requests per day. 

lines 23-34, bot.js

The axios post function allows us to configure the request with the required headers and parameters. Any incorrect parameter or header could result in a "bad request error". The arguments passed were previously split by white-space into an array, so we need to rejoin them to have a full string of translatable text.  The replyTranslated function is called via a JavaScript promise when the request was successful. 

lines 9-14, bot.js

The replyTranslated function I created processes the HTTP response. More specifically, it parses the received JSON and tells the bot to reply with the translation. 

Node: Results

basic translation

The bot is able to translate English successfully using the "!dova" command! 


translation of "Ragnar the Red"


Overall I found developing in Node an absolute breeze! I ran into several issues with the translation API, however node always seemed to function when I least expected it too. Nonetheless, next week I will cover Python and Java versions of this bot. This post included more introductory sections, so I was unable to cover Python like I originally intended. Expect later posts to go more in depth into the discord libraries and code. 

Comments