Skip to main content

Slash Commands

Using slash commands, you can have commands without the need of read messages intents. It also gives a user a standardized graphical user interface.

note

Slash Commands are only available in the develop branch. Many features have not been implemented yet. Slash Commands are still being worked on. The main parts of slash commands are finished but many new features like localization, attachment option types, and permissions are still being worked on.

Add Commands

const std::string name = "hello";
const std::string description = "says hello";

createGlobalAppCommand(getID(), name, description);

or

const std::string name = "hello";
const std::string description = "says hello";

createServerAppCommand(getID(), server.ID, name, description);

There's two create app command request, you can choose from createGlobalAppCommand, and createServerAppCommand for commands that are available depending on the server. The library also provides a createAppCommand that combines the two.

note

Names for commands, values, and choices have a character limit and don't allow some characters. More info here.

Responding to Commands

void onInteraction(SleepyDiscord::Interaction interaction) override {
if (interaction.data.name == "hello") {
SleepyDiscord::Interaction::Response<> response;
response.type = SleepyDiscord::InteractionCallbackType::ChannelMessageWithSource;
response.data.content = "hello";
client.createInteractionResponse(interaction.ID, interaction.token, response);
} else {
//not found
SleepyDiscord::Interaction::Response<> response;
response.type = SleepyDiscord::InteractionCallbackType::ChannelMessageWithSource;
response.data.content = "Couldn't find command";
response.data.flags = SleepyDiscord::InteractionAppCommandCallbackData::Flags::Ephemeral; //only for the user to see
createInteractionResponse(interaction, interaction.token, response);
return;
}
}

When a user uses a command, the bot will hear about it in the onInteraction event. The interaction's data object will have the name of the command that was used. The bot should create a Interaction Response, with a type and content. After that, the bot sends a Create Interaction Response request, using the interaction's ID, and token.

Adding Options

const std::string name = "add";
const std::string description = "sum of two number";
std::vector<SleepyDiscord::AppCommand::Option> options;

SleepyDiscord::AppCommand::Option left;
left.name = "left";
left.isRequired = true;
left.description = "The number to the left of the + sign";
left.type = SleepyDiscord::AppCommand::Option::TypeHelper<double>::getType();
options.push_back(std::move(left));

SleepyDiscord::AppCommand::Option right;
right.name = "right";
right.isRequired = true;
right.description = "The number to the right of the + sign";
right.type = SleepyDiscord::AppCommand::Option::TypeHelper<double>::getType();
options.push_back(std::move(right));

createGlobalAppCommand(getID(), name, description, std::move(options));

When creating the list of the options, you'll need to create a SleepyDiscord::AppCommand::Option for each option and move that into the options. The required data is type, name, and description. The type needs to be a value in the SleepyDiscord::AppCommand::Option::Type enum. You can use SleepyDiscord::AppCommand::Option::TypeHelper to get the type for things like string, int, and bool. There are some more advanced useful types like SleepyDiscord::AppCommand::Option::Type::MENTIONABLE, SleepyDiscord::AppCommand::Option::SUB_COMMAND, and SleepyDiscord::AppCommand::Option::SUB_COMMAND_GROUP. Another important thing about Options is that they can't easily be copied because the Choices in Options can't be copied, but they can easily be moved with std::move.

Responding to Options

There's more than one way to handle this, that comes with different pros and cons.

void onInteraction(SleepyDiscord::Interaction interaction) override {
if (interaction.data.name != "add")
return; //not found

for (auto& option : interaction.data.options) {
if (option.name == "left" || option.name == "right") {
double num;
if (option.get(num)) {
answer += num;
} else {
return; //fail
}
}
}
}
void onInteraction(SleepyDiscord::Interaction interaction) override {
if (interaction.data.name != "add")
return; //not found

for (auto& option : interaction.data.options) {
if (option.name == "left" || option.name == "right") {
try {
answer += option.get<double>();
} catch (...) {
//fail
}
}
}
}

