In this talk, we will discuss the development of WishBot: a trading card game based entirely in discord. we will talk about some of the intricacies of building bots at scale, and how Directus can be used to manage users, collections, and trading.
Speaker 0: My name
Speaker 1: is Milt. As you already know, I'm going to be talking about directors in combination with Discord bots and in this specific case, a Disney themed trading card game. Very simple. Stage 1, the concept where I got into the project. It was a project which I took over from someone else.
It's a trading card game. It's Disney themed. It had to be a Discord bot because they just wanted it to be, very easy to market to a new user base and very easy to interact with. That's where the interaction base is coming in because in Discord that means not just sending a message but sending a interaction request command. So doing a slash saying I want something and giving some parameters to it and making it work and having it integrated with ranking sites.
So it shows up on the ranking sites, you vote for them, you get something in Discord using the bot, and the more you vote the more you get. And we had some early problems which were caused by the project. First of all, being, mainly hosted on a different site. Let's just not call them how they actually are. Fast code, let's say.
The problem was that the code was spread around multiple projects. They were all interconnected with each other. That caused problems because things were, defined multiple times in different ways, sometimes with differing definitions. So a is equal to b, but b also is equal to c. At the same time, a is 1 and b is 2.
And we had high response times because multiple projects interacting with another on, for some reason multiple continents as well. And multiple data sources was we had multiple Google Sheets which were used as databases. I think we know where that one is going the moment we start getting into multiple 1,000 entries per Google Sheet. So stage 1, consolidation, getting everything into one application, getting all the projects combined into 1, which still used, the same platform for it in the beginning, getting all the data sources merged into 1 as well, which later on made it a little bit easier exporting all the data and getting rid of those data sources. And we used a MySQL database as an intermediary before we started switching over the infrastructure to something called Sapphire, which is, a framework for creating Discord bots.
And here's where Directus comes in. We tried a lot of CMS systems and we wanted to have one thing. We wanted to have it quickly deployable. We wanted to have it highly adaptable. Easy to use, not only for administrators and developers, but for, creators, like creating new material inside the CMS, event based and cron based, automation, also known as flows, a REST and a GraphQL API, which was a requirement set by the owner of the project.
I just rolled with it. We never used it since we then just use the director's SDK for everything that, happened. And for everything that we didn't use it, we use the internal API. And then it had to have the option to be either self or cloud hosted depending on the scope of the project later on. Funny enough, if you start with self hosted, I heard it's quite easy to get a self hosted directors instance into cloud hosted?
Fairly easy?
Speaker 0: Oh, I'm not sure you can let me for validation. Sure. That sounds correct.
Speaker 1: It's easier than just, here, have a Google Sheet.
Speaker 0: I mean, you said you are real low. Yeah. We we, leapfrog leapfrog that.
Speaker 1: Yeah. And this code verification causes one big problem. It's you have to anonymize all the data. You have to follow GDPR, g yeah. GDPR.
You have a lot of restrictions when it comes to collecting data from a user. So, generally, you're only allowed to know their ID, and that's all. No name. No information about what they did, what they wrote. There's exceptions to it, and that means you can handle those information when an event happens and you're not able to save it outside of that event.
And infrastructure security, that's something that they recently added. They want you to essentially not allow anyone to access your bot on an administrative, capacity. Meaning, those servers that it's running on usually have to be secured by, for example, SSH with identity tokens instead of passwords. And funny enough, they actually checked it. So that's the quick rundown of the project and can actually look into it, how that, structure looks like.
Here I actually see the user database. A lot of gibberish.
Speaker 2: Could you switch the light theme?
Speaker 1: We can switch light theme. Yeah.
Speaker 2: It would be better for the projector.
Speaker 1: Now this is an older instance. Now I have to see where it was in here.
Speaker 0: Right there. Yeah.
Speaker 1: Right there.
Speaker 0: I believe so. If you scroll down. If it's an older you you should, just change your browser theme. It was it not user configurable? No.
What was it? It's a 10 by 4. It's not write that theme.
Speaker 1: There we go.
Speaker 0: Yeah. That's better.
Speaker 1: It's easier to be read.
Speaker 2: And now the database needs some time to load again.
Speaker 1: Apparently, I have a little bit of a bad connection with about 2 millisecond 2 seconds until database response. But, yeah, here we go. We have anonymized data. We know where user is, and we know who the user is, but only by ID, which is interesting because we have to handle things like a complete inventory in JSON. And all of those IDs in this case correspond to, where do we have it now?
Why is it not showing what's above there? Can have There you go. Useful. They have already one part of the bot, and that's the ability to break everything. That's the ability to get yourself a random card dropped.
In this case, the only thing that's actually random is the card being dropped. The value is always the same and it actually gains a little bit by liking it, having, yeah, people like, dislike, and delete their accounts. In this case, affecting the value because those are usually counted together. You have the ability to claim a card by clicking on a button. That then adds it directly into your inventory which is instantly reflected in direct us.
And you can actually view your collection of items, which is a little bit problematic at the moment. You have a larger inventory because all the information gets invalidated after about 10 minutes of the session being started. So for 200 cards, you might need more than 2 minutes to scroll through all of them. I think Kevin might already encountered that problem. So that one is keeping a server specific record of your inventory.
So you can have multiple inventories and multiple servers at some point. It was planned to have a global one but, Ono decided against it. And where directors starts to actually be more involved is by running the client command, and I hope the website still works because If we go where is it? It's like come here. You already claimed your reward.
Hold on. No. It seems that you haven't voted yet. I hate it. That one will send you to a different website, which then makes you vote for a bot, which sends information to, directors.
Funny enough in a format that is by default not understood by directors which means I had to include a custom hook which passes all the data and then sends it back to directors as JSON formatted.
Speaker 0: So
Speaker 2: log What are you voting for?
Speaker 1: Especially not especially. Essentially, for the popularity of that specific application.
Speaker 0: Oh, really? So that's like an external ranking of different Discord bots. And, obviously, bot creators want their bot to be not voted. So this is an incentive mechanism built into WishBot. But once every 12 hours, I think is how is how often you can vote here, you vote.
I assume it sends, yeah, it sends like a webhook with this archaic data format to digest. That's handled, and then you can claim a reward inside of WishBot for doing that every 12 hours.
Speaker 1: There you go. We have voted. And now that just to show that one, this data is actually present in here. And it has the time to live because, this k b storage is cleaning itself up. 300 3,600 seconds.
It's exactly an hour that this data is, living inside it. So it should be enough time for anyone to run a simple command and get their reward, which in this case is totally random. You get what you get. The drops, those are static, but the value that you get is random based on nothing but a number generator.
Speaker 0: And so this currency effectively in the game, the star
Speaker 1: that's I
Speaker 0: think it's called that star. You get that as a user, but that's also held on cards. So you have
Speaker 1: a value gap. You have value which you can use to essentially get more cards. So it is a self perpetuating cycle. You get more cards, you sell more cards, and you end up at some point with the ones that you would like. Like for example, Mickey Mouse.
Let's see. Yeah. I do have another currency and it's generating those cards currently based off list of 1,000 entries into database, which is including those images which are pulled off directors directly. So, yeah, that's about.
Speaker 0: And there's a trading mechanism.
Speaker 1: Yeah. That's a trading mechanism of for which I need a second user to actually initiate it.
Speaker 0: Well, we we all know there's a trading Yeah.
Speaker 1: Yeah. Any questions?
Speaker 3: So this obscure data format that you're getting from the the voting side. How are you handling that with the customer input? Or is that being done right with the flow?
Speaker 1: It's done with the flow, which is listening on all push requests that are coming in. And in that specific, case, I have to, I had to create a custom hook, which then allowed directors to pull any data which is not default JSON formatted and converted into the correct format. Let's see if I can act yeah. I can't pull it up here because that one doesn't have remote access to the machine that's running it.
Speaker 0: So Directus is handling the the voting and claiming mechanism. It's obviously handling user registration effectively. Hello. It's also handling, a user's collection and
Speaker 1: stuff
Speaker 0: like that. There's the trading mechanism which it manages. Is Is there anything else that we are missing in there?
Speaker 1: Actually, no. It's besides handling data. Directors is in this stage not having any additional part in it.
Speaker 0: There's also subscribing, you know, the same monthly that's not directors, though.
Speaker 1: To a degree, it actually is because directors has an internal record of everyone that ever subscribed. I just have to see if there's personal data on that one. So let me just Okay. If we don't scroll to the right, it's okay. So it's keeping a track of everyone that has subscribed within the last, 6 months and still has one active.
So it's essentially, clearing up that list almost every day. In this case, it's getting from Ko Fi an information about everyone that subscribed. It's only getting their email address though. So in this case, directors has to keep a record of the email address, which in this court, a user can use their own email address. And since they know what they subscribe for, they can select what they subscribe for out of a list.
I can actually show that one. For example not not activate the server sub. That shouldn't work because activate server sub was never activated. Jesus. Oh, yeah.
Here. You have to put in your email. And in here, you have a list of tiers which are available. And the combination of both is actually used to verify you as a user because Ko Fi doesn't know who you are on Discord. Same with Discord, they don't know who you are on Kofi.
Speaker 0: And you can only store from Discord, arbitrary user ID that means something outside of
Speaker 1: Discord is actually by default not even giving you the user's email address, and that would not help much because you most likely or not most likely but maybe didn't use the same email address for 2 services. So that would break the whole system. So in this case, they have to provide the data themselves, then select the correct tier, which is interesting for anyone to crack in the beginning because they don't know your personal data and they don't know you even subscribe because Kofi is not making that information public.
Speaker 0: That's an interesting workaround to I have a question.
Speaker 1: Yeah.
Speaker 0: I've built a Discord bot before. Terrible development experience. It's really, really rough. And it just lived as an express app.
Speaker 1: Yeah.
Speaker 0: Where's the bot running? Is it running as part of, like, the direct project or is it running elsewhere?
Speaker 1: It's running externally, on the same virtual machine as Directus, but not within Directus, especially because it's using Sapphire, which requires a ESM module. And, as I think, directors as a project is not set up to handle ESM. Maybe. At least not the version that I'm using here. It would just instantly, crash the moment I build it.
Speaker 0: Interesting. That's been one thing I I wondered about particularly is could you feasibly run a Discord bot entirely within
Speaker 1: within directors and users? But
Speaker 0: if you're not doing it here, we need we need, you know, talk about that possibility. So just curious.
Speaker 1: You would just have to have a flow which is handling the incoming data because Discord is sending it to one endpoint, so, like, an interaction, webhook endpoint. And then you essentially just send a fetch request back to Discord, and it does what you want.
Speaker 0: Yeah.
Speaker 2: Would you like to show how you create new cars? Because I've seen your use of, like, input groups. Maybe some, people get a sense of how the Reactors looks on the back end. Yeah. Always interesting to see how others create content.
Mhmm.
Speaker 1: Oh, nice. Hi, camera. What is this? Ah, there we go. The cards.
Speaker 0: So at the moment when you create cards, you do it. It is done here within the Data Studio?
Speaker 1: It's done within the Data Studio. Yes. The ID, we set it actually as something manual because in the beginning, we imported a lot of data, and we actually modified some IDs because we had duplicates and it's kind of hard to import data and have duplicate IDs which are the primary key. So I kinda let the creator of those cars handle it themselves rather than me handling more than a 1000 entries. Mhmm.
Name wish listed is something that is default to 20 and deactivated is, a feature that we use for essentially simulating misprints, like creating cards. Sometimes there's a mistake in the card, maybe in the image of it. It's getting marked as a misprint. But until it's getting marked as that, it's actually available. So
Speaker 0: And it doesn't stop being available afterwards. It just stops
Speaker 1: being distributed. It stops being distributed, which makes them kind of rare to get. Cards actually have a color in the sense of Discord messages, especially embed messages, always having a little bar on the left. We have a default color for that one. We never actually use anything else.
The image is stored within directors manually setting a value, and we actually have a larger list of movies and series where you can select your character to be from. We use that way because we don't want to enter that data multiple times with thousands of entries and rather have that one once. And a little description of the character which is shown on the embed as well. Value? Value is, how much the card is worth in terms of the bot's own currency called stardust.
Speaker 0: But you said it was randomized. Oh, no. It isn't. What's randomized? Are the claims Yes.
Speaker 1: The value itself is fixed. Otherwise, you wouldn't be having, high value cards which stay the same value. Because you could have the same card with the same ID multiple times. But what differentiates the one card with ID 1 and the other card with ID 1? Actually, nothing really.
Unless it has a difference, which means then it's not the same card anymore, which means a new ID is being created.
Speaker 0: Is there any limitations, placed by Discord on response times to the slash command. And is there anything interesting you've had to do to do with the
Speaker 1: 3 milliseconds? The initial response has to be done within 3 milliseconds.
Speaker 0: 3?
Speaker 1: Yes. Okay. 3 milliseconds initial response means you get a ping and you instantly send one back.
Speaker 0: It's breaking. Yeah.
Speaker 1: That's how much time they give you. That it's an ephemeral ready response.
Speaker 0: Yeah.
Speaker 1: It says 3 milliseconds in the docs even though it isn't.
Speaker 0: Yeah. Do but do you have let's say, I'd say slash collection. Right? And you're generating a list of 200 cards with images, and you're grabbing those from directors. Is how long do you have to respond to that before it will time out?
And then I suppose there's another interesting thing around, like, image, like, image sizes or optimizer. And you can't just say no. None of
Speaker 1: that is
Speaker 0: the case, but I have to believe there's some kind of limits there.
Speaker 1: From back when the bot was created, the limitation of response time was 60 seconds to any command. It's pretty long. Yeah. At the same time, if you use image generating AI and you usually take more than 60 seconds, you would have to consume the command and then send a response later from an external event because otherwise, it would just start breaking everything. And what was the other question?
Speaker 0: Image optimizations, whether there's anything
Speaker 1: first of all, we, from the start, created images with a small size because that is a self hosted instance. It means every image that is shown in Discord is actively being requested from that instance. It's even though it's in a data center, has a lot of bandwidth, it still starts to overload directors as a self hosted instance in that VM if I get more than a couple thousand requests a second because it can't handle it anymore. And we kind of figured out that, funny enough that's another thing but Directus breaks regularly. No.
Does it break now? I think it's going to break.
Speaker 0: Yeah. And what's what broke there? Do you know why?
Speaker 1: That's the one point. I can't tell you why because nothing shows up in the logs. It just stops loading. So it essentially times out trying to load that, not only that specific image, but all of them. Interesting.
But
Speaker 0: You you mean the video that I have the recording of and will take through an editing workflow before I
Speaker 1: Oh, yeah. Definitely. That's cool. That's cool. Nah.
Doesn't want to load it. It's actually not even loading the project image anymore. It shouldn't be.
Speaker 0: Yeah. Anyway, cool. Are there any other interesting, like, technical notes about how this was come how this was put together, usage of directors, and of course, any questions for that maybe? I feel like we're we're at that point.
Speaker 1: Right? You have no
Speaker 0: more planned content. Right?
Speaker 1: I have no more planned content indeed. Yeah. And there is actually nothing else in here anymore besides a little log of arrows and bugs that sometimes happen. But I think that the directors or that specific instance is at the point where it, doesn't want to cooperate anymore. Oh, actually, there are But most of them seem to be acknowledged.
So it's actually keeping a track of every error that happens within the Discord bot because the Discord bot is writing a log, but it's a rotating log. So this way I can keep track of them and actually connect them to the user that triggered it. So I can get get back to them, can figure out what happens. What happened if I go in here? Let's see what it is.
Ah, nice. That's the response from Discord, by the way.
Speaker 0: Yeah. The message is there. Come on. Yeah.
Speaker 1: And that's an interesting one. FaceTime. Interesting.
Speaker 0: Really interesting projects. I think a really novel use of directus. And one that I I certainly didn't see before this project and haven't
Speaker 3: database? I a database?
Speaker 1: I selected directors instead of a database because the creators don't necessarily have the technical lodge, knowledge to put in data manually into the database, let alone then put files on a file server, connect them to the database, and have that everything retrieved. This is making it very easy for me to administer, administrate, for me to develop for it, and for anyone creating content to add to it. At the same time, I can see logs about things that happen outside of directors.