From 67f6779b385becc17636735325fe1a537be22763 Mon Sep 17 00:00:00 2001 From: HadziqM Date: Sat, 9 Nov 2024 22:07:49 +0700 Subject: [PATCH] add error handling --- common/src/setting.rs | 23 ++++----- rain-bot/src/error.rs | 111 ++++++++++++++++++++++++++++++++++++++++++ rain-bot/src/setup.rs | 54 ++++++++++++++++++-- 3 files changed, 173 insertions(+), 15 deletions(-) diff --git a/common/src/setting.rs b/common/src/setting.rs index 05909ad..2644e4c 100644 --- a/common/src/setting.rs +++ b/common/src/setting.rs @@ -122,16 +122,17 @@ pub struct SettingDiscord { #[derive(Serialize, Deserialize, Clone)] pub struct DiscordChannelSetting { - pub log_channel: String, - pub transfer_channel: String, - pub bounty_submision: String, - pub bounty_title: String, - pub speedrun_submision: String, - pub speedrun_leaderboard_channel: String, - pub speedrun_leaderboard_msg: String, - pub market_channel: String, - pub market_menu_channel: String, - pub market_menu_msg: String, + pub log_channel: u64, + pub error_channel: u64, + pub transfer_channel: u64, + pub bounty_submision: u64, + pub bounty_title: u64, + pub speedrun_submision: u64, + pub speedrun_leaderboard_channel: u64, + pub speedrun_leaderboard_msg: u64, + pub market_channel: u64, + pub market_menu_channel: u64, + pub market_menu_msg: u64, } #[derive(Serialize, Deserialize, Clone)] @@ -147,7 +148,7 @@ pub struct DiscordServerRole { pub struct DiscordBotSetting { pub token: String, pub webhook: String, - pub author: String, + pub author: u64, } #[derive(Serialize, Deserialize, Clone)] pub struct DatabaseSetting { diff --git a/rain-bot/src/error.rs b/rain-bot/src/error.rs index 3d2fb5b..c5df5d2 100644 --- a/rain-bot/src/error.rs +++ b/rain-bot/src/error.rs @@ -1,3 +1,8 @@ +use common::setting::SettingAll; +use serenity::all::{ + ChannelId, Color, Context, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, + CreateInteractionResponse, CreateInteractionResponseMessage, CreateMessage, User, UserId, +}; use thiserror::Error; #[derive(Debug, Error)] @@ -5,3 +10,109 @@ pub enum MyError { #[error("Test")] Test, } + +enum Severity { + Critical, + FalsePossitive, + CanBeHandledManually, +} + +impl MyError { + pub fn severity(&self) -> Severity { + todo!() + } + pub fn advice(&self) -> String { + todo!() + } + pub fn log(&self) { + todo!() + } +} + +pub struct ErrorHandling { + pub err: MyError, + pub author: User, + pub effected_user: User, + pub location: String, + pub log_channel: u64, +} + +impl ErrorHandling { + pub async fn new( + err: MyError, + ctx: &Context, + setting: &SettingAll, + effected_user: User, + location: String, + ) -> Self { + let thor = UserId::new(setting.main.discord.author); + let author = thor.to_user(&ctx.http).await.unwrap_or_default(); + Self { + err, + author, + effected_user, + location, + log_channel: setting.discord.channel.error_channel, + } + } + + pub fn response(&self) -> CreateInteractionResponse { + CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new().embed(self.embed()), + ) + } + pub async fn channel_send(&self, ctx: &Context) { + let ch = ChannelId::new(self.log_channel); + if ch + .send_message( + &ctx.http, + CreateMessage::new() + .embed(self.embed()) + .content(format!("for {}", self.effected_user)), + ) + .await + .is_err() + { + log::error!( + "error sending log to channel, the error is: {:?}, on: {}, and user effected: {}", + self.err, + self.location, + self.effected_user.name + ) + } + } + + fn embed(&self) -> CreateEmbed { + let color = match self.err.severity() { + Severity::Critical => Color::RED, + Severity::FalsePossitive => Color::DARK_GREEN, + Severity::CanBeHandledManually => Color::ORANGE, + }; + CreateEmbed::default() + .color(color) + .title("🛑 Error Occured 🛑") + .description("some cant be handled error occured") + .fields(vec![ + ( + "🚧 occured on", + format!("**{}**", self.location.to_uppercase()), + false, + ), + ("📜 error message", format!("> {}", &self.err), false), + ( + "⛑ author advice", + format!("```\n{}\n```", &self.err.advice()), + false, + ), + ]) + .author( + CreateEmbedAuthor::new(&self.effected_user.name) + .icon_url(self.effected_user.face()), + ) + .footer( + CreateEmbedFooter::new(format!("you can consult this to {}", self.author.tag())) + .icon_url(self.author.face()), + ) + .thumbnail("https://media.discordapp.net/attachments/1068877712841789490/1303771799246344272/panics.png?ex=6730435b&is=672ef1db&hm=5f6e31b20eb02607c29ae1a961d97b694dee45ccbdc7889638edc4a6f93903a3&=&format=webp&quality=lossless&width=1000&height=660") + } +} diff --git a/rain-bot/src/setup.rs b/rain-bot/src/setup.rs index e44272f..f6c88b3 100644 --- a/rain-bot/src/setup.rs +++ b/rain-bot/src/setup.rs @@ -3,7 +3,7 @@ use serenity::all::*; use std::{collections::HashMap, sync::Arc}; use tokio::sync::RwLock; -use crate::error::MyError; +use crate::error::{ErrorHandling, MyError}; pub type MyResult = Result; @@ -19,15 +19,61 @@ pub struct DiscordHandler { #[async_trait] pub trait CommandInteractionTrait: Sync + Send + 'static { async fn hand(&self, app: Arc, cmd: CommandInteraction, ctx: Context) { - if let Err(e) = Self::handle(app, cmd, ctx).await { - // TODO: Proper error handling - log::error!("there is error {:?}", e); + if let Err(e) = Self::handle(app.clone(), cmd.clone(), ctx.clone()).await { + e.log(); + let setting = app.setting.read().await; + let err = ErrorHandling::new(e, &ctx, &setting, cmd.user.clone(), Self::name()).await; + if cmd + .create_response(&ctx.http, err.response()) + .await + .is_err() + { + err.channel_send(&ctx).await + } } } async fn handle(app: Arc, cmd: CommandInteraction, ctx: Context) -> MyResult<()>; fn command() -> CreateCommand; fn name() -> String; } +#[async_trait] +pub trait ButtonInteractionTrait: Sync + Send + 'static { + async fn hand(&self, app: Arc, cmd: ComponentInteraction, ctx: Context) { + if let Err(e) = Self::handle(app.clone(), cmd.clone(), ctx.clone()).await { + e.log(); + let setting = app.setting.read().await; + let err = ErrorHandling::new(e, &ctx, &setting, cmd.user.clone(), Self::name()).await; + if cmd + .create_response(&ctx.http, err.response()) + .await + .is_err() + { + err.channel_send(&ctx).await + } + } + } + async fn handle(app: Arc, cmd: ComponentInteraction, ctx: Context) -> MyResult<()>; + fn name() -> String; +} +#[async_trait] +pub trait ModalInteractionTrait: Sync + Send + 'static { + async fn hand(&self, app: Arc, cmd: ModalInteraction, ctx: Context) { + if let Err(e) = Self::handle(app.clone(), cmd.clone(), ctx.clone()).await { + e.log(); + let setting = app.setting.read().await; + let err = ErrorHandling::new(e, &ctx, &setting, cmd.user.clone(), Self::name()).await; + if cmd + .create_response(&ctx.http, err.response()) + .await + .is_err() + { + err.channel_send(&ctx).await + } + } + } + async fn handle(app: Arc, cmd: ModalInteraction, ctx: Context) -> MyResult<()>; + fn name() -> String; +} #[async_trait] impl EventHandler for DiscordHandler {