Switching to TypeScript
Hello once again everyone! Last post I finished the final Dovahzul bot, therefore this week I will be starting an entirely new bot. Pokémon-TRPS became a more ambitious bot than I had expected, so there is much information to share. Rather than sticking with JavaScript, I made the executive decision to switch to TypeScript. Since TypeScript is a superset to JavaScript, I figured the change would not be too significant. There are three main reasons for this change:
Reason 1: Another project I am working on uses TypeScript, thus it would make more sense for me to continue only learning TypeScript to not cause further confusion by learning both at the same time.
Reason 2: TypeScript can be written in a more object-oriented style further differentiating itself from python.
Reason 3: Since significantly less resources exist for writing discord bots in TypeScript, I believe this blog will now provide more useful information.
Thanks to TypeScript, a class I created called "Bot" now extends all discord.js client functionality.
Designing Pokémon-TRPS
As mentioned in a previous post, Pokémon-TRPS is a more sophisticated form of Rock Paper Scissors. Instead of having three choices, the player chooses between 17 different types. Types are strong, weak, or immune against other types. All types properties are shown in the type chart below. The green squares indicate that the attacking type is strong against the defending type. For Pokémon-TRPS, I will only consider these green strengths. The player that selects a type strong against another player type wins, much like rock-paper-scissors.
|
| Type chart from https://pokemondb.net/type |
Compared to Dovahzul's one command to translate, Pokémon-TRPS has five unique commands.
- play - create a new game with another player on the server
- playcpu - create a new game with the bot
- type - show type information (strength, weakness, immunes, reaction emoji)
- types - show all possible types
- games - show list of active games
Project Pipeline Changes
In light of TypeScript, some changes occurred to the project structure and general dependencies. Luckily discord.js automatically includes TypeScript tpyings, therefore no additional typing modules are required. The typings are yet another plus to chose Node over Python and Java. The main change was the inclusion of a type script config file and additional compiler options. All the code for this bot is available on my GitHub. (See link in results section) The property "experimentalDecorators" needs to be true for my implementation of the command design pattern.
|
| tsconfig.json |
Besides TypeScript, another dependency I installed was ts-node. This dependency let TypeScript compile and run with a single npm script all within nodemon.
nodemon --exec ts-node src/App.ts -e ts
Command Design Pattern
Given the larger amount of Commands, I decided to utilize the command design pattern.
|
| CommandManager.ts |
All the commands are loaded from a command directory, and have the Command decoration provided by the CommandManager. This decoration tells TypeScript to add the command field to the given
Type Mapping
|
| lines 198 - 204, Game.ts |
Given 17 Pokémon types with each having their own sets of strengths and discord reactions, I included a few helper methods in the Game class to retrieve type data. I stored the type data as a JSON string originally, but later I switched to a regular JavaScript object. Either way would work, however I decided to save myself from parsing JSON.
|
| lines 310 - 329, Data.ts |
I mapped a Pokémon Type data object containing the information for each type to a string representation. The mapping is essential for quickly retrieving the data for the correct type. Otherwise, the bot needs to search for the data in a more algorithmic manner.
|
|
| lines 303 - 308, Data.ts |
One additional type, "missing", was added in the event a user or the bot attempts to reference a type that does not exist.
Instanced Games
So how can multiple games persist simultaneously across the same Discord text channel? Or let alone entirely different servers? Well besides most methods being asynchronous, I had to create a list to store all the active games. Each Game also has a randomly generated unique id for differentiation.
Reactions
Now with instanced games working, the final piece of the puzzle is reaction collection. Bot's need to create a "reaction request" to send to Discord's API, and unfortunately this often causes massive slowdowns. Embedded Discord buttons may have been more performant. Reactions are pushed onto the message using the client's react method.
|
| lines 157 - 166, Game.ts |
Before creating a reaction collector, I declared a filter function which filters out reactions from users not participating in the game.
|
| lines 54 - 58, Game.ts |
Discord.js provides a createReactionCollector method for Message objects which enables the tracking of reactions. The filter function as well as other information such as time may be supplied to the creation method. When the message reaches the time limit from the creation of the collector, the collector's "end" event fires. The collector's "collect" event triggers after any added reaction that successfully passes the filter. The "end" event may also be triggered by the collector's stop method, which I actually utilized for ending the game prior to the time limit.
|
| lines 63 - 81, Game.ts |
Once both users selected reactions, the onReactionCollectorEnd method gets executed as a callback for the collector's "end" event. In onReactionCollectorEnd the types are compared and the game declares a winner or a tie.

















I think this is neat
ReplyDeleteNice to see you using typescript, I honestly don't see it often so its a nice change. Also, out of most of the blogs I've seen in our class you are one of the ones who put in the most work so good job. I can barely understand all the types weaknesses/strengths when playing the actual games so I can't say I'm necessarily stoked for a game focused only on that... but maybe it would be good practice!
ReplyDelete