
VANGUARD: How I Ended Up Reworking the Official VALORANT Discord Bot
This is the story of this one time when I made a significant structure update to the official VALORANT Discord server’s moderation bot: VANGUARD.
Quite the catchy name, right? VANGUARD was originally “created” for the VALORANT Discord servers. Why did I put “created” in quotes? Why “originally”? Also, why does everything that’s about VALORANT has to be in all caps, making it seem like someone is screaming? (Almost) All of these questions and more answered in the rest of this article.
Ready for a nice story? Get comfy, get a nice chocky milk if that’s your jam, or a toast with jam if that sounds more like something that milks your boat… And let’s dive in!
Joining the VANGUARD
There was a time when Discord thought that fostering healthy moderation practice was a good idea, and they made a server: DMD, Discord Moderators Discord. The… Discord server for Discord moderators. Yeah, the abbreviation is better.
If you think moderating a Discord server is just banning people, well… Let’s just say you might not have the right idea about what a moderator should bring to a community 😛. And while there are a lot of trigger happy and / or power-hungry moderators out there, they do not represent “healthy” moderation practices.
What good moderation is about is a huge topic and I will not talk about it here. Just know that it can get very complex very quickly. It’s not something that’s easy or natural at all, and not a lot of people are good moderators. Well, the DMD server was filled to the brim with excellent moderators. And somehow, I got invited too! I felt incredibly humbled to be surrounded by such experts.
Primarily, the DMD server was a way for mods to exchange tips and tricks about moderation. It was also for Discord employees to get feedback from Discord experts about new or upcoming features. Lastly, it was a way for important communities to easily find competent staff.
The Announcement
One day, I noticed an announcement posted by the owner of the VALORANT Discord servers, Flokie. Basically, he was explaining that 🏳️🌈 Pride Month 🏳️🌈 was around the corner, and that since they were going to change the server logo for something more ✨ rainbow-y ✨, they expected a lot of hate in the chats, and needed help. It was not a wild guess. They’d change the logo for the Pride Month every year, and every year they’d get an insane amount of hate for it. They knew what was coming.


They needed extra mods for that period of time, and I was happy to answer the call! The experience was great, getting to know the team, familiarize myself with the tools and processes, getting trained… I really clicked with the vibe of the team, they were amazing!
When hate faded, they sent us a message to thank us for our help. I asked if I could stay and they said yes! So I joined the staff of the official VALORANT Discord server as a moderator 😎.
Not too long after, I noticed a conversation in a moderation channel. Someone needed to warn more than 10 users. Problem: the bot we had back then only had a warn command that could target a single user at a time. So the mod needed to type 10+ warn commands, and that was… An unpleasant experience to say the least!
So naturally, I asked: “Where is the code?”
I was 100% expecting them to tell me that it was not theirs, it was closed source, or something similar. What a surprise it was to receive a GitHub URL to a public repository! I was excited, Discord bots are my specialty!
And then I took a look at the repository.
Opening the monster
Here I was, inside the GitHub repository. Full of hope, poking around haphazardly, expecting a somewhat complex but still manageable amount of code. But the more I opened folders, the more I grew worried that the “manageable” part was not going to join the party.
Eventually, I thought “You know what? I am more comfortable exploring code in my IDE anyway!”. I booted up a terminal, cloned the repository, then opened the folder in IntelliJ. It was at that moment that I opened a folder named “plugins”. I noticed my IDE slowing down a bunch, heard my fans revving up, and felt my jaw drop.
That is… a lot of plugins.
I was blafferghasted. Uncombobulated. My entire collection of timbers had been shivered. There were so many subfolders, so many plugins that my screen was not tall enough to show them all. Well, my screen is not very big to begin with, but still! It reminded me of the time I thought I could read Mozilla Firefox’s code and promised myself to never talk about what an embarrassing experience that was! … Wait.
This time, though, I kept pushing forward! I started reading the plugins’ names, trying to see if it made sense to me. And… Well, it did! It was all surprisingly clear, and easy to understand! It was pretty reassuring, it felt like a cool breeze of sense in a desert of confusion.
If Monster, Why Friend-Shaped?
Then, I started opening a few plugin folders, fully expecting my computer to puke another flurry of subfolders with each one, but… No, they were actually pretty clean. As I removed my safety squints, I started noticing patterns. For each plugin, a PluginNamePlugin.ts with a types.ts at the root, then only the relevant folders based on what the plugin does, but always the same architecture. Surprisingly clean!
Don’t get me wrong, I was only surprised because in my experience, big projects like that tend to get messy over time, and people justify it with the “legacy” buzzword all over the place. And sure, this project was not perfect, but it was still very clean.
Zeppelin: The Prequel

