Statefulness and data sharing for Pieces
Hi,
let's imagine the following: you want to keep track of some data about the people who join and leave a certain voice channel. It would be convenient to store this data alongside the listener and maybe provide an interface to it directly from the listener.
However, it's not fully clear how and when Pieces are instantiated. There are no examples of holding (let alone sharing) any state. How to do this right?
1. is it okay to hold state in a Listener instance?
2. if so, how to access the very same instance somewhere else?
3. if not, what to do instead? Exposing all that data via the container sounds quite painful because that's very far from encapsulated. What would you do to expose some data managed by a certain event handler?
Solution:Jump to solution
use a database like sqlite, postgres, mongo, etc. Especially if you want to retain it between reboots. Otherwise, remember how DJS and sapphire do caching internally:
```ts
// store.ts
import { Collection } from 'discord.js';
import { container } from '@sapphire/framework';...
33 Replies
Solution
use a database like sqlite, postgres, mongo, etc. Especially if you want to retain it between reboots. Otherwise, remember how DJS and sapphire do caching internally:
as for when listeners are instantiated, once, before client login.
Whether to use a database or not should also rather be an implementation detail; allowing anyone to just grab the database would be a lot like shared global variables
"Worst case" that should still be a container service but it really would be best to keep this the business of those 2 or 3 classes inside the same "module" (registered path)
Most people just attach properties to container (accessible in pieces with
this.container
and importable elsewhere
For example, I have this code in my main function
And then I can access redis from calling this.container.redis.whatever
, same for cardCacheYeah well, this is the point: this is better than nothing but in a sufficiently complex bot - which this will become over time - you wouldn't want to globally expose things that are only related to one functionality
In discord.py, this is a no-brainer because the whole functionality could be one cog with its shared state
I started thinking that listeners of the same name might even override each other, regardless that they have different paths. I wouldn't know the implementation but it's too suspicious
that is correct because we use a Map to store entries which are keyed properties, ergo keys have to be unique
MDN Web Docs
Map - JavaScript | MDN
The Map object holds key-value pairs and remembers the original insertion order of the keys.
Any value (both objects and primitive values) may be used as either a key or a value.
have you checked out https://sapphirejs.dev/docs/Guide/additional-information/implementing-a-discordpy-like-cog-system/ ?
Sapphire Framework
Implementing a discord.py like Cog system | Sapphire
Developer who come from the Python ecosystem may be familiar with the "Cog ↗️" system that discord.py has
also you say your bot will be "sufficiently complex" but I actually no joke dare you to hold up a lens against @Skyra which still does exactly as I described https://github.com/skyra-project/skyra
GitHub
GitHub - skyra-project/skyra: A multipurpose Discord Bot designed t...
A multipurpose Discord Bot designed to carry out most of your server's needs with great performance and stability. - skyra-project/skyra
yes, and it is really close to a trap. You wouldn't want name collisions for things you intend to be completely unrelated components
just name the files better :meguFace: ? sounds like a skill issue tbh lol
Okay, this is cheap now. If anything, it is a skill issue to not distinguish same file names coming from unrelated components
it aint hard to name a file
playMessageCreate
for audio and pointMessageCreate
for level up systemwhy even have directories if you still gonna hardcode the feature into the file name
aside from the fact that those are horrible examples because audiobots are DOA and level up systems are a dime a dozen
categorization
if you hardcode the category into all names, there isn't much to categorize
and this is not even hard to implement better, I actually created a custom class builder that encodes the folder structure into the name...
talking about skill issue
categories arent hardcoded
also it should be noted that Sapphire supports fs-less pieces where piece names are defined through config only
but to silently do the thing that you would basically never want...
You're the only person among many to complain about this tiny detail so I wouldn't say never
then perhaps it was about time
🤷♂️ very unlikely to change plus if we would change it, it would be a breaking change (semver major)
it's the first step to improvements
but you're welcome to make an RFC issue 🤷♂️
anyway, now that I caught this gotcha and have a fix
is there a way to keep the data of a certain feature more local than the container but less local than the same class?
Basically the same as before but export the constant and import it elsewhere?
I have one other idea but I doubt you'll like it.
Extend sapphire's Piece class and add a property to it. Call it SpecificPiece for example. Then create SpecificAliasPiece, SpecificCommand and SpecificListener so you can access the custom property down the line.
But that's a LOT of structural overhead
(by the way, this codebase still doesn't seem to organize separate features into any sort of unit, it is a big chunk of DJS handlers dressed up into OO syntax in this regard)
Correct on the first part. We don't use cogging for skyra. We never had a use case for it.
Long ago only audio was split off before we removed it entirely but that was moreso to localise the code and not to localise something like you want
As for the second part, that's what any bot is really
Well any OO bot
to be frank, I don't like this confusion between hard singletons (like a cached package) and object instances that happen to be the only one of their kind
That's basic OO with dependency injection ¯\_(ツ)_/¯.
You'll also see it in other big frameworks like Spring Framework for Java and I think .net core does something like it too
Fwiw spring and .net are like the poster children of OO
In Java, this dichotomy doesn't really exist. One file is one class. The static parts are plain singletons
Have you used spring though. Raw java is not comparable at all.
you would never import or export a variable
Anyway Im driving now
I haven't used Spring but I have used some ASP.NET Core and I'm pretty sure it's not like this. Everything is instantiated and passed around.