use async_trait::async_trait; use twilight_model::channel::message::component::ButtonStyle; use crate::{ database::models::UserIncident, extras::Extras, framework::interactions::{InteractionError, components::Button, context::InteractionContext}, groups::moderation::get_incident_page, state::State, utils::get_random_u64, }; #[derive(Debug)] pub struct ArrowIncidentButton { pub arrow: &'static str, pub user_id: u64, pub go_to: Option, } #[async_trait] impl Button for ArrowIncidentButton { fn id(&self) -> Option { Some(format!("incidents:arrow:{}", get_random_u64())) } fn label(&self) -> Option { Some(format!("{}", self.arrow)) } fn disabled(&self) -> bool { self.go_to.is_none() } async fn on_press( &self, ctx: InteractionContext, ) -> Result<(), InteractionError> { if !ctx.extras.user.is_moderator { ctx.defer().await?; return Ok(()); } if let Some(go_to) = self.go_to { ctx.edit_original_message( get_incident_page(self.user_id, &ctx.rt.state.db, go_to) .await .inspect_err(|e| log::error!("Failed to get incident page: {e:?}"))? .into(), ) .await .inspect_err(|e| log::error!("Failed to edit original message: {e:?}"))?; } else { ctx.defer().await?; } Ok(()) } } #[derive(Debug)] pub struct CounterButton { pub current: usize, pub total: usize, } #[async_trait] impl Button for CounterButton { fn id(&self) -> Option { Some("incidents:counter".into()) } fn label(&self) -> Option { Some(format!("{}/{}", self.current, self.total)) } fn disabled(&self) -> bool { true } } #[derive(Debug)] pub struct ExpireIncidentButton { pub user_id: u64, pub incident_id: u64, pub incident_index: usize, } #[async_trait] impl Button for ExpireIncidentButton { fn id(&self) -> Option { Some(format!("incidents:expire:{}", get_random_u64())) } fn label(&self) -> Option { Some("Expire Incident".into()) } fn style(&self) -> ButtonStyle { ButtonStyle::Danger } fn disabled(&self) -> bool { false } async fn on_press( &self, ctx: InteractionContext, ) -> Result<(), InteractionError> { if !ctx.extras.user.is_moderator { ctx.defer().await?; return Ok(()); } UserIncident::expire_by_user_and_incident_id( self.user_id, self.incident_id, &ctx.rt.state.db, ) .await?; ctx.edit_original_message( get_incident_page(self.user_id, &ctx.rt.state.db, self.incident_index) .await .inspect_err(|e| log::error!("Failed to get incident page: {e:?}"))? .into(), ) .await .inspect_err(|e| log::error!("Failed to edit original message: {e:?}"))?; Ok(()) } } #[derive(Debug)] pub struct DeleteIncidentButton { pub user_id: u64, pub incident_id: u64, pub incident_index: usize, } #[async_trait] impl Button for DeleteIncidentButton { fn id(&self) -> Option { Some(format!("incidents:delete:{}", get_random_u64())) } fn label(&self) -> Option { Some("Delete Incident".into()) } fn style(&self) -> ButtonStyle { ButtonStyle::Danger } fn disabled(&self) -> bool { false } async fn on_press( &self, ctx: InteractionContext, ) -> Result<(), InteractionError> { if !ctx.extras.user.is_moderator { ctx.defer().await?; return Ok(()); } UserIncident::delete_by_user_and_incident_id( self.user_id, self.incident_id, &ctx.rt.state.db, ) .await?; let new_page_index = self.incident_index.saturating_sub(1); ctx.edit_original_message( get_incident_page(self.user_id, &ctx.rt.state.db, new_page_index) .await .inspect_err(|e| log::error!("Failed to get incident page: {e:?}"))? .into(), ) .await .inspect_err(|e| log::error!("Failed to edit original message: {e:?}"))?; Ok(()) } }