Can't read default_member_permissions from SlashCommandBuilder?

I'm building a script that documents slash command attributes for a wiki page. I have a wrapper class that holds command attributes that aren't directly used by the SlashCommandBuilder (eg cooldown, execute function). Strangely, the default_member_permissions is readable inside the class, but not outside the wrapper. For a more concrete exmaple, here's the wrapper constructor:
...
this.data = new SlashCommandBuilder()
.setName(customIdInput)
.setDescription(descriptionInput)
.setDefaultMemberPermissions(PermissionFlagsBits.ViewChannel)
.setDMPermission(allowInDMsInput);
console.log("inside wrapper", this.data);
...
...
this.data = new SlashCommandBuilder()
.setName(customIdInput)
.setDescription(descriptionInput)
.setDefaultMemberPermissions(PermissionFlagsBits.ViewChannel)
.setDMPermission(allowInDMsInput);
console.log("inside wrapper", this.data);
...
In the console.log, this shows default_member_permissions: '1024',. However, this differs from when it gets logged outside the wrapper class.
module.exports = new CommandWrapper(customId, "Get a link to BountyBot's commands page", PermissionFlagsBits.ViewChannel, false, true, 3000, options, subcommands,
/** Link the user to the repo Commands wiki page (automatically updated) */
(interaction) => {
interaction.reply({ content: "Here's a link to the BountyBot Commands page (automatically updated): https://github.com/Imaginary-Horizons-Productions/BountyBot/wiki/Commands", ephemeral: true });
}
);

console.log("outside wrapper", module.exports);
module.exports = new CommandWrapper(customId, "Get a link to BountyBot's commands page", PermissionFlagsBits.ViewChannel, false, true, 3000, options, subcommands,
/** Link the user to the repo Commands wiki page (automatically updated) */
(interaction) => {
interaction.reply({ content: "Here's a link to the BountyBot Commands page (automatically updated): https://github.com/Imaginary-Horizons-Productions/BountyBot/wiki/Commands", ephemeral: true });
}
);

