'Create a slash command choice for every mongoDB entry with the ID
Basically what I'm trying to do is create a choice within my SlashCommanderBuilder for every entry of my mongoDB database with the id which is returned. The entries of my database look like the following:
id:"test"
title:"Test Embed"
description:"This is a test embed."
link:"test"
So in my code below, I need to retrieve all entries from the database (which must be asynchronous) and then use each entry in a for loop to set the choice. The issue I'm having is my SlashCommandBuilder isn't asynchronous, and surrounding the .find inside an asynchronous function renders the data unusable outside the scope. I fear the structure of how I'm storing these commands isn't playing nicely with what I'm trying to achieve.
const { SlashCommandBuilder } = require("@discordjs/builders");
const { MessageEmbed, MessageActionRow, MessageButton } = require('discord.js');
const { ROLE } = require('../../../config/roles.js');
const config = require('../../../config/config.json')
const channel = require('../../../config/channel_ids.json')
const addfeatureSchema = require('../../database/models/addfeatureSchema.js');
let thing;
async function getAThing() {
thing = await addfeatureSchema.find();
return thing;
}
// Set Role.<Role> if this command should be restricted to a specific role. Refer to /config/roles.js for the enums
const allowedRoles = [ROLE.Owner];
const data = new SlashCommandBuilder()
.setName("editfeature")
.setDescription('Edit a feature embed')
.addStringOption(option => {
option.setName('id')
.setDescription('ID')
.setRequired(true)
getAThing().then(() => {
for (const record of thing) {
option.addChoice(record.id, record.id)
}
})
return option;
})
async function run(interaction) {
const client = interaction.client;
}
module.exports = {
allowedRoles,
data,
run
};
For further reference, I've included how the command files are handled in the index.js.
async function refreshCmds() {
fs.readdirSync(`${config.command_dir}`).forEach(group => {
const commandFiles = fs.readdirSync(`${config.command_dir}${group}`).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const command = require(`${config.command_dir}${group}/${file}`)
commands.push(command.data.toJSON());
client_commands.set(command.data.name, command);
console.log(`Loaded command file: ${group} -> ${file}`)
}
});
try {
console.log('Started refreshing application (/) commands.');
await rest.put(Routes.applicationGuildCommands(client_id, guild_id), { body: commands });
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
}
async function handleCommand(interaction) {
if (!interaction.isCommand() && !interaction.isButton()) return;
const command = client_commands.get(interaction.commandName);
if (!command) return;
if (command.allowedRoles && command.allowedRoles.length != 0) {
if (!interaction.member.roles.cache.hasAny(...command.allowedRoles)) {
const Error1 = new MessageEmbed()
.setColor('RED')
.setDescription(`You do not have permission to run this command.`)
await interaction.deferReply({ephemeral: true});
await interaction.editReply({embeds: [Error1], ephemeral: true})
return;
}
}
try {
await command.run(interaction);
} catch (error) {
console.error(error);
// await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
const executionErrorEmbed = new MessageEmbed()
.setColor(config.color)
.setTitle('Uh oh...')
.setDescription('There was an error while executing this command. **Please contact <@279334901000699904>**.');
await interaction.reply({content: ' ', embeds: [executionErrorEmbed], ephemeral: true})
}
};
client.once("ready", () => {
refreshCmds();
client.on('interactionCreate', async (interaction) => {
handleCommand(interaction);
});
console.log("Online");
});
Any help is greatly appreciated and I'm happy to alter my file structure accordingly.
Solution 1:[1]
Not a full answer but here is my suggestion :
thing is your collection of option records and addfeatureSchema.find() will return a promise. So instead of doing the asynchronous call in the SlashCommandBuilder why not declare your new slash command inside the promise then() method where you can access all your records ?
You could then use the addChoices() method instead of a loop of addChoice() calls to add all the choices at once with an array.
something like
addfeatureSchema.find().then(thing => {
// prepare you choices array before
const choicesArray = [];
for (const record of thing) {
choicesArray.push([record.id, record.id]);
}
const data = new SlashCommandBuilder()
.setName("editfeature")
.setDescription('Edit a feature embed')
.addStringOption(option => {
option.setName('id')
.setDescription('ID')
.setRequired(true)
.addChoices(choicesArray)
});
}).catch(error => {
console.log(error);
});
It might screw the export since it's just moving the scope issue from one point to another, but I hope that's helpful.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | maximob |
