RoosterBot is a platform-agnostic command bot with a highly modular design, written in C#. The entire source code is available on Github.
It has been a living project since September 2018 and I intend support it until at least after I finish college, and it will continue to exist even after that.
Platform-agnostic means that it does not care if it’s running on Discord, or Telegram, Whatsapp, or any other messaging system over any theoretical connection. Existing UI code does not need to be updated to allow usage on an entirely new platform.
Its modular design means that there is no core feature set visible to the user. It is like you removed all the blocks and items from Minecraft and placed them into a mod. All commands in RoosterBot are defined in separate .NET assemblies that are loaded at startup. The program known as RoosterBot.exe does have a lot of boilerplate code to make development of new features extremely easy. It defines entities that can be produced by a messaging service and consumed by commands functions, and provides several services, including a system for localization of UI strings, and abstract configuration services. These are abstract because they are implemented by components – by simply swapping out a component, you can move a RoosterBot instance from one cloud environment to another without any hassle.
Currently, there is one public instance of the bot running in the Discord server for my gamedev course. You can’t access that one, unfortunately. It is currently running on Amazon AWS, and deployment of new versions is automated. While it is currently only connected to one Discord server it is fully capable of being hosted on any number of Discord servers, or indeed any amount of website connections.
That instance, with its installed components, does have a core feature set. Users can look up schedule information quickly using a simple but powerful set of commands. (This is where its name comes from.) Unfortunately, during the COVID-19 pandemic, it has fallen into disuse.
The schedule code is split into a generic Schedule component, which only cares about the actual schedule information, and a component for reading the schedule data specific from the college I’m attending. This makes it possible to add schedules for any amount of sources, which may not be compatible with each other. The generic Schedule component ensures that schedule data can only be accessed by authorized people, so it wouldn’t be possible for a student of College A to see schedules of College B.
In February 2019 I released a new feature that lets people plan a train journey using RoosterBot. This uses the public API of the NS Reisplanner, and the C# interface to this API was built entirely by myself.
In April 2019 I released a beta feature that uses IBM Watson Assistant to let users ask the bot questions in natural language. Instead of learning commands like “!dag 2gd1 woensdag”, you could say “@RoosterBot wat heeft 2gd1 op woensdag” and get the same answer. This stops new users from having to learn commands, making it easier to use. In January 2020, I removed this feature because the concept became incompatible with RoosterBot after a redesign of the code.
In October 2019 I started working on a component that lets people look up actual weather and predictions for any location, using Weatherbit.io as the weather service. This has not been released, mainly because of concerns with overusing the free license, and because of maintainability issues with the C# wrapper for Weatherbit.io. Said wrapper is built into the RoosterBot component, and is not writter particularly well, so currently on my long-term todo list is to write a general .NET library for using Weatherbit.io.
The entire code can be found on Github, but here are some of the highlights. Given the changing nature of all the code, I will link to individual files here, but place extensive explanations.
When a user edits their command, for example after an error, the bot will execute the new version of the user’s message, and update the response instead of sending a new message. This is accomplished by storing the ID of the user command messages along with the ID of the bot’s response in the user configuration, and retrieving this information when a command has been executed and a result is ready to be sent.
Paginated schedule information
When you receive schedule data on Discord, it is presented in a nicely-formatted embed with all the data clearly visible. If you would want to see what comes before or after that schedule item, then you can simply click on the reactions that the bot added to its message. The bot picks this up and instantly updates its message with the relevant schedule information.
This is accomplished by the generic Schedule component returning a type of result known as a PaginatedResult. This is essentially a wrapper around a a custom IEnumerator of other Result objects. When the message is sent by the Discord platform component, a special handler is created that subscribes to the message’s reactions, and moves the IEnumerator when needed, updating the message with the new result.
The custom IEnumerator used can go both ways, instead of only forward, so the user can scroll through as much info as they like.
This pagination system is not limited to schedule information, or any type of command result. It does not even need an embed.