ephemeral & better errors

This commit is contained in:
Stachelbeere1248 2024-11-14 17:28:11 +01:00
parent a18ea80d1a
commit 52b61d47ca
Signed by: Stachelbeere1248
SSH key fingerprint: SHA256:IozEKdw2dB8TZxkpPdMxcWSoWTIMwoLaCcZJ1AJnY2o
9 changed files with 69 additions and 48 deletions

View file

@ -6,11 +6,11 @@ use serenity::{
json::JsonError, json::JsonError,
}; };
use serenity::all::ButtonStyle; use serenity::all::ButtonStyle;
use sqlx::{query_as, Pool, Sqlite}; use sqlx::{Pool, query_as, Sqlite};
use crate::commands::command_helper::cooldown; use crate::commands::command_helper::cooldown;
use crate::error::Error;
use crate::Context; use crate::Context;
use crate::error::Error;
#[derive(Deserialize)] #[derive(Deserialize)]
struct Links { struct Links {
@ -145,7 +145,7 @@ pub(crate) async fn add<'a>(
#[max_length = 16] #[max_length = 16]
ign: String, ign: String,
#[description = "Discord User"] user: Option<User>, #[description = "Discord User"] user: Option<User>,
#[description = "Minecraft username"] force: Option<bool>, #[description = "admin-only"] force: Option<bool>,
) -> Result<(), Error> { ) -> Result<(), Error> {
ctx.defer_ephemeral().await?; ctx.defer_ephemeral().await?;
let user: User = user.unwrap_or(ctx.author().clone()); let user: User = user.unwrap_or(ctx.author().clone());
@ -187,20 +187,20 @@ pub(crate) async fn add<'a>(
mc_id.cast_signed(), mc_id.cast_signed(),
dc_id.cast_signed() dc_id.cast_signed()
) )
.as_str(), .as_str(),
) )
.execute(&pool) .execute(&pool)
.await?; .await?;
sqlx::query( sqlx::query(
format!( format!(
"UPDATE discord_links SET link_id = {} WHERE link_id = {};", "UPDATE discord_links SET link_id = {} WHERE link_id = {};",
mc_id.cast_signed(), mc_id.cast_signed(),
dc_id.cast_signed() dc_id.cast_signed()
) )
.as_str(), .as_str(),
) )
.execute(&pool) .execute(&pool)
.await?; .await?;
"Both your Discord and Minecraft account had linked accounts. Merged all account links." "Both your Discord and Minecraft account had linked accounts. Merged all account links."
} }
}, },
@ -213,9 +213,15 @@ pub(crate) async fn add<'a>(
.content(s) .content(s)
.allowed_mentions(CreateAllowedMentions::new().empty_roles().all_users(true)) .allowed_mentions(CreateAllowedMentions::new().empty_roles().all_users(true))
.components(vec![CreateActionRow::Buttons(vec![ .components(vec![CreateActionRow::Buttons(vec![
CreateButton::new("accept_verification").emoji(ReactionType::from('✅')).style(ButtonStyle::Primary), CreateButton::new("accept_verification")
CreateButton::new("deny_verification").emoji(ReactionType::from('❌')).style(ButtonStyle::Primary), .emoji(ReactionType::from('✅'))
CreateButton::new("list_accounts").emoji(ReactionType::from('📜')).style(ButtonStyle::Secondary), .style(ButtonStyle::Primary),
CreateButton::new("deny_verification")
.emoji(ReactionType::from('❌'))
.style(ButtonStyle::Primary),
CreateButton::new("list_accounts")
.emoji(ReactionType::from('📜'))
.style(ButtonStyle::Secondary),
])]), ])]),
) )
.await?; .await?;
@ -242,7 +248,7 @@ pub(crate) async fn list(ctx: Context<'_>, user: Option<User>) -> Result<(), Err
r.content(s) r.content(s)
.allowed_mentions(CreateAllowedMentions::new().empty_roles().empty_users()), .allowed_mentions(CreateAllowedMentions::new().empty_roles().empty_users()),
) )
.await?; .await?;
Ok(()) Ok(())
} }
@ -285,12 +291,12 @@ async fn link_id_from_discord(pool: &Pool<Sqlite>, snowflake: u64) -> Option<u16
"SELECT link_id FROM discord_links WHERE discord_id = {} LIMIT 1;", "SELECT link_id FROM discord_links WHERE discord_id = {} LIMIT 1;",
snowflake.cast_signed() snowflake.cast_signed()
) )
.as_str(), .as_str(),
) )
.fetch_optional(pool) .fetch_optional(pool)
.await .await
.expect("Database error: fetching link id by discord") .expect("Database error: fetching link id by discord")
.map(|link_id: LinkId| link_id.link_id.cast_unsigned()) .map(|link_id: LinkId| link_id.link_id.cast_unsigned())
} }
#[derive(sqlx::FromRow)] #[derive(sqlx::FromRow)]

