scrap ttt

This commit is contained in:
jacob
2024-03-15 13:26:47 -05:00
parent f2554174ce
commit 289dbff4e3
5 changed files with 0 additions and 349 deletions

View File

@@ -1,43 +0,0 @@
---
title: command
sidebar_position: 2
---
import GuideFeedback from "../../../../src/components/GuideFeedback";
# Commands
## Example command
- View the command **ping.js** in `src/commands`.
- Make a new file next to **ping.js** called **tictactoe.js**
- Copy **ping.js** contents into **tictactoe.js**.
We base **tictactoe.js** from **ping.js**
:::tip
The name of your command will be the name of your file.
:::
Each slash command will follow this similar structure.
In this tutorial, maybe you were smart enough to guess, but we'll be making tictactoe!
## New command, tic-tac-toe
- Instead of **CommandType.Both**, `type` property should be **CommandType.Slash**
- This is to keep it simple. You'll see later, but slash commands work well with message components.
- Give it a description.
- run `npm run commands:publish`
- Your command should be usable on discord now!
### Result
Your command should now look something along the lines of this:
```js title=./commands/tictactoe.js
export default commandModule({
type: CommandType.Slash,
description: "I do tictactoe.",
execute: async (ctx) => {
await ctx.reply("Pwease wait. dis command in pwogwess"); // 👻
}
})
```
---
<GuideFeedback />

View File