console.log("outside wrapper", module.exports);
This ends up logging default_member_permissions: undefined,. This seems especially problematic since my command uploader reads the default member permission in the same way, which would likely mean that attribute is getting stripped before getting uploaded. Am I missing something about how exporting might affect SlashCommandBuilders? Bonus: any recommendations on converting from a permission bit field back to a human readable list of permissions?
7 Replies
d.js toolkit
d.js toolkit12mo ago
• What's your exact discord.js npm list discord.js and node node -v version? • Post the full error stack trace, not just the top part! • Show your code! • Explain what exactly your issue is. • Not a discord.js issue? Check out #useful-servers.
Squid
Squid12mo ago
Can you show everything that's getting logged? <SlashCommandBuilder>.toJSON().default_member_permissions should be what you're looking for, but you may not be directly assigning every property to whatever's being logged Bonus: <PermissionsBitField>.toArray() returns strings like ViewChannel
Arcane_ish
Arcane_ish12mo ago
Looks like the only difference is the default_member_permissions (omitted some looping over other commands).
Squid
Squid12mo ago
I'm guessing it has something to do with your constructor for CommandWrapper. It looks like you're passing in each property from the builder to the CommandWrapper class, so something might be getting lost in translation there
Arcane_ish
Arcane_ish12mo ago
Seems plausible to me, but I'm stumped on what's going wrong specifically. Here's the CommandWrapper in its entirety:
class CommandWrapper extends module.exports.InteractionWrapper {
/** Additional wrapper properties for command parsing
* @param {string} customIdInput
* @param {string} descriptionInput
* @param {import("discord.js").PermissionFlags} defaultMemberPermission
* @param {boolean} isPremiumCommand
* @param {boolean} allowInDMsInput
* @param {number} cooldownInMS
* @param {{type: "Attachment" | "Boolean" | "Channel" | "Integer" | "Mentionable" | "Number" | "Role" | "String" | "User", name: string, description: string, required: boolean, choices: { name: string, value }[]}[]} optionsInput
* @param {{name: string, description: string, optionsInput: {type: "Attachment" | "Boolean" | "Channel" | "Integer" | "Mentionable" | "Number" | "Role" | "String" | "User", name: string, description: string, required: boolean, choices: { name: string, value }[]}}[]} subcommandsInput
* @param {(interaction: import("discord.js").Interaction) => void} executeFunction
*/
constructor(customIdInput, descriptionInput, defaultMemberPermission, isPremiumCommand, allowInDMsInput, cooldownInMS, optionsInput, subcommandsInput, executeFunction) {
super(customIdInput, cooldownInMS, executeFunction);
this.description = descriptionInput;
this.premiumCommand = isPremiumCommand;
this.data = new SlashCommandBuilder()
.setName(customIdInput)
.setDescription(descriptionInput)
.setDefaultMemberPermissions(PermissionFlagsBits.ViewChannel)
.setDMPermission(allowInDMsInput);
console.log("inside wrapper", this.data);
optionsInput.forEach(option => {
this.data[`add${option.type}Option`](built => {
built.setName(option.name).setDescription(option.description).setRequired(option.required);
if (option.choices === null || option.choices === undefined) {
throw new Error(`${this.customId} (${descriptionInput}) ${option.type} Option was nullish.`);
}
if (option.choices.length) {
built.addChoices(...option.choices);
}
return built;
})
})
subcommandsInput.forEach(subcommand => {
this.data.addSubcommand(built => {
built.setName(subcommand.name).setDescription(subcommand.description);
subcommand.optionsInput.forEach(option => {
built[`add${option.type}Option`](subBuilt => {
subBuilt.setName(option.name).setDescription(option.description).setRequired(option.required);
if (option.choices === null || option.choices === undefined) {
throw new Error(`${this.customId} (${descriptionInput}) ${option.type} Option was nullish.`);
}
let choiceEntries = Object.entries(option.choices);
if (choiceEntries.length) {
subBuilt.addChoices(...Object.entries(option.choices));
}
return subBuilt;
})
})
return built;
})
})
this.data.setDefaultMemberPermissions()
}
};
module.exports.CommandWrapper = CommandWrapper;
class CommandWrapper extends module.exports.InteractionWrapper {
/** Additional wrapper properties for command parsing
* @param {string} customIdInput
* @param {string} descriptionInput
* @param {import("discord.js").PermissionFlags} defaultMemberPermission
* @param {boolean} isPremiumCommand
* @param {boolean} allowInDMsInput
* @param {number} cooldownInMS
* @param {{type: "Attachment" | "Boolean" | "Channel" | "Integer" | "Mentionable" | "Number" | "Role" | "String" | "User", name: string, description: string, required: boolean, choices: { name: string, value }[]}[]} optionsInput
* @param {{name: string, description: string, optionsInput: {type: "Attachment" | "Boolean" | "Channel" | "Integer" | "Mentionable" | "Number" | "Role" | "String" | "User", name: string, description: string, required: boolean, choices: { name: string, value }[]}}[]} subcommandsInput
* @param {(interaction: import("discord.js").Interaction) => void} executeFunction
*/
constructor(customIdInput, descriptionInput, defaultMemberPermission, isPremiumCommand, allowInDMsInput, cooldownInMS, optionsInput, subcommandsInput, executeFunction) {
super(customIdInput, cooldownInMS, executeFunction);
this.description = descriptionInput;
this.premiumCommand = isPremiumCommand;
this.data = new SlashCommandBuilder()
.setName(customIdInput)
.setDescription(descriptionInput)
.setDefaultMemberPermissions(PermissionFlagsBits.ViewChannel)
.setDMPermission(allowInDMsInput);
console.log("inside wrapper", this.data);
optionsInput.forEach(option => {
this.data[`add${option.type}Option`](built => {
built.setName(option.name).setDescription(option.description).setRequired(option.required);
if (option.choices === null || option.choices === undefined) {
throw new Error(`${this.customId} (${descriptionInput}) ${option.type} Option was nullish.`);
}
if (option.choices.length) {
built.addChoices(...option.choices);
}
return built;
})
})
subcommandsInput.forEach(subcommand => {
this.data.addSubcommand(built => {
built.setName(subcommand.name).setDescription(subcommand.description);
subcommand.optionsInput.forEach(option => {
built[`add${option.type}Option`](subBuilt => {
subBuilt.setName(option.name).setDescription(option.description).setRequired(option.required);
if (option.choices === null || option.choices === undefined) {
throw new Error(`${this.customId} (${descriptionInput}) ${option.type} Option was nullish.`);
}
let choiceEntries = Object.entries(option.choices);
if (choiceEntries.length) {
subBuilt.addChoices(...Object.entries(option.choices));
}
return subBuilt;
})
})
return built;
})
})
this.data.setDefaultMemberPermissions()
}
};
module.exports.CommandWrapper = CommandWrapper;
Notably, I have it hardcoded to .setDefaultMemberPermissions(PermissionFlagsBits.ViewChannel) to reduce complexity for debugging, but normally it's just putting defaultMemberPermission straight in there without any modifications.
Squid
Squid12mo ago
The last line in the constructor is this.data.setDefaultMemberPermissions(). That's probably why the property is present in the log, but empty: you explicitly set it to undefined
Arcane_ish
Arcane_ish12mo ago
facepalms thanks!