View file

@ -2,8 +2,8 @@ use std::string::String;
use poise::CreateReply; use poise::CreateReply;
use crate::error::Error;
use crate::Context; use crate::Context;
use crate::error::Error;
#[poise::command(slash_command, guild_only, owners_only)] #[poise::command(slash_command, guild_only, owners_only)]
pub(crate) async fn bots( pub(crate) async fn bots(

View file

@ -1,7 +1,7 @@
use std::time::Duration; use std::time::Duration;
use crate::error::Error;
use crate::Context; use crate::Context;
use crate::error::Error;
pub(crate) fn cooldown(ctx: &Context, user: u64, guild: u64) -> Result<(), Error> { pub(crate) fn cooldown(ctx: &Context, user: u64, guild: u64) -> Result<(), Error> {
let mut cooldown_tracker = ctx.command().cooldowns.lock().unwrap(); let mut cooldown_tracker = ctx.command().cooldowns.lock().unwrap();

View file

@ -2,8 +2,8 @@ use poise::CreateReply;
use serenity::all::CreateAllowedMentions; use serenity::all::CreateAllowedMentions;
use crate::commands::command_helper; use crate::commands::command_helper;
use crate::error::Error;
use crate::Context; use crate::Context;
use crate::error::Error;
#[poise::command(slash_command, guild_only)] #[poise::command(slash_command, guild_only)]
pub(crate) async fn helpstart( pub(crate) async fn helpstart(

View file

@ -7,8 +7,8 @@ use crate::commands::command_helper::cooldown;
use crate::commands::lfg::Difficulty::Normal; use crate::commands::lfg::Difficulty::Normal;
use crate::commands::lfg::Map::*; use crate::commands::lfg::Map::*;
use crate::commands::lfg::Mode::*; use crate::commands::lfg::Mode::*;
use crate::error::Error;
use crate::Context; use crate::Context;
use crate::error::Error;
#[derive(Debug, poise::ChoiceParameter, PartialEq)] #[derive(Debug, poise::ChoiceParameter, PartialEq)]
pub enum Map { pub enum Map {
@ -96,7 +96,7 @@ pub(crate) async fn lfg(
AlienArcadium => Normal, AlienArcadium => Normal,
}; };
let mut reply_content: String = format!("<@&{ping}> {current}/{desired} {map_name}",); let mut reply_content: String = format!("<@&{ping}> {current}/{desired} {map_name}", );
match difficulty { match difficulty {
Normal => {} Normal => {}
Difficulty::Hard | Difficulty::Rip => { Difficulty::Hard | Difficulty::Rip => {

View file

@ -1,5 +1,5 @@
use crate::error::Error;
use crate::Context; use crate::Context;
use crate::error::Error;
const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\\ const XD: &str = "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\n⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⡿⠿⠿⠿⠿⠿⠿⠿⢿⡿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿\\
n\n\\ n\n\\

View file

@ -1,3 +1,15 @@
use poise::{CreateReply, FrameworkError};
use crate::Data;
macro_rules! reply_fail_handler {
($fut:expr) => {{
if let Err(e) = $fut.await {
tracing::error!("Fatal error while sending error message: {}", e);
}
}};
}
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
SqlxError(sqlx::Error), SqlxError(sqlx::Error),
@ -14,7 +26,7 @@ impl std::fmt::Display for Error {
Error::SqlxError(e) => write!(f, "SQLx Error: {}", e), Error::SqlxError(e) => write!(f, "SQLx Error: {}", e),
Error::HttpsError(e) => write!(f, "HTTPS Error (Hypixel / Mojang API):\n{}", e), Error::HttpsError(e) => write!(f, "HTTPS Error (Hypixel / Mojang API):\n{}", e),
Error::ParsingError(e) => write!(f, "Parsing Error:\n {}", e), Error::ParsingError(e) => write!(f, "Parsing Error:\n {}", e),
Error::SerenityError(e) => write!(f, "Serenity Error:\n {}", e), Error::SerenityError(e) => write!(f, "Discord Error:\n {}", e),
Error::OnCooldown(d) => { Error::OnCooldown(d) => {
write!(f, "This command is on cooldown. {}s remaining.", d.as_secs()) write!(f, "This command is on cooldown. {}s remaining.", d.as_secs())
} }
@ -46,3 +58,23 @@ impl From<serenity::Error> for Error {
Error::SerenityError(error) Error::SerenityError(error)
} }
} }
pub(crate) async fn handle_error<'a>(error: FrameworkError<'a, Data, Error>) {
match error {
FrameworkError::Command { error, ctx, .. } => {
reply_fail_handler!(ctx.send(CreateReply::default().content(error.to_string()).ephemeral(true)))
},
FrameworkError::CommandStructureMismatch { description, ctx, .. } => {
reply_fail_handler!(ctx.send(
CreateReply::default()
.content(format!(
"# Command arguments did not match. The command probably has been updated recently. Try reloading Discord. \
Description:\n{}",
description
))
.ephemeral(true)
))
}
other => reply_fail_handler!(poise::builtins::on_error(other)),
}
}

View file

@ -1,3 +1,4 @@
use serenity::all::{ButtonStyle, ComponentInteraction};
use serenity::all::ButtonStyle::Success; use serenity::all::ButtonStyle::Success;
use serenity::all::ComponentInteractionDataKind; use serenity::all::ComponentInteractionDataKind;
use serenity::all::Context; use serenity::all::Context;
@ -11,10 +12,9 @@ use serenity::all::GuildId;
use serenity::all::Interaction; use serenity::all::Interaction;
use serenity::all::ReactionType; use serenity::all::ReactionType;
use serenity::all::RoleId; use serenity::all::RoleId;
use serenity::all::{ButtonStyle, ComponentInteraction};
use crate::error::Error;
use crate::Data; use crate::Data;
use crate::error::Error;
pub(crate) async fn component(ctx: &Context, interaction: &Interaction, data: &Data) -> Result<(), Error> { pub(crate) async fn component(ctx: &Context, interaction: &Interaction, data: &Data) -> Result<(), Error> {
let component = interaction.clone().message_component().unwrap(); let component = interaction.clone().message_component().unwrap();

View file

@ -5,10 +5,10 @@ use std::convert::Into;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use poise::{serenity_prelude as serenity, CreateReply, FrameworkError}; use poise::serenity_prelude as serenity;
use serenity::{FullEvent, model::id::UserId};
use serenity::all::{ActivityData, InteractionType, RoleId}; use serenity::all::{ActivityData, InteractionType, RoleId};
use serenity::prelude::GatewayIntents; use serenity::prelude::GatewayIntents;
use serenity::{model::id::UserId, FullEvent};
use sqlx::Sqlite; use sqlx::Sqlite;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -56,25 +56,7 @@ async fn main() {
}, },
on_error: |error| { on_error: |error| {
Box::pin(async move { Box::pin(async move {
match error { error::handle_error(error).await;
FrameworkError::CommandStructureMismatch { description, ctx, .. } => {
if let Err(e) = ctx
.send(CreateReply::default().content(format!(
"# Command arguments did not match. The command probably has been updated recently. Try reloading \
Discord. Description:\n{}",
description
)))
.await
{
tracing::error!("Fatal error while sending error message: {}", e);
}
}
other => {
if let Err(e) = poise::builtins::on_error(other).await {
tracing::error!("Fatal error while sending error message: {}", e);
}
}
}
}) })
}, },
owners: { HashSet::from([UserId::new(449579075531440128_u64), UserId::new(659112817508745216_u64)]) }, owners: { HashSet::from([UserId::new(449579075531440128_u64), UserId::new(659112817508745216_u64)]) },
@ -104,6 +86,7 @@ async fn main() {
.await; .await;
client.unwrap().start_autosharded().await.unwrap() client.unwrap().start_autosharded().await.unwrap()
} }
async fn event_handler( async fn event_handler(
ctx: &serenity::Context, ctx: &serenity::Context,
event: &FullEvent, event: &FullEvent,