At that point, I grew curious about the origins of this project. I jumped back into Discord and asked about it. It turns out VANGUARD is a fork of another very respected bot: Zeppelin. Years ago, the mods of the VALORANT server were using Zeppelin directly, but they started noticing they needed more features. So they started a fork of it, named it VANGUARD (like the anti-cheat system of the VALORANT game), and added whatever they needed. That bit of history helped a lot, so I thanked them and bounced back to my IDE.
As I kept browsing, I found a plugin named “ModActions”. Hey, that sounds like a plugin that takes care of warning users, right? Sure does! But it’s also one of the biggest plugins, of course 😅! So, in order to find the file I needed to edit, I used the most precious tool in my belt: search.
I fired up a local instance of the bot, used the warn command as a test, and whatever the bot said in response, I copied, opened a global search in IntelliJ, and pasted. That gave me enough guidance to find the files responsible for that command, and to start reading and learning how existing code worked. Next step was just a matter of imitating what had been done in the warn command, but modify it so it could be a “mass” warn instead, testing it, and…
Woa! It works! First try? Should I commit? No, I should clean up my code, refactor some things… But… I’m scared to touch it!
It works! Don’t touch anything!
You know this feeling when you build a castle of cards so high that you’re proud of yourself? And then you realize that it’s not something that you can just let sit there and trust it will stay exactly as it is? Something, likely sooner rather than later, is going to knock it over and make a mess you’d rather not be the one cleaning up? Well I hope you do because that’s the only metaphor I could come up with!
My castle of cards was holding up, the mass warn command was working, but… That was just on my local environment, on the few tests that I ran, and… I didn’t exactly understand 100% of the code I had to read and replicate… Certainly, it was not going to hold up in production. I could *not* leave it like that. So I dived back in. Trying to read more code, this time not just about the pre-existing warn command, but other commands as well. Then, I tried to find the event listeners. And the command system, how was it working anyways? There had to be a listener somewhere for something like “New message” that would tie it to one of the existing commands!
At that point, I was mentally exhausted, and somewhat discouraged, even if only temporarily. I had accomplished something, even if it was not structurally sound, it was still more than I had thought I could when I first realized how big the codebase was. So I went to rest, thinking about everything I found, about how confusing all of it was.
A Good Night of Sleep Later…
See, the VANGUARD project used an ORM that I had never used before. It also had several layers of abstraction. Oh, and the bot part was only like half of it, there was also a web dashboard made in Vue.js and at that time, I had never touched that in my life. It was also my first time encountering “zod”… And if you, reading this, have no idea what all of this is, imagine you have to understand it all in order to build something, and you’d be able to relate to how I was feeling back then 😔.
A night of sleep is always a great help in these cases though, and as I woke up feeling energized, I decided it was time to read some docs! So I started looking up what all of that was. Finding real-life examples of how people used those tools on GitHub. The goal: having a clear idea of where each piece was supposed to go in the bigger puzzle.
Okay, so the ORM has its models in this folder. Oh, and that’s how it queries the database, got it.
The logs are in their own separate plugin, but they have… Oh, yes, plugins have public interfaces! And the logs plugin has a public interface that’s used when the others need to log anything! That makes a lot of sense, that’s super cool!
Zod is… A tool that checks types? But why do we need that in a Typescript project where type checking is already part of the langua- OH! It’s to validate the yaml configuration!
Growing Codebase, Growing Confidence
With a much clearer understanding of the tools used by the project came a respectable amount of confidence. Reading my code again, I realized some of my mistakes and fixed them. I noticed some things that were missing and added them. I was starting to feel like I was part of the project, not just someone who was trying to hack something together.
Eventually came the time to commit, push, merge, deploy, and test. It was working, still! I gave the news to the team, the bot now had a way to mass warn users. It didn’t take long until a mod needed the feature and tested it for real: all good! I received a lot of thanks, and during the days that followed, more requests for new features, more gratitude from the team, and that’s exactly what drives me, so I was more motivated than ever.
As I was adding more and more code to VANGUARD, though, I kept stumbling upon something that still was unclear to me: “Knub”. Every time I saw that word, the code was doing something that I did not understand. I had no idea what it was, and it was really starting to slow down my efforts. For some reason, I totally missed that part in my earlier research. But something was worrying me: the other tools I looked up (Zod, TypeORM, Vue.JS), even though I had never worked with them before, I at least knew they were famous. But “Knub”? I had never heard of that in my life ever before.
Turns out it’s a framework!
In retrospect, I should’ve expected it. Every time I was trying to understand what the Knub calls were doing, it felt like it was code that should’ve been part of the Discord bot directly. Decided to really understand what it is, I opened a web browser and searched “Knub”… But not before opening the node_modules folder to get a grasp of its code and what it was doing.

