#dynamic select menu handler
1 messages · Page 1 of 1 (latest)
oh boy, well what exactly are you looking for?
Really any working example beyond those two files. If I can see it in the full context of a repo, I think I can reverse engineer it or at least get past what's stumping me on the guide
i can write up a super quick sandbox box i guess
it's actually still exactly the same, if you'd tell me specifically what you want to look at, i could give you the pointers
Sure, let me push something to my repo
I have a menu command that I'm not sure how to incorporate into the events
ah i get the confusion
so what you need to understand is that select menu presses emit interactionCreate, with a select menu, right?
you already have an interaction create handler
https://github.com/LaureiVarju/Zapros-bot/blob/nicholai-deserves-better/events/interactionCreate.js
what you should do is before checking if (!interaction.isCommand()) return;
check if it's a select menu and run that logic there
now do you want to also split up different files for your different menus as well?
so the control flow here is:
- deploy-command.js sends out commands to the target server
- index.js fires up ready.js and collects all events and commands.
- A valid command occurs on a server, which fires off to interactionCreate.js ..... and then.... that's where I get confused
if( interaction.isSelectMenu()) {
// select menu process
} else if (interaction.isCommand()) {
// slashie process
}```
if you want to split up your menu handlers, the guide doesn't have that
Is 1-3 right so far?
yes you're not wrong at all
but do you think that select menus get registered as commands? just asking if you misunderstood that
No I think in this example /menu is the command, and when the menus themselves get interacted with um, they fire off something, that manages to reach interactionCreate.js, but after that I get very confused.
Thank you for your patience by the way, when you don't know enough to form the right questions, it's a struggle to communicate
yeah i understand that, let me take a look at the repo a few minutes later, then i'll come back to this
A selectMenu fires the interactionCreate event. As I mentioned earlier you need to check if isButton(), isSelectMenu(), isCommand(), depending on what you want
Where is that handled though? Like literally which existing .js file would I need to incorporate that logic to keep with this architecture pattern in the guide?
Do I need to create a menu.js file in /events? Or should I be adding this to interactionCreate.js?
In the interactionCreate event. If the inreraction is a command, interaction.isCommand() will return true, if its a menu, interaction.isSelectMenu() returns true
And so on
as this
^^
menu is the one that's out of place in your code
If you want it to divide it into different files once again, you can "copy" (adapting it to your needs) the code used to the event handler or the command handler
let's get this straight
menu, is a command, right?
yes /menu is a command
then what's it doing in the events file?
There's one in the commands folder too. I started putting something in events as well because I thought that's where interactionCreate.js would be reaching out to next. I don't fully understand how interactionCreate.js works
understood
so when you interact with the bot specifically, like with slash commands, select menus and buttons sent by the bot, it creates an interaction
and that emits interactionCreate
so with that out of the way let's go through your command handler
client.commands = new Collection(readdirSync('./commands').filter(isJs).map(path => {
const cmd = require(`./commands/${path}`)
return [cmd.data.name, cmd]
}))
so you are reading every single js file in this commands directory and then adding them in what is basically a javascript Map right? and attaching to your client
we did this to use it later on
now the structure for this is Collection<CommandName, CommandData>
name: 'interactionCreate',
async execute(interaction) {
if (!interaction.isCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
console.log("in the try block of interactionCreate")
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
},
``` here what you do is you check if the command the user initiated `interaction.commandName` is indeed a command you locally stored
and if so, you run it's .execute function
and the execution function is what's in the
async execute(interaction) { }``` block in the command's file, yes?
yes exactly
notice that we used .commandName, the unique commandName in order to identify which command we need to handle
custom ids, is what we'll use for selectmenus as their unique identifiers
first, prepare your code to handle both select menus and slashies, since your current code can be harder to follow
if( interaction.isSelectMenu()) {
// select menu process
} else if (interaction.isCommand()) {
// slashie process
}
``` move to this first
so I'm putting that code in the execute(interaction) {} block of ./commands/menu.js yes?
nope, we're putting this in our interaction create handler
/commands/menu.js is a command, we're dealing with interactionCreate the event
sorta but notice the interaction.isCommand at the very beginning
splendid
❤️
so what, now you want to dynamically execute select menus?
But how will I eventually differentiate between different commands? They all get handled in here?
different slashies?
yeah we use commandName as the unique identifier as i've mentioned
I think so? Let's go with yes
okay so this is not covered in the guide
as i've said, identifiers, with slashies, there are commandNames
with events there are.. well the event names
and with menus there are custom ids
so let's think of a structure first
make a select menus folder first and make a random menu file, like selectmenus/testMenu.js
on it!
okay so now let's look at the structure for an event handler for reference
module.exports = {
name: 'ready',
once: true,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
},
};
so we export an object, where we have a
once, which we can count as some optional data and not care for now
an execute, which is the function that gets called, this is the handler itself pretty much
and name, this is how we know this is the ready file, this is as i've been saying for so long, the unique identifier ™️
now all we need to take from this is the unique identifier and the handler
and for menus, that would be customId
and then for the handler we can have an execute function
so the structure for each of the files in the SelectMenus folder is...
{
customId: id,
execute() {}
}```
are we clear up to this point?
I believe so
wanna push your changes to a temporary branch for now?
push some temporary data to the file as well
whatever customId you may want and an execute that maybe replies with hello
Okay I added some data to testMenu.js
see if I'm on the right track
or were you expecting a different format
?
why's your selectmenus/testMenu.js empty?
i'm telling you to push some dummy data to the file
// testMenu.js
module.exports = {
customId: "someid",
execute(interaction) => {
interaction.reply("hello")
}
}```
alright dumbed it down
one that follows the menu handler structure we've established
okay so you see how js client.commands = new Collection(readdirSync('./commands').filter(isJs).map(path => { const cmd = require(`./commands/${path}`) return [cmd.data.name, cmd] })) we attach these to client.commands?
we'll pretty much just copy this entire code
but assign it to client.selectMenus
this is in your index.js btw
so I'm creating a block that reads:
client.selectMenus = new Collection(readdirSync('./commands').filter(isJs).map(path => {
const cmd = require(`./commands/${path}`)
return [cmd.data.name, cmd]
}))```
and adding that to my index.js file as well?
amazing
changes pushed
but
return [cmd.data.name, cmd]
```we used the command name as the unique identifier right?
but select menus don't have names, what do we use as the identifiers for select menu handlers?
😮
that's correct 👏👏👏
so now we're done pushing the data to a local collection
now move to interactionCreate.js
} else if (interaction.isCommand()) {
// slashie process
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
console.log("in the try block of interactionCreate")
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
}
``` okay so the process as i've explained
we receive the interaction, we search `client.commands` for the `commandName`, the *unique identifier*
then we call execute basically
now i want you to write the entire thing for menus by yourself
no you didn't change cmd.data.name to use customids
[cmd.customId, cmd]
the structure is like so
[identifier, handler]
you got amazingly close, but there's no .data, 😛
client.selectMenus = new Collection(readdirSync('./selectmenus').filter(isJs).map(path => {
const cmd = require(`./selectmenus/${path}`)
return [cmd.customId, cmd]
}))
``` ?
👍
now on to this
yessir
there is interaction.customId for menus
so inside of here:
//interactionCreate.js
if (interaction.isSelectMenu()) {
// select menu process
} else if (interaction.isCommand()) {```
I can start to search for the custom id of the menu event coming through?
yes buut it's still the interactionCreate event, there's no menu event, i'm picky because it may lead to misunderstandings 👀
No I appreciate the pickiness, a lot
I appreciate all of this
so, so much
module.exports = {
name: 'interactionCreate',
async execute(interaction) {
if (interaction.isSelectMenu()) {
if (interaction.customId == "someid") {
// do something here?
}
// select menu process
} else if (interaction.isCommand()) {
// slashie process
....
...
well yes but no
you want to dynamically execute this, check interaction.client.selectMenus if the customId is included
the same way you did here
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
// interactionCreate.js
module.exports = {
name: 'interactionCreate',
async execute(interaction) {
if (interaction.isSelectMenu()) {
const command = interaction.client.selectmenus.get(interaction.customId);
if (!command) return;
try {
await command.execute(interaction);
}
// select menu process
} else if (interaction.isCommand()) {
?
hmmm
yep, all okay 👍
congrats you just set up a dynamic menu handler
now to test it
i believe you have a menu command, right? make it send a menu where the customId is someid
// ./commands/menu.js
const row = new MessageActionRow()
.addComponents(
new MessageSelectMenu()
.setCustomId('someid') // like this, Ben?
.setPlaceholder('Select a dungeon')
.addOptions([
{
label: 'DOS',
description: 'De Other Side',
value: 'DOS',
},
{
label: 'HOA',
description: 'Halls of Atonement',
value: 'HOA',
},
...```
🧑🍳🤌
heheh
now send it, and then click it
uh oh
time for debugging
in here
if (interaction.isSelectMenu()) {
// in here
```you should log a few things, interaction.customId, interaction.client.selectMenus
if (interaction.isSelectMenu()) {
console.log("In the interaction.selectMenu() is true block") // yeah?
const command = interaction.client.selectmenus.get(interaction.customId);
if (!command) return;
try {
await command.execute(interaction);
}```
yeah better not log it as a string but as an object log(string, object)
congrats!
now you can have different files for your different menus
also before we wrap this up
rename this thread to "dynamic select menu handler" please, that way people looking for the issue may find it helpful and we're done 🥳