@@ -1,106 +0,0 @@
---
title: Logic
sidebar_position: 5
---
import GuideFeedback from "../../../../src/components/GuideFeedback";
## Game rules
- Two players max.
- Each player is assigned **X** or **O**
- The first person to get 3 in a row of their letter wins.
Get the option selected, adding this to the top of your **execute** function:
```js
const opponent = ctx.options.getUser("opponent");
```
:::warning
Since we are in a Slash command, the above is valid. If we were to interoperate between Text Commands and Slash Commands,
we'd have to check if the current [Context](https://sern.dev/docs/api/classes/Context) is the correct type. In our current setup,
this won't happen since we did not set our command type to **Both**.
:::
We need to ensure we're not playing against **an invalid user**. Invalid users are those that
- are bots **or**
- ourselves.
Create helper function **isInvalidUser** which returns a boolean satisfying the conditions above.
```js
const isInvalidUser = (user) => {
return user.id === "182326315813306368" || user.bot;
}
```
:::tip
You can easily access anyone's user id by [enabling dev mode](https://beebom.com/how-enable-disable-developer-mode-discord).
:::
Cool, now let's call it, preventing invalid users from being opponents. Put the code snippet before any of the UI logic:
```js title="tictactoe, execute function"
if(isInvalidUser(opponent)) {
return;
}
```
We **return**, or **stop** execution of the function if the condition `isInvalidUser` is true.
:::info
A step further would be extracting `isInvalidUser` into a separate plugin, if multiple commands share this same logic.
:::
Before we go any further, let's test. Start up the bot again.
- Try running `/tictactoe` with a bot.
- What happens?
- It may be beneficial to give the user some feedback instead of a **return**.
- Try running `/tictactoe` with yourself
- What happens?
- It may be beneficial to give the user some feedback instead of a **return**.
- Try running `/tictactoe` in a DM channel with the bot itself.
- What happens?
Great! notice how in a DM channel, the command is entirely **useless**.
Let's use our **channelType** plugin to ensure this doesn't happen.
- Add the **plugins** field to your commandModule, adding the channelType plugin.
```js
plugins: [channelType( )]
```
- Call it with the right parameters.
- All plugins come with documentation in the same file
- In our case, we need to supply an array of ChannelTypes, and an optional message.
```js
plugins: [channelType([ChannelType.GuildText], "You cannot execute this command here.")]
```
:::info
You can publish commands to specific guilds using special configuration.
For more information, view the docs [here](https://sern.dev/docs/cli/publish).
For educational and simplicity purposes, I'm using a plugin here.
:::
Great! When we rerunning these test conditions, it should catch and respond to these conditions.
## Collectors
We know how to display components, but what about responding to them?
Discord.js gives us the Collector API, which at its core, listens to incoming interactions from discord.js. It essentially comes in two flavors.
- **await**
- **callback based**
**Await** is [easy](https://www.infoq.com/presentations/Simple-Made-Easy/), but it comes at the cost of only collecting `one` at a time. Also,
it is blocking on other commands attempting to be run; When we await a component,
All other commands will be waiting as well. So, we'll be using callback based, lazily waiting. Let's create a collector to the message sent by the bot. How do we get that message?
Luckily `ctx.reply` returns the [Message](https://discord.js.org/#/docs/discord.js/main/class/Message) sent. Let's capture that in a variable.
- Capture the result of ctx.reply in a variable called **resultMessage**.
```js
const resultMessage = await ctx.reply({ message, components: grid });
```
- Create a collector from the message's property `.createMessageComponentCollector()`
- Accept only interactions of type **Button** (we are making a tictactoe game of solely buttons),
- Make sure to import **ComponentType** from discord.js
- Give it a time limit of 60_000 milliseconds aka 1 minute.
```js
// verbosity!! //ignore this
const collector = responseMessage.createMessageComponentCollector({
componentType: ComponentType.Button,
time: 60_000
});
```
---
<GuideFeedback />

View File

@@ -1,54 +0,0 @@
---
title: plugins
sidebar_position: 3
---
import GuideFeedback from "../../../../src/components/GuideFeedback";
# What are plugins?
## Code ran before a command to modify output.
sern has a bunch of premade plugins thanks to our awesome community, which you can install via the cli!
## Forward:
- Run: `sern plugins`, selecting the **channelType** plugin
- Thank the creator(s) of the plugin. (Thank you)
- Import your plugin at the top of the file like this
```js
import { channelType } from '../plugins/channelType.js'
```
Once you download the `channelType` plugin, You may continue.
## Aside:
### How to use plugins?
Lets take an aside to show how to install plugins.
- Run: `sern plugins`, selecting plugins
- Thank the creator(s) of the plugin. (Thank you)
```js
import { commandModule, CommandType } from '@sern/handler'
import { channelType } from '../plugins/channelType.js'
export default commandModule({
type: CommandType.Slash,
description: "haliburt",
// here:
plugins: [channelType([ChannelType.GuildText], 'This cannot be used here')]
})
```
Keep in mind some plugins cannot run for every type of interaction sern handles.
For example, the **channelType** plugin will probably not work in a modal, and if you are using typescript,
the type checker won't allow it. However, there are some more generic plugins, one being **fromCallback**, which can
run in any command / component.
### How to contribute plugins?:
- view [here](../../../guide/walkthrough/plugins).
---
<GuideFeedback />

View File

@@ -1,24 +0,0 @@
---
title: intro
sidebar_position: 1
---
import GuideFeedback from "../../../../src/components/GuideFeedback";
> How hard can this be?
This will probably be the longest chapter in the guide. Don't worry though, once we're done, making commands is [**fodder**](https://www.merriam-webster.com/dictionary/fodder).
Here are some inspirational quotes to make you get motivated.
> "If you don't make mistakes, you're not working on hard enough problems." ― Frank Wilczek
> "Dwell on the beauty of life. Watch the stars, and see yourself running with them." ― Marcus Aurelius, Meditations
> "Never let the future disturb you. You will meet it, if you have to, with the same weapons of reason which today arm you against the present." ― Marcus Aurelius, Meditations
If you want to add more, feel free to pull request this, but not too many please!
TODO!!! - ADD TIC TAC TOE GIF HERE
---
<GuideFeedback />

View File

@@ -1,122 +0,0 @@
---
title: UI
sidebar_position: 4
---
import GuideFeedback from "../../../../src/components/GuideFeedback";
## Message Components
Discord has a rich api for developers, and they allow bots to use and interact with certain UI components.
Some of these include
- Buttons
- Modals (technically not a component)
- Select Menu
- Action Rows
Let's use the mush we call [**brain**](https://www.ninds.nih.gov/health-information/public-education/brain-basics/brain-basics-know-your-brain).
> **Question**: What components do we need to create a tic tac toe game?
> **Answer**: Buttons!!!!
### Rules
Obviously we can't spam the user interface with lots of components; Discord knows this as well.
Per the [**discord api documentation**](https://discord.com/developers/docs/interactions/message-components#action-rows):
- You can have up to 5 Action Rows per message
- Maximum of **5** components in an Action Row
- An Action Row cannot contain another Action Row
- Select Menus take up an entire Action Row, and cannot have buttons
Do the math, and we can have up to 5 (action rows) times 5 (per row), 25 buttons in one message
### Custom Ids
We also need some way to distinguish buttons between other buttons. Whenever a user clicks a button, we are notified that **some** interaction has happened, but from **where?** Custom ids to the rescue. Think of these like 'classes' or 'ids' on html tags. **Each component that we need to handle
will have to be retrieved by their custom id in order to properly handle it**
We'll get more into this in the next chapter.
### Design
Our UI will contain a message, and a total of 9 buttons, laid out in a 3x3 grid.
TODO!!! PICTURE HERE
### Implementation
How do we represent this in code? In discord.js, the library we're using, we can represent this via their builder api. Let's create some utility functions before diving straight in.
```js title="tictactoe.js"
const EMPTY = "\0"; // A little trick to show buttons with no 'label'
const button = (custom_id, label=EMPTY) => {
return new ButtonBuilder({
custom_id,
label,
style: ButtonStyle.Secondary
});
}
const row = (...elements) => {
return new ActionRowBuilder().addComponents(elements);
}
```
The names for these are pretty self explanatory. **EMPTY** is a placeholder for labels in a button.
In our **execute** function, let's build the Action Rows.
```js title="tictactoe.js, in the execute body"
const message = "Tic Tac Toe";
//Our TicTacToe grid. consists of 3 rows, and in each row, 3 buttons.
//Each button has a unique 'customId' of a number.
const grid = [row(button('0'), button('1'), button('2')),
row(button('3'), button('4'), button('5')),
row(button('6'), button('7'), button('8'))];
await ctx.reply("Pwease wait. dis command in pwogwess"); // 👻
```
> Question: Does this grid satisfy the rules stated above?
> Answer: Yes! We have 3 Action Rows in total, with 3 Buttons in each. Less than 25 Components!
Let's try displaying this to Discord. We're not just displaying plain text anymore, we need to change ctx.reply.
- Change how we call `ctx.reply`. Give it an object instead:
```js
// await ctx.reply("Pwease wait. dis command in pwogwess"); // 👻
await ctx.reply({ content: message, components: grid });
```
One more thing, we need to play against someone, so we'll need to update our commandModule.
- Add the import **ApplicationCommandOptionType** from discord.js
- Add a new **option** of type **User** to the **options** field of your commandModule.
This will display on the user's end as an option to choose from.
```js
options: [
{
name: "opponent",
description: "Opponent you would like to play with",
type: ApplicationCommandOptionType.User,
required: true,
},
],
```
- Run `npm run commands:publish` once more to update it.
:::tip
Anytime we add a new slash command or its options, its a good idea to run `npm run commands:publish`
:::
Run your bot.
- Congrats! You got something to display.
Notice how when you try to click, it will say something like `interaction failed to respond`.
**Next chapter**, let's wire everything up.
---
<GuideFeedback />