# Todor: A Discord music bot

# Introduction

This article's purpose is to describe the development of "Todor" a music bot I wrote for the Discord server that me and my friends hang out in. During the early stages of the project, a friend of mine would constantly play something on the bot, and then leave. Thus, I named the bot after him.

Todor is written in C#, utilizing the DSharpPlus library, and early versions utilized the library's build-in audio playback. Since then, that system has been replaced by Lavalink. Lavalink is an audio server written in Java, which also has a client library written in C#. The main reason for the switch, was to support a broader range of audio sources.

Over the course of this article, I will go over:

  1. Some of the bot's origins
  2. Deployment
  3. Hosting
  4. Maintenance
  5. Future
  6. Closing statements

# Origins

As already mentioned, the bot is named after a friend of mine. Around that time, I was still getting started with development, and a lot of the ready made bots' free versions, had limitations that I didn't like. Not to mention, that plenty of them would either not work, or have laggy playback.

So, I originally started experimenting with using Python, having seen some tutorials on the internet. To say that the original version was messy, would be an understatement. I wish I had some archives to be able to show some code, but alas, they have been lost to time. Not to say that the current version is anything to write home about, after all, it has suffered from 4 years of organic growth, but it is still much better than the original Python version.

# todor-reloaded

After that initial version, todor-reloaded was created in C# with DSharpPlus.

The reasons why C# was chosen, were:

  1. I already had experience with the language
  2. DSharpPlus

The main thing that I learned from that original Python version, is that I needed some sort of a structure in the program, so that it didn't become unmaintainable. The Python version was mostly two .py files, one for the entry point, and a big if-else statement for each command, and another file, which implemented each command. Finally, there was a big global dictionary, which severed as centralized storage.

DSharpPlus on the other hand, provided the framework to build out commands, as separate classes, with a more defined structure. Not to mention, the framework automatically handled things like command and argument parsing, as well as providing important events, to which you can attach handlers.

Finally, this was before the time I used Docker in any serious capacity. So, I really valued that I could compile the bot as a single executable, and that I didn't have to install a runtime on the server hosting it.

# Deployment

For quite some time, the bot's deployment consisted of me manually cross-compiling an executable (as I develop on Windows) for Linux, getting a cheap VPS from somewhere, copying it to the server, and running it with the screen command. This worked surprisingly well for a good amount of the bot's lifespan. As it was a single executable, and didn't really depend on anything else (aside from a config file that had to sit next to it), it didn't really need any fancy orchestration.

After I switched to using Lavalink for audio playback, I also started using Docker with docker-compose to deploy the bot, as having to have another screen for Lavalink, started to feel annoying. This way, the only things needed for a deployment from scratch, were the config files for the bot and Lavalink as well as a docker-compose.yml file.

# Hosting

Hosting has been a journey and a half. Trying to find a way to host the bot, either for free or effectively free, has meant that I've had to work with hosting providers, who haven't been exactly reliable. Imagine buying a VPS, and it just doesn't boot because it is stuck in a boot loop. Fun times!

# Google Cloud

At the start, I hosted it on Google Cloud, using their free trail. That took about a year to run out, as the bot (even when using Lavalink) can happily live on a machine with 1GB of RAM and 2 CPUs. After that was over free trial, I had to find somewhere affordable to host.

# Romania

For about a year, there was a Romanian hosting provider, that managed to suffice. The bot ran fine, but anytime I had to do any maintenance on that machine (such as updating it) it was a pain, as it seems that there was something fundamentally wrong with the OS templates that they provided. And don't get me started on their KVM solution. Good luck installing a custom ISO. That said, I persisted, as the price was good, and 95% of the time, there were no real issues. The bot ran fine. That was, until I started running into the boot loop issues I mentioned in the first section.

# Hetzner

DISCLAMER: Hetzner have not given me any money, and this is not an advertisement for them. That said, they have been the most affordable, reliable, and performant hosting option I've used so far. Period. And, technically, I could end this section here, but that is not the end of the Hetzner story. Unfortunately, Todor is no longer hosted there. And this is no fault of Hetzner themselves, but rather Google.

You see, the way the bot fundamentally works, is that it streams videos from YouTube, strips the video from them, and then it forwards the audio to Discord. This, as you may imagine, put extra pressure on YouTube's servers, which I can bet that they don't appreciate. However, worse than putting extra load, is that it also acts as a bit of an ad blocker. Whenever you play a "video" via the bot, ads are not streamed, as they are a separate video/audio stream.

As a result, YouTube, seem to have marked the entirety of Hetzner's IP ranges, as addresses from which requests require authentication, which is something that Lavalink doesn't support. So, I had to move again.

UPDATE (03/12/24): Since version 1.10.1 of the youtube-source plugin for Lavalink, it is now possible use OAuth to authenticate with a YouTube account. You can refer to the plugin's docs on how that works. Tip: Use a throwaway account, just in case it gets banned!

# Sofia

Currently, the bot lives on a small VPS in Sofia, Bulgaria. The provider seems to be small enough, so that their IPs are not flagged. That said, this new VPS only has 256 MB of memory, and it is a really tight fit. But, it is still alive! If anything, it highlights something that I really want to achieve with the next major version of the bot (todor-revolutions perhaps?). I want it to be extremely memory disciplined, so that it can even run on extremely low end VPS boxes (such as the one from LowEndSpirit), some of which can have as low as 64MB of memory, and a quarter of a CPU core.

After all, theoretically, it is just needs to move bytes from A to B while deleting some of them.

# Maintenance

So, if you look on GitHub, project's repository has been on the site since May 31, 2020. A question that might come up, is what was it like to maintain over 4 years (and counting). Well, despite me not being very satisfied with the bot's overall code architecture, and the way it does some things, after going through some growing pains during the first year or so, it has been surprisingly reliable. Mostly, it has only needed minor version bumps, and that is about it.

If anything, the bigger problems have come from operating the bot, mostly due to YouTube's flagging of Hetzner's IP range, rather than the bot itself.

The only bigger change that has happened in the last 2 years or so, has been the need to change Lavalink's configuration, to use the new YouTube plugin that they have, but even that, hasn't required any changes in the bot's code itself.

# Future

So, what does the future hold for Todor? Well, there are a few goals that I would like to achieve with a future version.

  1. It needs to be able to run on extremely low end VPS-es, such as the ones from LowEndSpirit. This will not only make it cheaper for me to run in the long term, but also make it more mobile, in the case the YouTube decide to keep flagging addresses.
  2. Support YouTube authentication. As much as I would like to not have to authenticate to pull video from the platform, eventually I think that they will make it a requirement for any request.
  3. Use Discord's newly introduced slash commands, instead of a config defined prefix (as it does now).
  4. Some sort of a remote control over the network. Either via HTTP, or something custom, as the memory budget would be quite tight. One could even call it out of band control, as in, not via Discord commands.

# Conclusion

Overall, working on Todor has given me a lot of the fundamental skills that I posses today. Working on it was the first time I had to:

  1. Work with Linux in way that was more than installing a package or two
  2. Developing a software project that would actually go on to be actively used for years
  3. Migration from one hosting provider to another
  4. Docker
  5. And probably more that I can't quite recall on the spot

More updates to come, bookmark this blog if you are interested!