aboutsummaryrefslogtreecommitdiff
path: root/examples/05_command_framework/src
diff options
context:
space:
mode:
Diffstat (limited to 'examples/05_command_framework/src')
-rw-r--r--examples/05_command_framework/src/main.rs194
1 files changed, 194 insertions, 0 deletions
diff --git a/examples/05_command_framework/src/main.rs b/examples/05_command_framework/src/main.rs
new file mode 100644
index 0000000..fbab6ec
--- /dev/null
+++ b/examples/05_command_framework/src/main.rs
@@ -0,0 +1,194 @@
+//! Requires the 'framework' feature flag be enabled in your project's
+//! `Cargo.toml`.
+//!
+//! This can be enabled by specifying the feature in the dependency section:
+//!
+//! ```toml
+//! [dependencies.serenity]
+//! git = "https://github.com/zeyla/serenity.git"
+//! features = ["framework"]
+//! ```
+
+#[macro_use]
+extern crate serenity;
+extern crate typemap;
+
+use serenity::client::Context;
+use serenity::Client;
+use serenity::model::{Message, permissions};
+use serenity::ext::framework::help_commands;
+use std::collections::HashMap;
+use std::env;
+use std::fmt::Write;
+use typemap::Key;
+
+struct CommandCounter;
+
+impl Key for CommandCounter {
+ type Value = HashMap<String, u64>;
+}
+
+fn main() {
+ // Configure the client with your Discord bot token in the environment.
+ let token = env::var("DISCORD_TOKEN")
+ .expect("Expected a token in the environment");
+ let mut client = Client::login(&token);
+
+ {
+ let mut data = client.data.lock().unwrap();
+ data.insert::<CommandCounter>(HashMap::default());
+ }
+
+ client.on_ready(|_ctx, ready| {
+ println!("{} is connected!", ready.user.name);
+ });
+
+ // Commands are equivalent to:
+ // "~about"
+ // "~emoji cat"
+ // "~emoji dog"
+ // "~multiply"
+ // "~ping"
+ // "~some long command"
+ client.with_framework(|f| f
+ // Configures the client, allowing for options to mutate how the
+ // framework functions.
+ //
+ // Refer to the documentation for
+ // `serenity::ext::framework::Configuration` for all available
+ // configurations.
+ .configure(|c| c
+ .allow_whitespace(true)
+ .on_mention(true)
+ .rate_limit_message("Try this again in `%time%` seconds.")
+ .prefix("~"))
+ // Set a function to be called prior to each command execution. This
+ // provides the context of the command, the message that was received,
+ // and the full name of the command that will be called.
+ //
+ // You can not use this to determine whether a command should be
+ // executed. Instead, `set_check` is provided to give you this
+ // functionality.
+ .before(|ctx, msg, command_name| {
+ println!("Got command '{}' by user '{}'",
+ command_name,
+ msg.author.name);
+
+ // Increment the number of times this command has been run once. If
+ // the command's name does not exist in the counter, add a default
+ // value of 0.
+ let mut data = ctx.data.lock().unwrap();
+ let counter = data.get_mut::<CommandCounter>().unwrap();
+ let entry = counter.entry(command_name.clone()).or_insert(0);
+ *entry += 1;
+
+ true // if `before` returns false, command processing doesn't happen.
+ })
+ // Similar to `before`, except will be called directly _after_
+ // command execution.
+ .after(|_, _, command_name, error| {
+ match error {
+ Ok(()) => println!("Processed command '{}'", command_name),
+ Err(why) => println!("Command '{}' returned error {:?}", command_name, why),
+ }
+ })
+ // Can't be used more than once per 5 seconds:
+ .simple_bucket("emoji", 5)
+ // Can't be used more than 2 times per 30 seconds, with a 5 second delay:
+ .bucket("complicated", 5, 30, 2)
+ .command("about", |c| c.exec_str("A test bot"))
+ .command("help", |c| c.exec_help(help_commands::plain))
+ .command("commands", |c| c
+ // Make this command use the "complicated" bucket.
+ .bucket("complicated")
+ .exec(commands))
+ .group("Emoji", |g| g
+ .prefix("emoji")
+ .command("cat", |c| c
+ .desc("Sends an emoji with a cat.")
+ .batch_known_as(vec!["kitty", "neko"]) // Adds multiple aliases
+ .bucket("emoji") // Make this command use the "emoji" bucket.
+ .exec_str(":cat:")
+ // Allow only administrators to call this:
+ .required_permissions(permissions::ADMINISTRATOR))
+ .command("dog", |c| c
+ .desc("Sends an emoji with a dog.")
+ .bucket("emoji")
+ .exec_str(":dog:")))
+ .command("multiply", |c| c
+ .known_as("*") // Lets us call ~* instead of ~multiply
+ .exec(multiply))
+ .command("ping", |c| c
+ .check(owner_check)
+ .exec_str("Pong!"))
+ .command("some long command", |c| c.exec(some_long_command)));
+
+ if let Err(why) = client.start() {
+ println!("Client error: {:?}", why);
+ }
+}
+
+// Commands can be created via the `command!` macro, to avoid manually typing
+// type annotations.
+//
+// This may bring more features available for commands in the future. See the
+// "multiply" command below for some of the power that the `command!` macro can
+// bring.
+command!(commands(ctx, msg, _args) {
+ let mut contents = "Commands used:\n".to_owned();
+
+ let data = ctx.data.lock().unwrap();
+ let counter = data.get::<CommandCounter>().unwrap();
+
+ for (k, v) in counter {
+ let _ = write!(contents, "- {name}: {amount}\n", name=k, amount=v);
+ }
+
+ if let Err(why) = msg.channel_id.say(&contents) {
+ println!("Error sending message: {:?}", why);
+ }
+});
+
+// A function which acts as a "check", to determine whether to call a command.
+//
+// In this case, this command checks to ensure you are the owner of the message
+// in order for the command to be executed. If the check fails, the command is
+// not called.
+fn owner_check(_: &mut Context, msg: &Message) -> bool {
+ // Replace 7 with your ID
+ msg.author.id == 7
+}
+
+command!(some_long_command(_ctx, msg, args) {
+ if let Err(why) = msg.channel_id.say(&format!("Arguments: {:?}", args)) {
+ println!("Error sending message: {:?}", why);
+ }
+});
+
+// Using the `command!` macro, commands can be created with a certain type of
+// "dynamic" type checking. This is a method of requiring that the arguments
+// given match the required type, and maps those arguments to the specified
+// bindings.
+//
+// For example, the following will be correctly parsed by the macro:
+//
+// `~multiply 3.7 4.3`
+//
+// However, the following will not, as the second argument can not be an f64:
+//
+// `~multiply 3.7 four`
+//
+// Since the argument can't be converted, the command returns early.
+//
+// Additionally, if not enough arguments are given (e.g. `~multiply 3`), then
+// the command will return early. If additional arguments are provided, they
+// will be ignored.
+//
+// Argument type overloading is currently not supported.
+command!(multiply(_ctx, msg, args, first: f64, second: f64) {
+ let res = first * second;
+
+ if let Err(why) = msg.channel_id.say(&res.to_string()) {
+ println!("Err sending product of {} and {}: {:?}", first, second, why);
+ }
+});