Skip to content

Instantly share code, notes, and snippets.

@112RG
Created October 21, 2018 04:09
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 112RG/ef3c645e712c797144a67cca6bd76c1b to your computer and use it in GitHub Desktop.
Save 112RG/ef3c645e712c797144a67cca6bd76c1b to your computer and use it in GitHub Desktop.
Discord bot. Microservice a love hate

Please read this gist by DasWolke it goes into a much better explanation of why you should use microservice Discord bots.

This is about my experience in making a microservice discord bot.

To start let's look at the core design I was looking for in creating my bot

(https://i.imgur.com/bWxXyc3.png)

I was looking to have multiple gateways that serve different shards groups this design could allow for a beta or premium version of the bot to be hosted on the same service more easily. I also wanted plug and play workers that don't store any information and could be restarted or crash without having any downtime and be scaled just by changing the replica value in docker swarm.

Gateway

The core of the gateway is built using CloudStorm an awesome library that handles the connection to discord. The gateway also has to handle requests from workers such as status update and voice updates and also Lavalink commands such as playing, pausing and skipping songs. The Gateway also forwards events received to the workers via AMQP such as message_create. The Gateway has to store some data such as current voice sessions and the lavalink player information the gateway has a connection to a Redis server that it uses as a cache for this type of data, since the data is not mission critical it isn't stored anywhere else.

One problem that I encountered while making my gateway was I wanted to run multiple gateway instances each serving a different pool of shards the problem was each gateway only knew information about there own shards, instead of all gateways subscribing to the same message queue when the gateway is starting they each create a message queue with a random ID generated at startup, then the ID is attached to all events are forwared to the workers so they know which gateway to reply back to

A quick example of this is gateway-1 is serving shard 0,1 so at startup, the gateway creates a new message queue called "qewr4114" and each event object forwarded to works has this attached to it Now when the worker wants to send a message back to the gateway its sends the message to gateway-[whatever] the workers know which gateway to send this too because each message forwarded by the gateway contains the gateway_id that the message was sent from.

Worker

The worker is the core of the bot it is responsible for handling events forwarded by the gateway running commands and making a request to Discords REST API. No library at the time was available that allowed events to be received by AMQP so I had to rig my own solution to this.

The basic layout of the worker is events are received via AMQP the worker then emits this and the event is received by the handler for that event. Since nobody has a modular command handler I had to write my own command parser atm it can handle rate-limiting and inhibitors the worker is also dumb nothing relies on a certain worker any worker can handle any event from any gateway and if a worker died no downtime would be seen by the user the workers uses a mix of MongoDB and Redis for storing data. MongoDB is used to store data the needs to kept longterm such as guild settings, user settings, bans and such. Redis is used for a cache the workers uses this to store cached discord objects such as users and guilds.

A great lib to use for this is RainCache

Voice

Voice was a tricky part to handle as the data for each voice connection has to be stored and needs to be accessed by both the gateway and worker. For this, I used Lavalink an audio node. Offloading voice from workers to Lavalink allows easy updates to the workers without the users seeing any downtime of the bot. To communicate with lavalink from the workers and the gateway lavalink.js and lavaqueue were used.

Final thoughts

After doing all this I have a working distributed bot. There are some problems and future things I want to implement

  • I want the gateway to be more easily scaled ATM a new service in docker swarm has to be created for each gateway
  • A better command handler the allows for prompting the user
  • Still have some issues with lavalink and broken voice connections
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment