Home > database >  Discord.js SlashCommandBuilder() : get option choices from a list in an async function
Discord.js SlashCommandBuilder() : get option choices from a list in an async function

Time:01-20

I'm trying to create a slash command /test3 with an option input that has choices. However, I'd like to get these choices from an async function, but the way I'm trying to do it isn't working.

Here is the code to generate a simplified version of my argument list, in tools.js

module.exports = {
    getArgumentList: () => getArgumentList()
}

async function getArgumentList(){
    return [
        {name: '1', value:'One'},
        {name: '2', value:'Two'},
        {name: '3', value:'Three'},
        {name: '4', value:'Four'},
        {name: '5', value:'Five'}
    ]
}

And the code for the command, in test3.js

const { SlashCommandBuilder } = require("discord.js");
const { getArgumentList } = require("../tools.js")

module.exports = {
    data: new SlashCommandBuilder()
            .setName('test3')
            .setDescription('Test command for commands with options.')
            .addStringOption(option =>
                getArgumentList()
                        .then(list => option.setName('input')
                                                .setDescription('The input to echo back.') 
                                                .setChoices(...list) )),           
    async execute(interaction){ 
        console.log('Testing')
    }
}

Here I get this error:

ExpectedValidationError: Expected
    at InstanceValidator.handle 
    ...
    at Module._load (node:internal/modules/cjs/loader:922:12) {
  validator: 's.instance(V)',
  given: Promise { <pending> },
  expected: [Function: SlashCommandStringOption]
}

Node.js v18.13.0

Would there be a good way of doing this ?

CodePudding user response:

The .then() function always returns a promise, so you can't set it as the return value of .addStringOption(). Using async/await would make this a lot cleaner and easier.

data: new SlashCommandBuilder()
            .setName('test3')
            .setDescription('Test command for commands with options.')
            .addStringOption(async option => {
               let list = await getArgumentList()
               return option.setName('input')
                   .setDescription('The input to echo back.')
                   .setChoices(...list)
            })

CodePudding user response:

Refactor your command system to have data be a function, and in your slash command deploy script (or wherever you used the data property before) await it, so you can then change data to be this:

module.exports = {
    data: async () => {
        // The list is fetched here (where we can use promises and async-await) before the SlashCommandBuilder gets made.
        const list = await getArgumentList();
        return new SlashCommandBuilder()
            .setName("test3")
            .setDescription("Test command for commands with options.")
            .addStringOption((option) =>
                option
                    .setName("input")
                    .setDescription("The input to echo back.")
                    .setChoices(...list)
            );
    };
}

Example slash command deploy script:

const commands = await Promise.all(
    client.commandRegistry.map((command) => (await command.data()).toJSON())
);

You can also pass a default SlashCommandBuilder to the data function now if you want:

module.exports = {
    data: async (b) => {
        // The list is fetched here (where we can use promises and async-await) before the SlashCommandBuilder gets made.
        const list = await getArgumentList();
        // no .setName() required anymore! Deploy script set it for us.
        return b
            .setDescription("Test command for commands with options.")
            .addStringOption((option) =>
                option
                    .setName("input")
                    .setDescription("The input to echo back.")
                    .setChoices(...list)
            );
    };
}
const commands = await Promise.all(
    client.commandRegistry.map((command, key) =>
        (await command.data(new SlashCommandBuilder().setName(key))).toJSON()
    )
);
  • Related