Implement the Error
trait and improve our custom error
This commit is contained in:
parent
6c48eb11ac
commit
0a5a3d366b
10 changed files with 135 additions and 196 deletions
|
@ -1,7 +1,7 @@
|
|||
//! Commons elements that are used in while executing the bot commands.
|
||||
|
||||
use log::{info, warn};
|
||||
use std::sync::Arc;
|
||||
use std::{fmt::Display, sync::Arc};
|
||||
|
||||
use serenity::{
|
||||
async_trait,
|
||||
|
@ -18,31 +18,65 @@ use crate::database::Client as DatabaseClient;
|
|||
|
||||
/// The kind of errors that can be returned while executing a command.
|
||||
#[derive(Debug)]
|
||||
pub enum CommandExecutionError {
|
||||
pub enum Error {
|
||||
/// An error that is returned when the `Serenity` crate failed to get the argument from the
|
||||
/// Discord API payload.
|
||||
ArgumentExtractionError(String),
|
||||
InvalidCommand(String),
|
||||
|
||||
/// An error that is returned when the cast from the argument value to a Rust type failed.
|
||||
ArgumentDeserializationError(String),
|
||||
ArgumentDeserialization(String),
|
||||
|
||||
/// The kind of error that is returned when a Database query failed.
|
||||
DatabaseQueryError(String),
|
||||
DatabaseQuery(String),
|
||||
|
||||
/// Error returned when we fail to extract the context of a Discord commands.
|
||||
ContextRetrievalError(String),
|
||||
ContextRetrieval(String),
|
||||
|
||||
/// Error returned when sending a command to the discord API failed.
|
||||
DiscordAPICallError(String),
|
||||
DiscordAPICall(serenity::Error),
|
||||
|
||||
/// Error returned when there was an issue while using a selector embed in the command
|
||||
/// response.
|
||||
SelectorError(String),
|
||||
Selector(String),
|
||||
|
||||
/// Error returned when the given command is unknown to Yorokobot.
|
||||
UnknownCommand(String),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let string = match self {
|
||||
Error::InvalidCommand(e) => format!("Received an invalid command: {e}"),
|
||||
Error::ArgumentDeserialization(e) => {
|
||||
format!("Failed to deserialize command argument: {e}")
|
||||
}
|
||||
Error::DatabaseQuery(e) => format!("Failed to execute a database query: {e}"),
|
||||
Error::ContextRetrieval(e) => {
|
||||
format!("Failed to retrieve current session context: {e}")
|
||||
}
|
||||
Error::DiscordAPICall(e) => format!("Error during Discord API call: {e}"),
|
||||
Error::Selector(e) => format!("Met an error while using an embed selector: {e}"),
|
||||
Error::UnknownCommand(e) => format!("Met an unknown bot command: {e}"),
|
||||
};
|
||||
|
||||
write!(f, "{string}")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::InvalidCommand(_) => None,
|
||||
Error::ArgumentDeserialization(_) => None,
|
||||
Error::DatabaseQuery(_) => None,
|
||||
Error::ContextRetrieval(_) => None,
|
||||
Error::DiscordAPICall(error) => error.source(),
|
||||
Error::Selector(_) => None,
|
||||
Error::UnknownCommand(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An option that can/have to be passed to a Discord command.
|
||||
pub struct BotCommandOption {
|
||||
pub name: String,
|
||||
|
@ -67,11 +101,7 @@ pub trait BotCommand {
|
|||
fn options_list() -> Vec<BotCommandOption>;
|
||||
|
||||
/// Execute the command with the given `context` and using the given `database`.
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
database: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError>;
|
||||
async fn run(&self, context: Context, database: Arc<DatabaseClient>) -> Result<(), Error>;
|
||||
|
||||
/// Extract and deserialize the `index`th value from the command `options`.
|
||||
fn extract_option_value(
|
||||
|
|
|
@ -20,7 +20,7 @@ use crate::database::{
|
|||
Client as DatabaseClient,
|
||||
};
|
||||
|
||||
use super::commons::{BotCommand, BotCommandOption, CommandExecutionError};
|
||||
use super::commons::{BotCommand, BotCommandOption, Error};
|
||||
|
||||
pub struct CreateTagCommand {
|
||||
interaction: ApplicationCommandInteraction,
|
||||
|
@ -50,25 +50,21 @@ impl BotCommand for CreateTagCommand {
|
|||
CreateTagCommand { interaction }
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
database: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError> {
|
||||
async fn run(&self, context: Context, database: Arc<DatabaseClient>) -> Result<(), Error> {
|
||||
// Extract tag_name parameter
|
||||
let tag_name = match self.interaction.data.options.first() {
|
||||
Some(a) => match &a.resolved {
|
||||
Some(r) => match r {
|
||||
CommandDataOptionValue::String(r_str) => Ok(r_str),
|
||||
_ => Err(CommandExecutionError::ArgumentDeserializationError(
|
||||
_ => Err(Error::ArgumentDeserialization(
|
||||
"Received non String argument for the CreateTagCommand".to_string(),
|
||||
)),
|
||||
},
|
||||
None => Err(CommandExecutionError::ArgumentDeserializationError(
|
||||
None => Err(Error::ArgumentDeserialization(
|
||||
"Could not deserialize the argument for the CreateTagCommand".to_string(),
|
||||
)),
|
||||
},
|
||||
None => Err(CommandExecutionError::ArgumentExtractionError(
|
||||
None => Err(Error::InvalidCommand(
|
||||
"Failed to get the CreateTagCommand argument".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -76,7 +72,7 @@ impl BotCommand for CreateTagCommand {
|
|||
// Extract guild id from Serenity context
|
||||
let guild_id = match self.interaction.guild_id {
|
||||
Some(a) => Ok(a.to_string()),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Could not fetch guild id from issued command".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -84,11 +80,11 @@ impl BotCommand for CreateTagCommand {
|
|||
let guild = match database.get_by_id::<Guild>(&guild_id).await {
|
||||
Ok(query) => match query {
|
||||
Some(r) => Ok(r),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to retrieve the guild where the command was issued".to_string(),
|
||||
)),
|
||||
},
|
||||
Err(()) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(()) => Err(Error::DatabaseQuery(
|
||||
"Could not access to the database".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -113,25 +109,19 @@ impl BotCommand for CreateTagCommand {
|
|||
.await
|
||||
{
|
||||
Ok(_) => Ok(String::from("Tag successfully created.")),
|
||||
Err(_) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(_) => Err(Error::DatabaseQuery(
|
||||
"Could not add new tag to the database".to_string(),
|
||||
)),
|
||||
}?
|
||||
};
|
||||
|
||||
match self
|
||||
.interaction
|
||||
self.interaction
|
||||
.create_interaction_response(context.http, |response| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|message| message.content(response_content))
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(_e) => Err(CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to answer to the initial command".to_string(),
|
||||
)),
|
||||
}
|
||||
.map_err(Error::DiscordAPICall)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use serenity::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
commons::{BotCommandOption, CommandExecutionError},
|
||||
commons::{BotCommandOption, Error},
|
||||
BotCommand,
|
||||
};
|
||||
|
||||
|
@ -50,31 +50,27 @@ impl BotCommand for DeleteTagCommand {
|
|||
}]
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
database: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError> {
|
||||
async fn run(&self, context: Context, database: Arc<DatabaseClient>) -> Result<(), Error> {
|
||||
let tag_name = match self.interaction.data.options.first() {
|
||||
Some(a) => match &a.resolved {
|
||||
Some(r) => match r {
|
||||
CommandDataOptionValue::String(r_str) => Ok(r_str),
|
||||
_ => Err(CommandExecutionError::ArgumentDeserializationError(
|
||||
_ => Err(Error::ArgumentDeserialization(
|
||||
"Received non String argument for DeleteTagCommand".to_string(),
|
||||
)),
|
||||
},
|
||||
None => Err(CommandExecutionError::ArgumentDeserializationError(
|
||||
None => Err(Error::ArgumentDeserialization(
|
||||
"Failed to deserialize argument for DeleteTagCommand".to_string(),
|
||||
)),
|
||||
},
|
||||
None => Err(CommandExecutionError::ArgumentExtractionError(
|
||||
None => Err(Error::InvalidCommand(
|
||||
"Failed to find argument in DeleteTagCommand".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
let guild_id = match self.interaction.guild_id {
|
||||
Some(r) => Ok(r.to_string()),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to extract guild id from current context".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -82,11 +78,11 @@ impl BotCommand for DeleteTagCommand {
|
|||
let guild = match database.get_by_id::<Guild>(&guild_id).await {
|
||||
Ok(query) => match query {
|
||||
Some(r) => Ok(r),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to retrieve the guild where the command was issued".to_string(),
|
||||
)),
|
||||
},
|
||||
Err(()) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(()) => Err(Error::DatabaseQuery(
|
||||
"Failed to access to the database".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -104,7 +100,7 @@ impl BotCommand for DeleteTagCommand {
|
|||
.await
|
||||
{
|
||||
Ok(_) => Ok(String::from("Successfully remove the tag")),
|
||||
Err(()) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(()) => Err(Error::DatabaseQuery(
|
||||
"Failed to remove tag from the database".to_string(),
|
||||
)),
|
||||
}?
|
||||
|
@ -112,18 +108,12 @@ impl BotCommand for DeleteTagCommand {
|
|||
String::from("No matching tag for this server.")
|
||||
};
|
||||
|
||||
match self
|
||||
.interaction
|
||||
self.interaction
|
||||
.create_interaction_response(context.http, |r| {
|
||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|message| message.content(response))
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => Ok(()),
|
||||
Err(_e) => Err(CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to answer the initial command".to_string(),
|
||||
)),
|
||||
}
|
||||
.map_err(Error::DiscordAPICall)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use serenity::{
|
|||
use crate::database::{models::Guild, Client as DatabaseClient};
|
||||
|
||||
use super::{
|
||||
commons::{BotCommandOption, CommandExecutionError},
|
||||
commons::{BotCommandOption, Error},
|
||||
BotCommand,
|
||||
};
|
||||
|
||||
|
@ -40,14 +40,10 @@ impl BotCommand for ListTagCommand {
|
|||
ListTagCommand { interaction }
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
database: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError> {
|
||||
async fn run(&self, context: Context, database: Arc<DatabaseClient>) -> Result<(), Error> {
|
||||
let guild_id = match self.interaction.guild_id {
|
||||
Some(id) => Ok(id.to_string()),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to extract guild id from current context".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -55,11 +51,11 @@ impl BotCommand for ListTagCommand {
|
|||
let guild = match database.get_by_id::<Guild>(&guild_id).await {
|
||||
Ok(query) => match query {
|
||||
Some(r) => Ok(r),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to retrieve the guild where the command was issued".to_string(),
|
||||
)),
|
||||
},
|
||||
Err(()) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(()) => Err(Error::DatabaseQuery(
|
||||
"Failed to access to the database".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -79,19 +75,13 @@ impl BotCommand for ListTagCommand {
|
|||
}
|
||||
}
|
||||
|
||||
match self
|
||||
.interaction
|
||||
self.interaction
|
||||
.create_interaction_response(context.http, |response| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|message| message.content(response_content))
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => Ok(()),
|
||||
Err(_) => Err(CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to answer to the initial command".to_string(),
|
||||
)),
|
||||
}
|
||||
.map_err(Error::DiscordAPICall)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
discord::message_builders::embed_builder::EmbedMessageBuilder,
|
||||
};
|
||||
|
||||
use super::commons::{BotCommand, BotCommandOption, CommandExecutionError};
|
||||
use super::commons::{BotCommand, BotCommandOption, Error};
|
||||
|
||||
pub struct SourceCodeCommand {
|
||||
interaction: ApplicationCommandInteraction,
|
||||
|
@ -39,14 +39,9 @@ impl BotCommand for SourceCodeCommand {
|
|||
SourceCodeCommand { interaction }
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
_: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError> {
|
||||
async fn run(&self, context: Context, _: Arc<DatabaseClient>) -> Result<(), Error> {
|
||||
let embed_builder = EmbedMessageBuilder::new(&context).await?;
|
||||
match self
|
||||
.interaction
|
||||
self.interaction
|
||||
.create_interaction_response(context.http, |response| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
|
@ -55,11 +50,6 @@ impl BotCommand for SourceCodeCommand {
|
|||
})
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => Ok(()),
|
||||
Err(_e) => Err(CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to answer to the issued command".to_string(),
|
||||
)),
|
||||
}
|
||||
.map_err(Error::DiscordAPICall)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use serenity::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
commons::{BotCommandOption, CommandExecutionError},
|
||||
commons::{BotCommandOption, Error},
|
||||
BotCommand,
|
||||
};
|
||||
use crate::{
|
||||
|
@ -43,26 +43,18 @@ impl BotCommand for SubscribeCommand {
|
|||
vec![]
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
database: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError> {
|
||||
async fn run(&self, context: Context, database: Arc<DatabaseClient>) -> Result<(), Error> {
|
||||
self.interaction
|
||||
.create_interaction_response(&context.http, |response| {
|
||||
response.kind(InteractionResponseType::DeferredChannelMessageWithSource)
|
||||
})
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to answer with a temporary response".to_string(),
|
||||
)
|
||||
})?;
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
|
||||
let user_id = self.interaction.user.id.to_string();
|
||||
let guild_id = match self.interaction.guild_id {
|
||||
Some(id) => Ok(id.to_string()),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to extract guild id from current context".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -70,11 +62,11 @@ impl BotCommand for SubscribeCommand {
|
|||
let guild = match database.get_by_id::<Guild>(&guild_id).await {
|
||||
Ok(query) => match query {
|
||||
Some(r) => Ok(r),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to retrieve the guild where the command was issued".to_string(),
|
||||
)),
|
||||
},
|
||||
Err(()) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(()) => Err(Error::DatabaseQuery(
|
||||
"Failed to access to the database".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -117,7 +109,7 @@ impl BotCommand for SubscribeCommand {
|
|||
)
|
||||
.await
|
||||
.map_err(|_| {
|
||||
CommandExecutionError::DatabaseQueryError(
|
||||
Error::DatabaseQuery(
|
||||
"Failed to update user subscriptions in database".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
@ -125,25 +117,20 @@ impl BotCommand for SubscribeCommand {
|
|||
|
||||
let mut response = match self.interaction.get_interaction_response(&context).await {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_e) => Err(CommandExecutionError::ContextRetrievalError(
|
||||
Err(_e) => Err(Error::ContextRetrieval(
|
||||
"Failed to fetch initial interaction response".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
response.delete_reactions(&context).await.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to remove reactions from initial response".to_string(),
|
||||
)
|
||||
})?;
|
||||
response
|
||||
.delete_reactions(&context)
|
||||
.await
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
|
||||
response
|
||||
.edit(&context, |msg| msg.suppress_embeds(true).content("Done !"))
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to edit content of the original response message".to_string(),
|
||||
)
|
||||
})?;
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use serenity::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
commons::{BotCommandOption, CommandExecutionError},
|
||||
commons::{BotCommandOption, Error},
|
||||
BotCommand,
|
||||
};
|
||||
|
||||
|
@ -46,25 +46,17 @@ impl BotCommand for TagNotifyCommand {
|
|||
vec![]
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
context: Context,
|
||||
database: Arc<DatabaseClient>,
|
||||
) -> Result<(), CommandExecutionError> {
|
||||
async fn run(&self, context: Context, database: Arc<DatabaseClient>) -> Result<(), Error> {
|
||||
self.interaction
|
||||
.create_interaction_response(&context.http, |response| {
|
||||
response.kind(InteractionResponseType::DeferredChannelMessageWithSource)
|
||||
})
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to answer with a temporary response".to_string(),
|
||||
)
|
||||
})?;
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
|
||||
let guild_id = match self.interaction.guild_id {
|
||||
Some(id) => Ok(id.to_string()),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to extract guild id from current context".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -72,11 +64,11 @@ impl BotCommand for TagNotifyCommand {
|
|||
let guild = match database.get_by_id::<Guild>(&guild_id).await {
|
||||
Ok(query) => match query {
|
||||
Some(r) => Ok(r),
|
||||
None => Err(CommandExecutionError::ContextRetrievalError(
|
||||
None => Err(Error::ContextRetrieval(
|
||||
"Failed to retrieve the guild where the command was issued".to_string(),
|
||||
)),
|
||||
},
|
||||
Err(()) => Err(CommandExecutionError::DatabaseQueryError(
|
||||
Err(()) => Err(Error::DatabaseQuery(
|
||||
"Failed to access to the database".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -104,7 +96,7 @@ impl BotCommand for TagNotifyCommand {
|
|||
for selected_tag in selection {
|
||||
let t = match guild.tags.iter().find(|s| s.name == selected_tag) {
|
||||
Some(t) => Ok(t),
|
||||
None => Err(CommandExecutionError::ArgumentExtractionError(
|
||||
None => Err(Error::InvalidCommand(
|
||||
"No matching tag found for selection".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -128,16 +120,15 @@ impl BotCommand for TagNotifyCommand {
|
|||
|
||||
let response = match self.interaction.get_interaction_response(&context).await {
|
||||
Ok(r) => Ok(r),
|
||||
Err(_e) => Err(CommandExecutionError::ContextRetrievalError(
|
||||
Err(_e) => Err(Error::ContextRetrieval(
|
||||
"Failed to fetch initial interaction response".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
response.delete(&context).await.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to remove the embed message".to_string(),
|
||||
)
|
||||
})?;
|
||||
response
|
||||
.delete(&context)
|
||||
.await
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
|
||||
// We have to create a new message as editing the original response will not notify
|
||||
// the pinged users
|
||||
|
@ -150,20 +141,12 @@ impl BotCommand for TagNotifyCommand {
|
|||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to create a new message to ping users".to_string(),
|
||||
)
|
||||
})?;
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
} else {
|
||||
self.interaction
|
||||
.delete_original_interaction_response(&context)
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to delete the original interaction message".to_string(),
|
||||
)
|
||||
})?;
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -8,7 +8,7 @@ use super::commands::{
|
|||
|
||||
use crate::{
|
||||
database::{models::Guild, Client as DatabaseClient},
|
||||
discord::commands::{commons::CommandExecutionError, DeleteTagCommand, SubscribeCommand},
|
||||
discord::commands::{commons::Error, DeleteTagCommand, SubscribeCommand},
|
||||
};
|
||||
use serenity::{
|
||||
async_trait,
|
||||
|
@ -153,7 +153,7 @@ impl EventHandler for Handler {
|
|||
.await
|
||||
} else {
|
||||
drop(users_selector);
|
||||
Err(CommandExecutionError::SelectorError(
|
||||
Err(Error::Selector(
|
||||
"User has already a selector running".to_string(),
|
||||
))
|
||||
}
|
||||
|
@ -170,13 +170,13 @@ impl EventHandler for Handler {
|
|||
.await
|
||||
} else {
|
||||
drop(users_selector);
|
||||
Err(CommandExecutionError::SelectorError(
|
||||
Err(Error::Selector(
|
||||
"User has already a selector running".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
_ => Err(CommandExecutionError::UnknownCommand(
|
||||
_ => Err(Error::UnknownCommand(
|
||||
"Received an unknown command from Discord".to_string(),
|
||||
)),
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ use serenity::{
|
|||
prelude::Context,
|
||||
};
|
||||
|
||||
use crate::discord::commands::commons::CommandExecutionError;
|
||||
use crate::discord::commands::commons::Error;
|
||||
|
||||
const HTML_COLOR_CODE: u32 = 0xffffff;
|
||||
|
||||
|
@ -16,12 +16,11 @@ pub struct EmbedMessageBuilder {
|
|||
}
|
||||
|
||||
impl EmbedMessageBuilder {
|
||||
pub async fn new(context: &Context) -> Result<Self, CommandExecutionError> {
|
||||
let bot_user = context.http.get_current_user().await.map_err(|_e| {
|
||||
CommandExecutionError::ContextRetrievalError(
|
||||
"Failed to get current bot user".to_string(),
|
||||
)
|
||||
})?;
|
||||
pub async fn new(context: &Context) -> Result<Self, Error> {
|
||||
let bot_user =
|
||||
context.http.get_current_user().await.map_err(|_e| {
|
||||
Error::ContextRetrieval("Failed to get current bot user".to_string())
|
||||
})?;
|
||||
|
||||
let embed_author = bot_user.name.clone();
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use serenity::{
|
|||
prelude::Context,
|
||||
};
|
||||
|
||||
use crate::discord::commands::commons::CommandExecutionError;
|
||||
use crate::discord::commands::commons::Error;
|
||||
|
||||
use super::embed_builder::EmbedMessageBuilder;
|
||||
|
||||
|
@ -83,9 +83,7 @@ impl<'a> EmbedSelector<'a> {
|
|||
selector
|
||||
}
|
||||
|
||||
pub async fn get_user_selection(
|
||||
&mut self,
|
||||
) -> Result<Option<HashSet<String>>, CommandExecutionError> {
|
||||
pub async fn get_user_selection(&mut self) -> Result<Option<HashSet<String>>, Error> {
|
||||
let embed_builder = EmbedMessageBuilder::new(self.context).await?;
|
||||
|
||||
match self
|
||||
|
@ -107,19 +105,16 @@ impl<'a> EmbedSelector<'a> {
|
|||
self.display_reactions().await?;
|
||||
Ok(self.wait_selector_end().await?)
|
||||
}
|
||||
Err(_e) => Err(CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to edit original interaction response".to_string(),
|
||||
)),
|
||||
Err(e) => Err(Error::DiscordAPICall(e)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn display_reactions(&self) -> Result<(), CommandExecutionError> {
|
||||
async fn display_reactions(&self) -> Result<(), Error> {
|
||||
if let Some(answer) = &self.embed_answer {
|
||||
answer.delete_reactions(self.context).await.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to delete reaction on the current selector".to_string(),
|
||||
)
|
||||
})?;
|
||||
answer
|
||||
.delete_reactions(self.context)
|
||||
.await
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
|
||||
for emote in SELECTION_EMOTES[0..self.get_current_page_choice_number()]
|
||||
.iter()
|
||||
|
@ -137,30 +132,24 @@ impl<'a> EmbedSelector<'a> {
|
|||
Some(a) => a
|
||||
.react(self.context, ReactionType::Unicode(emote.to_string()))
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to add reactions on the current selector".to_string(),
|
||||
)
|
||||
}),
|
||||
None => Err(CommandExecutionError::SelectorError(
|
||||
.map_err(Error::DiscordAPICall),
|
||||
None => Err(Error::Selector(
|
||||
"Failed to refresh the reactions of the current selector".to_string(),
|
||||
)),
|
||||
}?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CommandExecutionError::SelectorError(
|
||||
Err(Error::Selector(
|
||||
"Tried to delete reaction from a non existent message".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_selector_end(
|
||||
&mut self,
|
||||
) -> Result<Option<HashSet<String>>, CommandExecutionError> {
|
||||
async fn wait_selector_end(&mut self) -> Result<Option<HashSet<String>>, Error> {
|
||||
let answer = match &self.embed_answer {
|
||||
Some(a) => Ok(a),
|
||||
None => Err(CommandExecutionError::SelectorError(
|
||||
None => Err(Error::Selector(
|
||||
"Tried to start collector before sending it".to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
@ -171,11 +160,7 @@ impl<'a> EmbedSelector<'a> {
|
|||
.add_message_id(*answer.id.as_u64())
|
||||
.timeout(Duration::from_secs(COLLECTOR_MAX_DURATION_SEC))
|
||||
.build()
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::SelectorError(
|
||||
"Failed to build the EventCollector".to_string(),
|
||||
)
|
||||
})?;
|
||||
.map_err(|_e| Error::Selector("Failed to build the EventCollector".to_string()))?;
|
||||
|
||||
while let Some(reaction_event) = collector.next().await {
|
||||
let reaction = match *reaction_event {
|
||||
|
@ -232,11 +217,10 @@ impl<'a> EmbedSelector<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
reaction.delete(self.context).await.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to delete reaction from selector".to_string(),
|
||||
)
|
||||
})?;
|
||||
reaction
|
||||
.delete(self.context)
|
||||
.await
|
||||
.map_err(Error::DiscordAPICall)?;
|
||||
}
|
||||
}
|
||||
collector.stop();
|
||||
|
@ -248,7 +232,7 @@ impl<'a> EmbedSelector<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
async fn next_page(&mut self) -> Result<(), CommandExecutionError> {
|
||||
async fn next_page(&mut self) -> Result<(), Error> {
|
||||
if self.current_page != self.page_number {
|
||||
self.current_page += 1;
|
||||
self.refresh_embed_selection().await?;
|
||||
|
@ -256,7 +240,7 @@ impl<'a> EmbedSelector<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn previous_page(&mut self) -> Result<(), CommandExecutionError> {
|
||||
async fn previous_page(&mut self) -> Result<(), Error> {
|
||||
if self.current_page != 1 {
|
||||
self.current_page -= 1;
|
||||
self.refresh_embed_selection().await?;
|
||||
|
@ -274,7 +258,7 @@ impl<'a> EmbedSelector<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
async fn refresh_embed_selection(&mut self) -> Result<(), CommandExecutionError> {
|
||||
async fn refresh_embed_selection(&mut self) -> Result<(), Error> {
|
||||
let embed_builder = EmbedMessageBuilder::new(self.context).await?;
|
||||
|
||||
let curr_choices = self.selectable
|
||||
|
@ -295,10 +279,6 @@ impl<'a> EmbedSelector<'a> {
|
|||
))
|
||||
})
|
||||
.await
|
||||
.map_err(|_e| {
|
||||
CommandExecutionError::DiscordAPICallError(
|
||||
"Failed to edit selector content".to_string(),
|
||||
)
|
||||
})
|
||||
.map_err(Error::DiscordAPICall)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue