Skip to content

Commit

Permalink
add surveys
Browse files Browse the repository at this point in the history
  • Loading branch information
BrettMayson committed Mar 30, 2024
1 parent 1fc8d0e commit 2c3c3fa
Show file tree
Hide file tree
Showing 13 changed files with 560 additions and 0 deletions.
8 changes: 8 additions & 0 deletions bin/bot/src/discord/handler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl EventHandler for Handler {
slash::missions::register(),
slash::reputation::register(),
slash::schedule::register(),
slash::surveys::register(),
],
)
.await
Expand All @@ -73,6 +74,7 @@ impl EventHandler for Handler {
"missions" => slash::missions::run(&ctx, &command).await,
"reputation" => slash::reputation::run(&ctx, &command).await,
"schedule" => slash::schedule::run(&ctx, &command).await,
"surveys" => slash::surveys::run(&ctx, &command).await,
MENU_AAR_IDS => menu::missions::run_aar_ids(&ctx, &command).await,
MENU_AAR_PAY => menu::missions::run_aar_pay(&ctx, &command).await,
_ => Ok(()),
Expand All @@ -94,6 +96,7 @@ impl EventHandler for Handler {
"gear" => slash::gear::autocomplete(&ctx, &autocomplete).await,
"missions" => slash::missions::autocomplete(&ctx, &autocomplete).await,
"schedule" => slash::schedule::autocomplete(&ctx, &autocomplete).await,
"surveys" => slash::surveys::autocomplete(&ctx, &autocomplete).await,
_ => Ok(()),
}
}
Expand All @@ -102,6 +105,11 @@ impl EventHandler for Handler {
"matching component: {:?}",
component.data.custom_id.as_str()
);
if component.data.custom_id.starts_with("survey_submit:") {
if let Err(e) = slash::surveys::submit_button(&ctx, &component).await {
error!("Cannot handle survey submit: {}", e);
}
}
match component.data.custom_id.as_str() {
"rsvp_yes" | "rsvp_maybe" | "rsvp_no" => {
slash::schedule::rsvp_button(&ctx, &component).await
Expand Down
1 change: 1 addition & 0 deletions bin/bot/src/discord/slash/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod gear;
pub mod missions;
pub mod reputation;
pub mod schedule;
pub mod surveys;

#[derive(Debug, Clone)]
pub enum ShouldAsk<'a> {
Expand Down
217 changes: 217 additions & 0 deletions bin/bot/src/discord/slash/surveys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
use serenity::all::{
ButtonStyle, CommandData, CommandDataOption, CommandDataOptionValue, CommandInteraction,
CommandOptionType, ComponentInteraction, Context, CreateActionRow, CreateAutocompleteResponse,
CreateButton, CreateCommand, CreateCommandOption, CreateEmbed, CreateInteractionResponse,
CreateMessage,
};
use synixe_events::surveys::db::Response;
use synixe_meta::discord::role::STAFF;
use synixe_proc::events_request_2;
use uuid::Uuid;

use crate::{discord::interaction::Interaction, get_option};

use super::ShouldAsk;

pub fn register() -> CreateCommand {
CreateCommand::new("surveys")
.description("surveys")
.add_option(
CreateCommandOption::new(CommandOptionType::SubCommand, "post", "Post a Survey")
.add_sub_option(
CreateCommandOption::new(
CommandOptionType::String,
"survey",
"Select the survey to post",
)
.required(true)
.set_autocomplete(true),
),
)
}

pub async fn autocomplete(
ctx: &Context,
autocomplete: &CommandInteraction,
) -> serenity::Result<()> {
let Some(subcommand) = autocomplete.data.options.first() else {
warn!("No subcommand for bank provided");
return Ok(());
};
if subcommand.kind() == CommandOptionType::SubCommand {
match subcommand.name.as_str() {
"post" => post_autocomplete(ctx, autocomplete).await?,
_ => unreachable!(),
}
}
Ok(())
}

pub async fn run(ctx: &Context, command: &CommandInteraction) -> serenity::Result<()> {
let Some(subcommand) = command.data.options.first() else {
warn!("No subcommand for missions provided");
return Ok(());
};
if let CommandDataOptionValue::SubCommand(options) = &subcommand.value {
match subcommand.name.as_str() {
"post" => post(ctx, command, options).await?,
_ => unreachable!(),
}
}
Ok(())
}

async fn post_autocomplete(
ctx: &Context,
autocomplete: &CommandInteraction,
) -> serenity::Result<()> {
let Some(focus) = CommandData::autocomplete(&autocomplete.data) else {
return Ok(());
};
if focus.name != "survey" {
return Ok(());
}
let Ok(Ok((Response::SearchSurvey(Ok(mut surveys)), _))) = events_request_2!(
bootstrap::NC::get().await,
synixe_events::surveys::db,
SearchSurvey {
title: if focus.value.is_empty() {
None
} else {
Some(focus.value.to_string())
},
}
)
.await
else {
error!("failed to fetch mission list");
return Ok(());
};
surveys.truncate(25);
if let Err(e) = autocomplete
.create_response(&ctx.http, {
let mut f = CreateAutocompleteResponse::default();
for (id, title) in surveys {
f = f.add_string_choice(title, id);
}
CreateInteractionResponse::Autocomplete(f)
})
.await
{
error!("failed to create autocomplete response: {}", e);
}
Ok(())
}

async fn post(
ctx: &Context,
command: &CommandInteraction,
options: &[CommandDataOption],
) -> serenity::Result<()> {
let mut interaction = Interaction::new(ctx, command.clone(), options);
let Some(channel) = &command.channel else {
return interaction
.reply("This command must be run in a guild channel")
.await;
};
super::requires_roles(
command.user.id,
&[STAFF],
&command
.member
.as_ref()
.expect("member should always exist on guild commands")
.roles,
ShouldAsk::Deny,
&mut interaction,
)
.await?;
let Some(survey) = get_option!(options, "survey", String) else {
return interaction
.reply("Required option not provided: survey")
.await;
};
let survey: Uuid = survey.parse().expect("survey id should be a valid uuid");
let Ok(Ok((Response::GetSurvey(Ok(Some((id, title, description)))), _))) = events_request_2!(
bootstrap::NC::get().await,
synixe_events::surveys::db,
GetSurvey { survey }
)
.await
else {
error!("failed to fetch mission list");
return Ok(());
};
if let Err(e) = channel
.id
.send_message(
&ctx,
CreateMessage::default()
.add_embed(CreateEmbed::default().title(title).description(description))
.components(vec![CreateActionRow::Buttons(vec![CreateButton::new(
format!("survey_submit:{}", id),

Check warning on line 152 in bin/bot/src/discord/slash/surveys.rs

View workflow job for this annotation

GitHub Actions / clippy

variables can be used directly in the `format!` string

warning: variables can be used directly in the `format!` string --> bin/bot/src/discord/slash/surveys.rs:152:21 | 152 | format!("survey_submit:{}", id), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args = note: `-W clippy::uninlined-format-args` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::uninlined_format_args)]` help: change this to | 152 - format!("survey_submit:{}", id), 152 + format!("survey_submit:{id}"), |
)
.style(ButtonStyle::Primary)
.label("Submit")])]),
)
.await
{
error!("failed to send survey message: {}", e);
}
interaction.reply("Survey posted").await?;
Ok(())
}

#[allow(clippy::too_many_lines)]
pub async fn submit_button(
ctx: &Context,
component: &ComponentInteraction,
) -> serenity::Result<()> {
let survey = component
.data
.custom_id
.strip_prefix("survey_submit:")
.expect("custom id should start with survey_submit:")
.parse()
.expect("survey id should be a valid uuid");
let mut interaction = Interaction::new(ctx, component.clone(), &[]);
let Ok(Ok((Response::GetOptions(Ok(options)), _))) = events_request_2!(
bootstrap::NC::get().await,
synixe_events::surveys::db,
GetOptions { survey }
)
.await
else {
error!("failed to fetch mission list");
return Ok(());
};
let Some(option) = interaction
.choice(
"Please select an option",
&options
.iter()
.map(|option| (option.clone(), option.clone()))
.collect::<Vec<_>>(),
)
.await?
else {
warn!("No option selected");
return Ok(());
};
let Ok(Ok((Response::Submit(Ok(())), _))) = events_request_2!(
bootstrap::NC::get().await,
synixe_events::surveys::db,
Submit {
survey,
member: component.user.id,
option
}
)
.await
else {
error!("failed to submit survey");
return Ok(());
};
interaction.reply("Survey submitted!").await?;
Ok(())
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2c3c3fa

Please sign in to comment.