Mainly different ways to error check that parsing the JSON data didn't run into issues. Since we don't know exactly what the type is, it's recommended to use matching types, but there is still a possibility for errors during parsing.

Adding Choices

SleepyDiscord::AppCommand::Option iceCream;
iceCream.name = "ice-cream";
iceCream.description = "Which ice-cream would you like";
iceCream.type = SleepyDiscord::AppCommand::Option::TypeHelper<std::string>::getType();

SleepyDiscord::AppCommand::Option::Choice vanilla;
vanilla.name = "Vanilla";
vanilla.set<std::string>("vanilla");
iceCream.choices.push_back(std::move(vanilla));

SleepyDiscord::AppCommand::Option::Choice matcha;
vanilla.name = "Matcha";
vanilla.set<std::string>("matcha");
iceCream.choices.push_back(std::move(matcha));

SleepyDiscord::AppCommand::Option::Choice mixed;
vanilla.name = "Mixed";
vanilla.set<std::string>("mixed");
iceCream.choices.push_back(std::move(mixed));

Adding choices requires setting with a type and a value. It's recommended that you use matching types.

To respond to choices, it's the same as responding to the option like earlier but look out for the values that were set in your choices.

warning

The choices limit is 25, try autocomplete if you are going over that limit

Autocomplete

Adding the option
SleepyDiscord::AppCommand::Option search;
search.name = "search";
search.description = "looks though the large database";
search.type = SleepyDiscord::AppCommand::Option::TypeHelper<std::string>::getType();
search.autocomplete = true;
warning

Choices need to be empty for autocomplete to be allowed

Responding to the user typing
void onInteraction(SleepyDiscord::Interaction interaction) override {
if (interaction.type == SleepyDiscord::Interaction::Type::ApplicationCommandAutocomplete) {
try {
std::string query = option.get<std::string>();
SleepyDiscord::Interaction::AutocompleteResponse response;
SleepyDiscord::AppCommand::Option::Choice extraE;
extraE.name = "E";
extraE.set<std::string>(query + "e");
response.data.choices.push_back(std::move(extraE));
client.createInteractionResponse(interaction.ID, interaction.token, response);
} catch (...) {
//fail
}
} else {
// execute search command
}
}

Using autocomplete, your bot can set the choices dynamically as the user types their command. You need to set an option without any choices to allow autocomplete by setting option.autocomplete to true. After that, the bot needs to respond to the options as the user types with a list of choices using an Interaction::AutocompleteResponse. You can also tell when the user is typing with autocomplete using interaction.type.

Context Menus

There's 2 forms of commands that use context menus, user commands and message commands.

const std::string name = "star";
const std::string description = "add a star a message";
const bool defaultPermissions = true;
//The type needs to be set to message
const SleepyDiscord::AppCommand::Type type = SleepyDiscord::AppCommand::Type::MESSAGE;

client.createGlobalAppCommand(getID(), name, description,
SleepyDiscord::AppCommand::emptyOptions, defaultPermissions, type);
Responding to Message Commands
void onInteraction(SleepyDiscord::Interaction interaction) override {
if (interaction.data.name != "star")
return; //not found

SleepyDiscord::Message message;
if (!interaction.data.resolved.messages.find(interaction.data.targetID, message)) {
return; //fail
}
// use message to get required data
message.content;
}

//or
void onInteraction(SleepyDiscord::Interaction interaction) override {
if (interaction.data.name != "star")
return; //not found

auto iterator = interaction.data.resolved.messages.find(interaction.data.targetID);
if (iterator == interaction.data.resolved.messages.end()) {
return; //fail
}
SleepyDiscord::Message message = iterator->value;
}

These types of commands do not contain any options and instead get parameters via interaction.data.resolved. Which contains the objects that are selected or in other objects related to the target, interaction.data.targetID. For example, if someone is mentioned in a message, you can find that user's User object in interaction.data.resolved.users.