Skip to content

Commit 0ce2d48

Browse files
committed
Add embed slash command
1 parent 6a987de commit 0ce2d48

4 files changed

Lines changed: 141 additions & 2 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ tokio = { version = "1.21.2", features = ["macros", "signal", "rt-multi-thread"]
1919
regex = "1.6.0"
2020
octocrab = "0.17.0"
2121
reqwest = "0.11.12"
22+
hex = "0.4.3"

src/commands/mod.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::collections::HashMap;
1313
use crate::structures::State;
1414

1515
mod snippets;
16+
mod utils;
1617

1718
pub async fn register(ctx: &Context) -> ApplicationCommandMap {
1819
println!("Registering slash commands...");
@@ -32,13 +33,24 @@ pub async fn register(ctx: &Context) -> ApplicationCommandMap {
3233
}
3334

3435
pub async fn interact(ctx: &Context, interaction: &ApplicationCommandInteraction) {
35-
match interaction.data.name.as_str() {
36+
let name = &interaction.data.name;
37+
38+
match name.as_str() {
3639
"snippet" => snippets::snippet(ctx, interaction).await,
3740
"create-snippet" => snippets::create_snippet(ctx, interaction).await,
3841
"edit-snippet" => snippets::edit_snippet(ctx, interaction).await,
3942
"remove-snippet" => snippets::remove_snippet(ctx, interaction).await,
4043
"export-snippet" => snippets::export_snippet(ctx, interaction).await,
41-
_ => println!("WARNING: Received invalid application command interaction!: {}", interaction.data.name)
44+
"embed" => utils::embed(ctx, interaction).await,
45+
_ => {
46+
println!("WARNING: Received invalid application command interaction!: {}", name);
47+
48+
interaction.defer(ctx).await.expect("Failed to defer interaction");
49+
50+
let title = "Invalid application command";
51+
let content = &format!("An invalid application command was recieved: {}", name);
52+
respond_err(ctx, interaction, title, content).await;
53+
}
4254
}
4355
}
4456

@@ -98,13 +110,49 @@ impl ApplicationCommandMap {
98110
.description("Exports a snippet for user editing")
99111
.clone();
100112

113+
let embed = CreateApplicationCommand::default()
114+
.description("Creates an embed in the current channel")
115+
.create_option(|o| o
116+
.name("title")
117+
.description("The embed title")
118+
.kind(CommandOptionType::String)
119+
)
120+
.create_option(|o| o
121+
.name("description")
122+
.description("The embed description")
123+
.kind(CommandOptionType::String)
124+
)
125+
.create_option(|o| o
126+
.name("color")
127+
.description("The color of the embed in hexadecimal form. (ex: #ff00ff)")
128+
.kind(CommandOptionType::String)
129+
)
130+
.create_option(|o| o
131+
.name("url")
132+
.description("The embed url")
133+
.kind(CommandOptionType::String)
134+
)
135+
.create_option(|o| o
136+
.name("footer")
137+
.description("The embed footer text")
138+
.kind(CommandOptionType::String)
139+
)
140+
.create_option(|o| o
141+
.name("image")
142+
.description("The image url for the embed")
143+
.kind(CommandOptionType::String)
144+
)
145+
.clone();
146+
147+
101148
let mut commands = ApplicationCommandMap(CommandHashMap::new());
102149

103150
commands.insert("snippet", snippet);
104151
commands.insert("create-snippet", create_snippet);
105152
commands.insert("edit-snippet", edit_snippet);
106153
commands.insert("remove-snippet", remove_snippet);
107154
commands.insert("export-snippet", export_snippet);
155+
commands.insert("embed", embed);
108156

109157
for (name, command) in commands.0.iter_mut() {
110158
match *name {

src/commands/utils.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use serenity::builder::CreateEmbed;
2+
use serenity::model::prelude::interaction::application_command::{ApplicationCommandInteraction, CommandDataOptionValue};
3+
use serenity::prelude::Context;
4+
use serenity::utils::Colour;
5+
6+
use super::{arg_opt, respond_err, respond_embed};
7+
8+
pub(super) async fn embed(ctx: &Context, interaction: &ApplicationCommandInteraction) {
9+
let title = arg_opt(interaction, "title");
10+
let description = arg_opt(interaction, "description");
11+
let color = arg_opt(interaction, "color");
12+
let url = arg_opt(interaction, "url");
13+
let footer_text = arg_opt(interaction, "footer");
14+
let image = arg_opt(interaction, "image");
15+
16+
interaction.defer(ctx).await.expect("Failed to defer interaction");
17+
18+
let mut embed = CreateEmbed::default();
19+
20+
if let Some(CommandDataOptionValue::String(title)) = title {
21+
embed.title(title);
22+
}
23+
24+
if let Some(CommandDataOptionValue::String(description)) = description {
25+
embed.description(description);
26+
}
27+
28+
if let Some(CommandDataOptionValue::String(color)) = &color {
29+
match hex::decode(color.to_ascii_lowercase().replace("#", "")) {
30+
Ok(hex_arr) => {
31+
embed.color(Colour::from_rgb(hex_arr[0], hex_arr[1], hex_arr[2]));
32+
},
33+
Err(e) => {
34+
let title = "Invalid color provided";
35+
let content = &format!("The color '{}' is not a valid hexadecimal color: {}", &color, e);
36+
return respond_err(ctx, interaction, title, content).await
37+
}
38+
}
39+
}
40+
41+
if let Some(CommandDataOptionValue::String(url)) = url {
42+
match url.parse::<reqwest::Url>() {
43+
Ok(_) => {
44+
if embed.0.contains_key("title") {
45+
embed.url(url);
46+
} else {
47+
let title = "Invalid parameters";
48+
let content = "A title is required for a url to function";
49+
return respond_err(ctx, interaction, title, content).await
50+
}
51+
},
52+
Err(e) => {
53+
let title = "Invalid url provided";
54+
let content = &format!("The url '{}' is not a valid url: {}", url, e);
55+
return respond_err(ctx, interaction, title, content).await
56+
}
57+
}
58+
}
59+
60+
if let Some(CommandDataOptionValue::String(footer_text)) = footer_text {
61+
embed.footer(|f| f.text(footer_text));
62+
}
63+
64+
if let Some(CommandDataOptionValue::String(image)) = image {
65+
match image.parse::<reqwest::Url>() {
66+
Ok(_) => {
67+
embed.image(image);
68+
},
69+
Err(e) => {
70+
let title = "Invalid image url provided";
71+
let content = &format!("The image url '{}' is not a valid image url: {}", image, e);
72+
73+
return respond_err(ctx, interaction, title, content).await
74+
}
75+
}
76+
}
77+
78+
if embed.0.contains_key("title") || embed.0.contains_key("description") || embed.0.contains_key("footer") {
79+
respond_embed(ctx, interaction, &embed, false).await
80+
} else {
81+
respond_err(ctx, interaction, "Failed to respond with embed", "Embed does not have any content").await
82+
}
83+
}

0 commit comments

Comments
 (0)