Read property from custom Command Options

Hey folks, I have a custom command options object, that contains a property called shortDesc. I want to read a property from the options, inside the registerApplicationCommands method, so that I can not have to provide this value (Get a random trivia question) twice. How do I read shortDesc? this.shortDesc didn't work the same as this.name did - I assume because name exists on the default CommandOptions object, but it's having trouble parsing the Options here as IDrpgCommandOptions ?
@ApplyOptions<IDrpgCommandOptions>({
name: "Trivia",
aliases: ["q", "t", "question", "trivia", "quiz"],
shortDesc: "Get a random trivia question",
showInHelpMenu: true,
})
export class QuestionCommand extends DrpgCommand {
public override async registerApplicationCommands(registry: ApplicationCommandRegistry) {
const shortDesc = "Get a random trivia question"; //TODO How to get shortDesc from above?
registry.registerChatInputCommand((builder) => builder.setName(this.name).setDescription(shortDesc), {
idHints: [""],
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
});
}
@ApplyOptions<IDrpgCommandOptions>({
name: "Trivia",
aliases: ["q", "t", "question", "trivia", "quiz"],
shortDesc: "Get a random trivia question",
showInHelpMenu: true,
})
export class QuestionCommand extends DrpgCommand {
public override async registerApplicationCommands(registry: ApplicationCommandRegistry) {
const shortDesc = "Get a random trivia question"; //TODO How to get shortDesc from above?
registry.registerChatInputCommand((builder) => builder.setName(this.name).setDescription(shortDesc), {
idHints: [""],
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
});
}
Solution:
First of all, the reason it works in your help command is because you're using command.options, likewise if you use this.options.shortDesc it'll also be there. Secondly, categories are built into sapphire so whatever you're setting in the constructor there is pretty pointless. That dates back to a very old by now version of Sapphire where categories weren't integrated Thirdly, Sapphire Command class has a detailedDescription option which can be an object of whatever you want and is set automatically so you do not even need custom properties for this at all. Just use module augmentation to modify the interface for detailedDescription then use ...
Jump to solution
10 Replies
Krish
Krish•2y ago
can we see DrpgCommand? if you have shortDesc there then it must work
Bejasc
Bejasc•2y ago
export interface IDrpgCommandOptions extends CommandOptions {
shortDesc?: string;
showInHelpMenu?: boolean;
examples?: { title: string; command: string; description?: string }[];
}

export abstract class DrpgCommand extends Command {
public readonly fullCategory: readonly string[] = [];

public constructor(context: PieceContext, options: IDrpgCommandOptions) {
super(context, { ...COMMAND_OPTIONS, ...options });

if (options.fullCategory) {
this.fullCategory = options.fullCategory;
} else if (this.location.directories.length) {
this.fullCategory = this.location.directories;
} else {
this.fullCategory = ["uncategorized"];
}
}
}
export interface IDrpgCommandOptions extends CommandOptions {
shortDesc?: string;
showInHelpMenu?: boolean;
examples?: { title: string; command: string; description?: string }[];
}

export abstract class DrpgCommand extends Command {
public readonly fullCategory: readonly string[] = [];

public constructor(context: PieceContext, options: IDrpgCommandOptions) {
super(context, { ...COMMAND_OPTIONS, ...options });

if (options.fullCategory) {
this.fullCategory = options.fullCategory;
} else if (this.location.directories.length) {
this.fullCategory = this.location.directories;
} else {
this.fullCategory = ["uncategorized"];
}
}
}
COMMAND_OPTIONS is just IDrpgCommandOptions with some defaults set it's not part of the object, it's part of the Options
Krish
Krish•2y ago
add public readonly shortDesc: string; in your class basically you need to add all of the extra options you added
Bejasc
Bejasc•2y ago
does that change the way I add/apply those options?
Krish
Krish•2y ago
no that makes it a member of your class Until now your extra options were useless
Bejasc
Bejasc•2y ago
I've been using them okay in a help command
const target = await args.pick("string");

const cmd = this.store.get(target) as DrpgCommand;
const opts = cmd.options as IDrpgCommandOptions;

embed.setTitle(stringTitleCase(opts.name ?? cmd.name));

if (opts.description) embed.setDescription(opts.description);

if (cmd.aliases?.length > 0) embed.addField("Aliases", joinString(cmd.aliases.map((e) => `\`${prefix}${e}\``)));

const categoryString = cmd.fullCategory.join(" / ");
embed.addField("Category", categoryString);

if (opts.examples?.length > 0) {
const exampleText = opts.examples.map((example) => {
return `**${example.title}**\n_${example.description ?? opts.shortDesc}_\n\`${prefix}${example.command}\``;
});

embed.addField("Examples", exampleText.join("\n\n"));
}
const target = await args.pick("string");

const cmd = this.store.get(target) as DrpgCommand;
const opts = cmd.options as IDrpgCommandOptions;

embed.setTitle(stringTitleCase(opts.name ?? cmd.name));

if (opts.description) embed.setDescription(opts.description);

if (cmd.aliases?.length > 0) embed.addField("Aliases", joinString(cmd.aliases.map((e) => `\`${prefix}${e}\``)));

const categoryString = cmd.fullCategory.join(" / ");
embed.addField("Category", categoryString);

if (opts.examples?.length > 0) {
const exampleText = opts.examples.map((example) => {
return `**${example.title}**\n_${example.description ?? opts.shortDesc}_\n\`${prefix}${example.command}\``;
});

embed.addField("Examples", exampleText.join("\n\n"));
}
I think actually... another approach to the answer exists in this code above... 🫣 Thanks for the guidance though - I'll try this out tomorrow 🙂
Solution
Favna
Favna•2y ago
First of all, the reason it works in your help command is because you're using command.options, likewise if you use this.options.shortDesc it'll also be there. Secondly, categories are built into sapphire so whatever you're setting in the constructor there is pretty pointless. That dates back to a very old by now version of Sapphire where categories weren't integrated Thirdly, Sapphire Command class has a detailedDescription option which can be an object of whatever you want and is set automatically so you do not even need custom properties for this at all. Just use module augmentation to modify the interface for detailedDescription then use
detailedDescription: {
shortDesc: 'whatever'
}
detailedDescription: {
shortDesc: 'whatever'
}
and reference it with this.detailedDescription.shortDesc Fourthly, you don't need a help command when going slash commands only because every command and every option already gets a description and if your commands/options are so convoluted that you cannot describe them in 100 characters then you should revise the command as a whole.
Bejasc
Bejasc•2y ago
Thanks Favna - honestly I'm not sure what I was doing with the constructor there - it existed in whatever sample project/source material I referenced, possibly Skyra? My understanding is that it was trying to use the folder structure to automatically build the categories, if they hadn't been provided Also, am not going slash commands only - supporting both how new is detailedDescription? I've been out of touch the last couple of months and hadn't come across it before
That dates back to a very old by now version of Sapphire where categories weren't integrated
That'd be right - the code there is well over 12 months old some time around August/September 2021
Favna
Favna•2y ago
skyra hasnt had that in the constructor for ages and ages since like v1.x
Bejasc
Bejasc•2y ago
Like I said - it's been there for ages and ages before there were even docs of any kind