In there, I found a lot of code interfacing with the Discord.JS library, functions exported and ready to be called, an entire architecture that seemed to revolve around Discord. Curiosity piqued, that’s when I searched it up on the internet. Turns out Knub is a framework made by Dragory, the creator of the Zeppelin bot. And even though it is still a work in progress, Knub has helpful documentation!
So I spent some time reading it, understanding it, and it all clicked into place. Knub being the Discord bot framework used for the Zeppelin bot, VANGUARD being a fork of Zeppelin… So every time I was seeing a call to a Knub function, or type, or whatever, it was about interfacing with Discord.
Armed with this new knowledge, I was finally able to understand the big picture of the project. How Dragory architectured it, how every component was working together… I could see it all as cogs in my head, and they were turning fluidly! I felt even more confident. The new feature implementations became much smoother, and looking back at my old code made me cringe, which was a sign of growth.
One day, someone asked if the bot could work with slash commands instead of message commands.
Discord bots can do a multitude of things, and most of them have commands. A command typically indicates the bot to do one specific thing. For example, with VANGUARD, if I want to send a warning to a user, I would use the “warn” command.
Commands have a name and parameters. Keeping the previous example, “warn” would be the command’s name. One parameter could be the user I want to warn.
Message commands were the first ever way to send commands to a bot. Back when Discord bots first became a thing, we basically just had messages, and we needed a way for bots to distinguish between normal human conversations and instructions they should follow. The solution was simple enough: define a prefix for your bot, and write your command right after that prefix.
Back then, the most common prefix was “!” with the most common command being “help”. It was very natural after installing a new bot to send “!help” in a message, and the bot would respond with instructions on how to use it.
With VANGUARD, the prefix is “?” (even though it is configurable). A warn command would be for example ?warn 161165663715459072 Warn reason here.
While message commands were working fairly well in most situations, they were still a hack, and as such were never very well integrated into the Discord client. Doomed to forever stay as an afterthought, it became obvious we needed a better way to send instructions to bots.
Application commands were introduced as a solution to all of those issues. They are a designed and intentional way to interface with Discord bots. And slash commands are the application commands closest to what existed before.

