Skip to content

Commit 4bfc412

Browse files
committed
chore(config-command): Add a config command with YAML and Rust representation options
1 parent b16c512 commit 4bfc412

2 files changed

Lines changed: 137 additions & 0 deletions

File tree

bot/src/commands/config.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use std::fmt::Display;
2+
use std::str::FromStr;
3+
4+
use anyhow::Context;
5+
use async_trait::async_trait;
6+
use builders::command_option::CommandOptionBuilder;
7+
use twilight_model::application::command::{
8+
Command, CommandOptionChoice, CommandOptionChoiceValue, CommandOptionType, CommandType,
9+
};
10+
use twilight_model::application::interaction::application_command::CommandOptionValue;
11+
use twilight_model::application::interaction::{
12+
Interaction, InteractionContextType, InteractionData,
13+
};
14+
use twilight_model::guild::Permissions;
15+
use twilight_model::http::attachment::Attachment;
16+
use twilight_model::http::interaction::{InteractionResponse, InteractionResponseType};
17+
use twilight_model::oauth::ApplicationIntegrationType;
18+
use twilight_util::builder::command::CommandBuilder;
19+
use twilight_util::builder::InteractionResponseDataBuilder;
20+
21+
use crate::commands::CommandHandler;
22+
23+
#[allow(dead_code)]
24+
pub(crate) struct Config<'a> {
25+
pub(crate) cmd: &'a Interaction,
26+
}
27+
28+
#[async_trait]
29+
impl CommandHandler for Config<'_> {
30+
fn model(_ctx: Option<crate::Context>) -> anyhow::Result<Command> {
31+
let file_type_choices = [
32+
CommandOptionChoice {
33+
name: "Rust".to_string(),
34+
value: CommandOptionChoiceValue::String(FileType::Rust.to_string()),
35+
name_localizations: None,
36+
},
37+
CommandOptionChoice {
38+
name: "YAML".to_string(),
39+
value: CommandOptionChoiceValue::String(FileType::Yaml.to_string()),
40+
name_localizations: None,
41+
},
42+
];
43+
let file_type_option = CommandOptionBuilder::new(
44+
"file_type",
45+
"The type of file to send",
46+
CommandOptionType::String,
47+
)
48+
.required(true)
49+
.choices(file_type_choices)
50+
.build()?;
51+
52+
Ok(CommandBuilder::new(
53+
"config",
54+
"Send quick responses to common questions/queries.",
55+
CommandType::ChatInput,
56+
)
57+
.contexts([InteractionContextType::Guild])
58+
.integration_types([ApplicationIntegrationType::GuildInstall])
59+
.default_member_permissions(Permissions::MANAGE_CHANNELS)
60+
.option(file_type_option)
61+
.validate()
62+
.context("validate config command")?
63+
.build())
64+
}
65+
66+
async fn exec(&self, ctx: crate::Context) -> anyhow::Result<()> {
67+
// Parse the given file type from command options
68+
let Some(InteractionData::ApplicationCommand(data)) = &self.cmd.data else {
69+
anyhow::bail!("expected application command interaction");
70+
};
71+
let CommandOptionValue::String(file_type) = &data.options[0].value else {
72+
anyhow::bail!("expected string option value");
73+
};
74+
let file_type =
75+
FileType::from_str(file_type).context("failed to parse file type from string")?;
76+
77+
// Create the config file based on the file type
78+
let cfg_file = match file_type {
79+
FileType::Rust => {
80+
// Get the stored config struct
81+
let content = format!("{:#?}", ctx.cfg).into_bytes();
82+
Attachment::from_bytes("magnolia.cfg.rs".to_string(), content, 0)
83+
},
84+
FileType::Yaml => {
85+
// Get the raw content of the config file
86+
let content = include_bytes!("../../../magnolia.cfg.yml").to_vec();
87+
Attachment::from_bytes("magnolia.cfg.yml".to_string(), content, 0)
88+
},
89+
};
90+
91+
// Respond to the interaction with the config file
92+
ctx.http
93+
.interaction(self.cmd.application_id)
94+
.create_response(self.cmd.id, &self.cmd.token, &InteractionResponse {
95+
kind: InteractionResponseType::ChannelMessageWithSource,
96+
data: Some(
97+
InteractionResponseDataBuilder::new()
98+
.attachments([cfg_file])
99+
.build(),
100+
),
101+
})
102+
.await
103+
.context("failed to send config file")?;
104+
105+
Ok(())
106+
}
107+
}
108+
109+
/// FileType enum to represent the type of config file to send
110+
enum FileType {
111+
Rust,
112+
Yaml,
113+
}
114+
115+
impl Display for FileType {
116+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117+
match self {
118+
FileType::Rust => write!(f, "Rust"),
119+
FileType::Yaml => write!(f, "YAML"),
120+
}
121+
}
122+
}
123+
124+
impl FromStr for FileType {
125+
type Err = anyhow::Error;
126+
127+
fn from_str(s: &str) -> Result<Self, Self::Err> {
128+
match s {
129+
"Rust" => Ok(FileType::Rust),
130+
"YAML" => Ok(FileType::Yaml),
131+
_ => Err(anyhow::anyhow!("Invalid file type")),
132+
}
133+
}
134+
}

bot/src/commands/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ use async_trait::async_trait;
22
use twilight_model::application::command::Command;
33
use twilight_model::application::interaction::Interaction;
44

5+
mod config;
56
mod devforum_self_role;
67
mod faq;
78

89
/// Get all application command models.
910
pub(crate) fn models(ctx: crate::Context) -> anyhow::Result<Vec<Command>> {
1011
Ok(vec![
1112
devforum_self_role::DevForumSelfRole::model(None)?,
13+
config::Config::model(None)?,
1214
faq::Faq::model(Some(ctx))?,
1315
])
1416
}
@@ -29,6 +31,7 @@ pub(crate) async fn handle_command(
2931
) -> anyhow::Result<()> {
3032
let handler: Box<dyn CommandHandler> = match cmd_name {
3133
"devforum-self-role" => Box::new(devforum_self_role::DevForumSelfRole { cmd }),
34+
"config" => Box::new(config::Config { cmd }),
3235
"faq" => Box::new(faq::Faq { cmd }),
3336
unknown => anyhow::bail!("unknown command name: {}", unknown),
3437
};

0 commit comments

Comments
 (0)