Build A Bot (DiscordJS) — Better Logging And A Persistent Bot Config

Last time we left off, we had turned our simple bot into a bot factory, allowing us to spawn multiple bots with different configs. Those configs though were still pretty simple and not persistent. The user could not make any changes unless he made them directly to the config files.

Today we will spend a little bit of time on a prettier logger and then allow our bot to read and write his own config file on the server.

SERIES: Build A Bot (DiscordJS)
1) Javascript Chatbots made easy
2) A scalable setup with command modules
3) A Bot Factory and Revealing Module Design Pattern
4) => you are here <=

As always, the link to the finished code in GitHub is at the end of the article.

Credits: Today’s session will include code influenced and partly taken from the Liora Bot Project. Feel free to look at their code for more inspiration.

Better logging

You know the drill, grab what we need from npm and then let’s get busy.

npm i -S winston chalk

Winston is working with log levels and colours so let’s start by setting up some sensible defaults. Right now we will mostly work with error, warn and info but later on, those other levels will be used too.

Then we create a new logger instance with the basic setup and formating. Within the printf function, we can format our desired logout format. We want a timestamp here along with the log level and of course the logged message.

What’s left to do now is to wire it up with our bot object, finally getting rid of that eslint-disable...

… and apply it in the places where we used the old and too simple logger and add our desired log levels and use chalk to paint the message where we see fit.

When you are done, your console logging should now look like this. If you want to see my choice of colours, check out this commit.

One thing that we can now get rid of is putting the tag everywhere by hand. We can let Winston handle that for us. Change the line where we assigned the winston.createLogger() result and turn it into a fat arrow function that passes in the tag and returns the logger. This way we can include the tag in our printf output via ${tag}.

Now we need to add the tag (including a sensible default) to our log assignment and we’re done.

The difference in the visual output is minimal but in our code, we just removed a lot of redundancy.

Before we move on to the config, we still need to clean up a bit. There are still useless tags scattered throughout our code.

Read & Write Configs

npm i -S jsonfile mkdirp opn

Let’s start by adding our new tools to the imports and defining a useful small sanitise function to radically clean up user input. We’ll use this later to create directories for the bots’ config files and we don’t want any funny characters in those directory names.

As we are going to implement proper configs now, let’s put some work in here and define a more detailed config schema. We can replace our old configSchema with this.

I’m using this schema to define what type of data the config accepts. This way we can run a basic check later to make sure every attribute resembles our requirements and we can include defaults in case the user has not set an attribute. Anything not in this list or of a wrong type will be discarded from the user input or old copies of the bot’s config. This way we can make sure that the current config is always compatible.

One advice, don’t put your token into the configSchema by hand. Include it in the initialConfig on bot start, as we had set it up last time. You would not want to hard code your bot’s token (or upload it to a public repository in any case!) as it better sits in the non-versioned .env file or environment config of your hosted project.

You should also add 2 lines to the rules in out .eslintrc file because we will need them soon to not get bugged by the linter about stuff that is working as intended / we want it to be.

1) Setting the config directory

2) Run it once initially

3) Open generated config files for proofreading

For this we will use something node provides us with, opn and if one of the bots had his config generated for the first time, we will open the generated file exit the process. On the next run of our script, all bots will connect regularly.

4) Check the configSchema

5) The big one, loadConfig

Our new loadConfig function will do a lot of things so I stripped it down to the shell and some comments to give you the outlines.

First of all, check for the existence of a config file. We will need this in a moment.

ALPHA

BETA

GAMMA

DELTA

EPSILON

If you want to make sure you got everything, have a look at the finished function in all it’s glory and complexity.

Link to the finished code/tag v0.0.4 on GitHub

Wrapping up

Our Bot(s) can now be started by creating a new or loading an existing config file. Next time we will add some commands that let the users with the right roles and permissions change the config on the fly, add new tags and maybe even access those from a dashboard… stay tuned.

Some words about me:

I’m also currently working on other series covering complex React Native Setups using Typescript and scalable apps with Redux, where I’ll go into details about how and why I do stuff the way I do as well as some articles on my experience in building games for web and mobile with React.

Here are some of my recent topics:
- React Quick Start with Typescript, Redux and Router
- Linting/Prettier with Typescript
- Redux + Toolkit with Typescript
- Spread & Rest Syntax in Javascript
- clean and simple Redux, explained
- Game Theory behind Incremental Games
- Custom and flexible UI Frames in React Native

And if you feel really supportive right now, you can always support me on patreon, thus allowing me to continue to write tutorials and offer support in the comments section.

I’m a Web / App Developer & father 👨‍👩‍👧 doing freelance and part-time agency work since 2003, 💻 building stuff on the side 🕹 and attending conferences 🎟