Skip to content

Commit

Permalink
Clarify slash commands implementation (#1356)
Browse files Browse the repository at this point in the history
This commit clarifies the slash commands and interaction implementation by improving the documentation and renames the confusing `create(_global)_application_commands(_permissions)` methods to `set(_global)_application_commands(_permissions)`.
  • Loading branch information
HarmoGlace committed Jul 1, 2021
1 parent 0be7d1a commit ae09e57
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 84 deletions.
129 changes: 117 additions & 12 deletions examples/e14_slash_commands/src/main.rs
Expand Up @@ -3,7 +3,19 @@ use std::env;
use serenity::{
async_trait,
client::bridge::gateway::GatewayIntents,
model::{gateway::Ready, interactions::{Interaction, InteractionResponseType, ApplicationCommand}},
model::{
gateway::Ready,
id::GuildId,
interactions::{
ApplicationCommand,
ApplicationCommandInteractionDataOptionValue,
ApplicationCommandOptionType,
Interaction,
InteractionData,
InteractionResponseType,
InteractionType,
},
},
prelude::*,
};

Expand All @@ -12,21 +24,112 @@ struct Handler;
#[async_trait]
impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
interaction
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| message.content("Received event!"))
})
.await;
if interaction.kind == InteractionType::ApplicationCommand {
if let Some(data) = interaction.data.as_ref() {
match data {
InteractionData::ApplicationCommand(data) => {
let content = match data.name.as_str() {
"ping" => "Hey, I'm alive!".to_string(),
"id" => {
let options = data
.options
.get(0)
.expect("Expected user option")
.resolved
.as_ref()
.expect("Expected user object");

if let ApplicationCommandInteractionDataOptionValue::User(
user,
_member,
) = options
{
format!("{}'s id is {}", user.tag(), user.id)
} else {
"Please provide a valid user".to_string()
}
},
_ => "not implemented :(".to_string(),
};

if let Err(why) = interaction
.create_interaction_response(&ctx.http, |response| {
response
.kind(InteractionResponseType::ChannelMessageWithSource)
.interaction_response_data(|message| message.content(content))
})
.await
{
println!("Cannot respond to slash command: {}", why);
}
},
_ => {},
}
}
}
}

async fn ready(&self, ctx: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);

let interactions = ApplicationCommand::get_global_application_commands(&ctx.http).await;
let commands = ApplicationCommand::set_global_application_commands(&ctx.http, |commands| {
commands
.create_application_command(|command| {
command.name("ping").description("A ping command")
})
.create_application_command(|command| {
command.name("id").description("Get a user id").create_option(|option| {
option
.name("id")
.description("The user to lookup")
.kind(ApplicationCommandOptionType::User)
.required(true)
})
})
.create_application_command(|command| {
command
.name("welcome")
.description("Welcome a user")
.create_option(|option| {
option
.name("user")
.description("The user to welcome")
.kind(ApplicationCommandOptionType::User)
.required(true)
})
.create_option(|option| {
option
.name("message")
.description("The message to send")
.kind(ApplicationCommandOptionType::String)
.required(true)
.add_string_choice(
"Welcome to our cool server! Ask me if you need help",
"pizza",
)
.add_string_choice("Hey, do you want a coffee?", "coffee")
.add_string_choice(
"Welcome to the club, you're now a good person. Well, I hope.",
"club",
)
.add_string_choice(
"I hope that you brought a controller to play together!",
"game",
)
})
})
})
.await;

println!("I now have the following global slash commands: {:#?}", commands);

let guild_command = GuildId(123456789)
.create_application_command(&ctx.http, |command| {
command.name("wonderful_command").description("An amazing command")
})
.await;

println!("I have the following global slash command(s): {:?}", interactions);
println!("I created the following guild command: {:#?}", guild_command);
}
}

