-- Leo's gemini proxy

-- Connecting to nanako.mooo.com:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini; charset=utf-8

Back to Work on Discord Bots

So I have three Discord bots that I've written. Well technically four, but I don't use or update the original anymore. Monika (currently v1.5 I think) is my main bot, and is fully functional and is written in Crystal. She's has a few admin functions, but is generally meant to be a "fun" bot. You can talk to her, get weather reports, ask her to decide on things, have her generate Doom levels using Oblige, etc. Her admin functionality was sorta shoehorned in, though (by "admin tasks", I mean things like logging user joins/leaves/name changes, pruning old pinned messages, statistics, etc.). They work, but have always been a bit buggy since they simply don't fit her overall model very well.


To compensate for this, I started work on a proper "admin" bot called Sakuya. This bot would handle all the admin tasks Monika currently does plus additional ones that a few friends have requested. As is usual for me, my initial prototype was in Common Lisp, with plans for the final version to eventually be ported to Crystal.


My third bot is Lewdmilia. She does nothing but post images for me on my behalf. I've been using her as a sort of test bed to explore ways to make my bots scale better. She's been written in Crystal since the start.


Monika's a Mess Inside

Monika's internal structure assumes that she doesn't need to keep track of much. Most of her commands and behavior is assumed to be simple one-shot commands such as "!decide tacos, pizza, hamburgers". Even with her rudimentary "conversation" behavior, she doesn't keep track of state. So you could ask her, "Do you like tacos, monika?" twice in a row and get two different answers (probably - it's random).


When it comes to admin stuff, she needs to keep track of a lot of state, which she uses a database to do. For example, she tracks the last three nicknames a user has used, and when they changed them, then posts it to a secured log channel that only my moderators can view. But of course all of this is shared state between all of the guilds that she's in (I say "all", but there's only three right now - there was four). Useful in some ways, such as when two guilds share a single user, but also annoying because the subsystems that handle the admin stuff simply were not designed with multiple guilds in mind.


So she's sort of a hybrid. Half of her is perfectly fine existing in multiple guilds. The other half of her is severely limited by this.


One additional thing I should point out is that Monika is "all or none". When another guild uses her, they get ALL of her functionality and commands. This is a downside in my view, as I feel she _should_ be more configurable. For example, if a guild doesn't want the "!imagemod" functionality (used to make meme images), the _should_ be able to disable it. Sadly, in her current state, they can't.


Enter Sakuya

Sakuya is the bot I have planned that will take admin tasks over from Monika and handle them properly. The first release would see all of Monika's current admin functionality moved over and fixed (with Monika then getting a 2.0 release). This helps make Monika easier to maintain in her current state, and each bot is a bit more directed in their purpose.


But then I got to thinking about how Monika is "all or none"... wouldn't this be a good time to also fix that issue?


Redesign

Monika currently works like this:

The program starts and instantiates an instance of the Monika class, which is handled as a singleton (ugh).

The Monika instance loads the configuration files and connects to the database.

The Monika starts up instances of various subsystems - command dispatch, user history monitoring, any non-trivial command, and message rules (conversation stuff).

The Monika instance connects to Discord and listens for certain events.

The main program body tries to handle TERM and KILL signals by telling Monika to shut down when they occur.


Straightforward, but you can easily see the problem: a single instance is used for all the guilds she knows about, yet to the users in Discord, she attempts to appear as individual instances. Sakuya changed a bit of the lower level subsystem stuff where the subsystems run concurrently and communicate through channels CSP-style, but otherwise she currently uses the same overall design.


So my new plan is this:

The program (the "supervisor") starts and creates one Monika instance PER GUILD.

Each Monika instance loads its own configuration file.

Each Monika instance connects to its own database.

Each Monika instance sets up its own set of subsystems according to how the guild is configured.

The supervisor creates an instance of a new class, Client. This connects to Discord and listens for events, then dispatches them to the correct Monika instance according to the payload's guild.

The supervisor shuts down everything cleanly if a TERM or KILL signal is detected.


Basically, a set of Monika bots all sharing a single connection, and are all managed by a simple supervisor program. I did look into sharding, but it not only seems overkill for what I'm trying to accomplish, it also didn't work correctly with the library I'm using :-P So I figure this will be a good compromise, and should still be flexible enough to eventually move to proper sharding.


As a side benefit, this design becomes nicely concurrent at a larger scale, not just between subsystems.


Earlier tonight I rewrote Lewdmilia to behave similar to how I want Monika and Sakuya to eventually behave, and so far the results are near perfect.


Time to Start!

Lewdmilia was sort of the start of my experimentation with how to rewrite Monika, and is also serving as the test for an abstraction library I started: remicord. This is where I'll put the common functionality, like the concurrent subsystem handling, and soon the new Client class and multiple bot instance handling. After this I'll port everything except the admin functionality of Monika over to remicord, then write the production version of Sakuya using it... then I'll actually start using them.


Now I just need to maintain the momentum ^_^


---------
Page served by Aya https://nanako.mooo.com/fossil/aya/
Aya is under the GNU Affero GPLv3 license

-- Response ended

-- Page fetched on Sun May 19 22:00:53 2024