And my initial thought was “Yeah, it probably can! I can’t believe there’d be no slash commands at all in this huge bot haha!”. It was… Wrong. There was a plugin hacked together that added Context Menu Application Commands (a barbaric name that just means you can right click on stuff and you get commands in the menu), but it was more of a proof of concept rather than a real implementation.
As I kept digging through what I knew about the architecture, I ended up realizing that… No, VANGUARD, and not even Zeppelin supported slash commands, at all. And the more I thought about it, the more I could find ways in which application commands would greatly help the experience of using the bot. It seemed like the next logical step.
But who would be foolhardy passionate enough to change the entire structure of one of the biggest moderation bots there is out there? I mean… I could see how it could be done but… There was no way the people in charge would allow me to make such significant changes to both Zeppelin and VANGUARD. Can you imagine though?
Implementing slash commands
Certain that the idea would get rejected, I went and asked Flokie (reminder: owner of the official VALORANT Discord servers) if he would be interested by slash commands in VANGUARD, or other kinds of application commands. He actually was pretty enthusiastic about it, and told me to go for it! I… Stayed frozen for a couple of minutes, and went to see Dragory, thinking that if anybody would reject the idea, it’d be the guy who created the architecture in the first place. He hesitated, thought about it, and told me that it was (technically) possible to implement, since Knub was already (technically) ready for application commands!
So I had all the green lights, nothing could stop me from implementing slash commands into Zeppelin and then VANGUARD! Except I was paralyzed. Sure, I had an idea of how to do it technically, and sure, my understanding of Knub was much better and I could probably work with it enough to be able to actually pull it off but… It was such a daunting task!
So I remembered what I had learned when I work at Valtech in Montreal. When you have something to work on that is too big to apprehend, slice it into smaller parts. And so I did! I made a full plan on how to implement this huge feature, and every time a part of this plan was too big, I’d slice it up again into smaller parts. Then, I sent the plan to Dragory, and we had a voicechat about it so I could really understand the ins and outs of what I was about to do. That’s when I learned that he pronounces “Knub” like “Noob”. Cute!
*Cracks Knuckles*
And off I was. I’m not going to detail everything that needed to be done for this to work, but let me just tell you this: the plugins were largely revolving around the concept that the bot was using message commands. A lot of functions were receiving a message as a parameter so they could reply to it, for example, and you don’t reply to a slash command in the same way! I had to change all of those places, often add a layer of abstraction so it could work the same whether it was called from a message command or a slash command… At that point, I wasn’t just adding a feature anymore. I was taking assumptions baked into years of code and changing them. It was long, tedious, repetitive, and very stressful because I knew I had to be extraordinarily thorough.
For a rewrite this big, you can’t 100% trust the tests! Testing all the features of this bot would require a professional team working full time on it for a week or so, and we didn’t really have that kind of resource available. So every time I was making a change, I would use all the tools available in my IDE to check if I needed to make a similar one in other places or not.
*Drops Keyboard*

After a couple of months, more than 52k lines of code changed, and a lot of caffeine (in the form of energy drinks), I did it.
It was only after creating that humongous pull request that I thought “Maybe I should’ve created multiple PRs as I was making progress so it would’ve been easier to review“. Oops. Apologies Dragory 🙏! Fortunately, the father of Zeppelin doesn’t disappoint, he did review my code, and gave a lot of feedback, which I then implemented.
After that, Dragory merged the PR into Zeppelin’s main branch, I merged it back into VANGUARD, deployed and just like that, slash commands were available. Can you imagine how happy the team was?
They were happy, right?
Old habits die hard
Mods had been working with the same commands for a while, and even when the bot changed, the admins made sure the syntax of the commands stayed the same. I should’ve expected that when I basically told them “Hey, thanks to me you can change the way you’re used to doing things to a completely different process!” they would raise at least one eyebrow.
What can I say, I was so proud of my achievement that I forgot about the reality of user experience! I’m not mad, though. Far from it. Application commands still exist in Zeppelin and VANGUARD, they still work, not just for the mods of the VALORANT Discord server, but for every single mod who uses Zeppelin. It’s completely okay that some prefer sticking with what they know best, and honestly very understandable!
In the end, I am very proud of myself for having contributed to such a cool project. I learned a lot thanks to Dragory and his code, I grew as a developer thanks to his experience and expertise. And that is something that can never be tarnished or diminished.
Thank you Flokie for all the opportunities.
Thank you Dragory for your patience, discipline, and pedagogy.