Expand All @@ -36,8 +139,10 @@ async fn main() {
let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");

// The Application Id is usually the Bot User Id.
let application_id: u64 =
env::var("APPLICATION_ID").expect("Expected an application id in the environment").parse().expect("application id is not a valid id");
let application_id: u64 = env::var("APPLICATION_ID")
.expect("Expected an application id in the environment")
.parse()
.expect("application id is not a valid id");

// Build our client.
let mut client = Client::builder(token)
Expand Down
26 changes: 13 additions & 13 deletions src/builder/create_application_command.rs
Expand Up @@ -54,7 +54,7 @@ impl CreateApplicationCommandOption {
self
}

/// Interaction commands can optionally have a limited
/// Application commands can optionally have a limited
/// number of integer or string choices.
///
/// **Note**: There can be no more than 10 choices set.
Expand Down Expand Up @@ -114,7 +114,7 @@ impl CreateApplicationCommandOption {
pub struct CreateApplicationCommand(pub HashMap<&'static str, Value>);

impl CreateApplicationCommand {
/// Specify the name of the Interaction.
/// Specify the name of the application command.
///
/// **Note**: Must be between 1 and 32 characters long,
/// and cannot start with a space.
Expand All @@ -133,17 +133,17 @@ impl CreateApplicationCommand {
self
}

/// Specify the description of the Interaction.
/// Specify the description of the application command.
///
/// **Note**: Must be between 1 and 100 characters long.
pub fn description<D: ToString>(&mut self, description: D) -> &mut Self {
self.0.insert("description", Value::String(description.to_string()));
self
}

/// Create an interaction option for the interaction.
/// Create an application command option for the application command.
///
/// **Note**: Interactions can only have up to 10 options.
/// **Note**: Application commands can only have up to 10 options.
pub fn create_option<F>(&mut self, f: F) -> &mut Self
where
F: FnOnce(&mut CreateApplicationCommandOption) -> &mut CreateApplicationCommandOption,
Expand All @@ -153,9 +153,9 @@ impl CreateApplicationCommand {
self.add_option(data)
}

/// Add an interaction option for the interaction.
/// Add an application command option for the application command.
///
/// **Note**: Interactions can only have up to 10 options.
/// **Note**: Application commands can only have up to 10 options.
pub fn add_option(&mut self, option: CreateApplicationCommandOption) -> &mut Self {
let new_option = utils::hashmap_to_json_map(option.0);
let options = self.0.entry("options").or_insert_with(|| Value::Array(Vec::new()));
Expand All @@ -165,9 +165,9 @@ impl CreateApplicationCommand {
self
}

/// Sets all the interaction options for the interaction.
/// Sets all the application command options for the application command.
///
/// **Note**: Interactions can only have up to 10 options.
/// **Note**: Application commands can only have up to 10 options.
pub fn set_options(&mut self, options: Vec<CreateApplicationCommandOption>) -> &mut Self {
let new_options = options
.into_iter()
Expand Down Expand Up @@ -196,8 +196,8 @@ impl CreateApplicationCommands {
}

/// Adds a new application command.
pub fn add_application_command(&mut self, interaction: CreateApplicationCommand) -> &mut Self {
let new_data = Value::Object(utils::hashmap_to_json_map(interaction.0));
pub fn add_application_command(&mut self, command: CreateApplicationCommand) -> &mut Self {
let new_data = Value::Object(utils::hashmap_to_json_map(command.0));

self.0.push(new_data);

Expand All @@ -207,9 +207,9 @@ impl CreateApplicationCommands {
/// Sets all the application commands.
pub fn set_application_commands(
&mut self,
interactions: Vec<CreateApplicationCommand>,
commands: Vec<CreateApplicationCommand>,
) -> &mut Self {
let new_application_command = interactions
let new_application_command = commands
.into_iter()
.map(|f| Value::Object(utils::hashmap_to_json_map(f.0)))
.collect::<Vec<Value>>();
Expand Down
29 changes: 17 additions & 12 deletions src/builder/create_application_command_permission.rs
Expand Up @@ -28,7 +28,7 @@ impl CreateApplicationCommandsPermissions {
self
}

/// Adds a new interaction.
/// Adds a new application command.
pub fn add_application_command(
&mut self,
application_command: CreateApplicationCommandPermissions,
Expand All @@ -40,7 +40,7 @@ impl CreateApplicationCommandsPermissions {
self
}

/// Sets all the interactions.
/// Sets all the application commands.
pub fn set_application_commands(
&mut self,
application_commands: Vec<CreateApplicationCommandPermissions>,
Expand Down Expand Up @@ -73,8 +73,8 @@ impl CreateApplicationCommandPermissions {
self
}

/// Creates a new permission for the interaction.
pub fn create_permission<F>(&mut self, f: F) -> &mut Self
/// Creates permissions for the application command.
pub fn create_permissions<F>(&mut self, f: F) -> &mut Self
where
F: FnOnce(
&mut CreateApplicationCommandPermissionData,
Expand All @@ -83,13 +83,13 @@ impl CreateApplicationCommandPermissions {
let mut data = CreateApplicationCommandPermissionData::default();
f(&mut data);

self.add_permission(data);
self.add_permissions(data);

self
}

/// Adds a permission for the interaction.
pub fn add_permission(
/// Adds permission for the application command.
pub fn add_permissions(
&mut self,
permission: CreateApplicationCommandPermissionData,
) -> &mut Self {
Expand All @@ -103,7 +103,7 @@ impl CreateApplicationCommandPermissions {
self
}

/// Sets all the permissions for the interaction.
/// Sets permissions for the application command.
pub fn set_permissions(
&mut self,
permissions: Vec<CreateApplicationCommandPermissionData>,
Expand All @@ -127,7 +127,7 @@ impl CreateApplicationCommandPermissions {
pub struct CreateApplicationCommandPermissionsData(pub HashMap<&'static str, Value>);

impl CreateApplicationCommandPermissionsData {
/// Creates a permission for the interaction.
/// Creates a permission for the application command.
pub fn create_permission<F>(&mut self, f: F) -> &mut Self
where
F: FnOnce(
Expand All @@ -142,7 +142,7 @@ impl CreateApplicationCommandPermissionsData {
self
}

/// Adds a permission.
/// Adds a permission for the application command.
pub fn add_permission(
&mut self,
permission: CreateApplicationCommandPermissionData,
Expand All @@ -157,7 +157,7 @@ impl CreateApplicationCommandPermissionsData {
self
}

/// Sets all the permissions for the interaction.
/// Sets permissions for the application command.
pub fn set_permissions(
&mut self,
permissions: Vec<CreateApplicationCommandPermissionData>,
Expand Down Expand Up @@ -199,7 +199,12 @@ impl CreateApplicationCommandPermissionData {
self
}

/// Sets the permission.
/// Sets the permission for the [`ApplicationCommandPermissionData`].
///
/// **Note**: Setting it to `false` will only grey the application command in the
/// list, it will not fully hide it to the user.
///
/// [`ApplicationCommandPermissionData`]: crate::model::interaction::ApplicationCommandPermissionData
pub fn permission(&mut self, permission: bool) -> &mut Self {
self.0.insert("permission", Value::Bool(permission));
self
Expand Down
3 changes: 2 additions & 1 deletion src/client/event_handler.rs
Expand Up @@ -422,7 +422,8 @@ pub trait EventHandler: Send + Sync {
) {
}

/// Dispatched when a user used a slash command.
/// Dispatched when an interaction is created (e.g a slash command was used
/// or a button was clicked).
///
/// Provides the created interaction.
#[cfg(feature = "unstable_discord_api")]
Expand Down
4 changes: 2 additions & 2 deletions src/http/client.rs
Expand Up @@ -88,7 +88,7 @@ impl<'a> HttpBuilder<'a> {
Self::_new().token(token)
}

/// Sets the application_id to use slash commands.
/// Sets the application_id to use interactions.
#[cfg(feature = "unstable_discord_api")]
pub fn application_id(mut self, application_id: u64) -> Self {
self.application_id = Some(application_id);
Expand Down Expand Up @@ -175,7 +175,7 @@ impl<'a> Future for HttpBuilder<'a> {
#[cfg(feature = "unstable_discord_api")]
let application_id = self
.application_id
.expect("Expected application Id in order to use slash commands");
.expect("Expected application Id in order to use interacions features");

let client = self.client.take().unwrap_or_else(|| {
let builder = configure_client_backend(Client::builder());
Expand Down
4 changes: 2 additions & 2 deletions src/model/event.rs
Expand Up @@ -1698,7 +1698,7 @@ pub enum Event {
VoiceServerUpdate(VoiceServerUpdateEvent),
/// A webhook for a [channel][`GuildChannel`] was updated in a [`Guild`].
WebhookUpdate(WebhookUpdateEvent),
/// A user used a slash command.
/// An interaction was created.
#[cfg(feature = "unstable_discord_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable_discord_api")))]
InteractionCreate(InteractionCreateEvent),
Expand Down Expand Up @@ -2053,7 +2053,7 @@ pub enum EventType {
///
/// This maps to [`WebhookUpdateEvent`].
WebhookUpdate,
/// Indicator that a slash command was received.
/// Indicator that an interaction was created.
///
/// This maps to [`InteractionCreateEvent`].
#[cfg(feature = "unstable_discord_api")]
Expand Down

0 comments on commit ae09e57

Please sign in to comment.