use std::error::Error;
use std::sync::Arc;
use twilight_http::request::channel::reaction::RequestReactionType;
use twilight_model::id::Id;
use twilight_model::id::marker::{ChannelMarker, MessageMarker};
use crate::aliases::DiscordClient;
use crate::cache::Cache;
use crate::commands::CommandNode;
use crate::commands::prefixed::prefixes::Prefixes;
use crate::errors::ErrorHandlerWithoutType;
use crate::state::StateBound;
use crate::wrappers::TwilightError;
use crate::wrappers::actions::message_create::MessageCreate;
use crate::wrappers::types::users::User;
#[derive(Clone)]
pub struct Handle<State>
where
State: StateBound,
{
pub client: DiscordClient,
pub commands: Arc<Vec<CommandNode<State>>>,
pub(crate) prefixes: Option<Arc<dyn Prefixes<State>>>,
pub(crate) on_errors: Vec<Arc<dyn ErrorHandlerWithoutType<State>>>,
pub cache: Option<Arc<dyn Cache>>,
}
#[derive(Debug, thiserror::Error)]
pub enum HandleError {
#[error("An error occurred while calling the Discord API: {0}")]
Twilight(#[from] TwilightError),
#[error("The cache backend returned an error: {0}")]
Cache(#[from] Box<dyn Error + Send + Sync>),
#[error("The emoji passed was not a valid one.")]
InvalidEmoji,
}
impl<State> Handle<State>
where
State: StateBound,
{
pub fn send(&self, channel_id: Id<ChannelMarker>, content: impl Into<String>) -> MessageCreate {
MessageCreate::new(self.client.clone(), channel_id, content)
}
pub async fn fetch_user(&self, user_id: u64) -> Result<User, HandleError> {
let user: User = self
.client
.user(Id::new(user_id))
.await
.map_err(TwilightError::Twilight)?
.model()
.await
.map_err(TwilightError::TwilightParsing)?
.into();
if let Some(cache) = &self.cache {
cache.set_user(user.clone()).await?;
}
Ok(user)
}
pub async fn get_or_fetch_user(&self, user_id: u64) -> Result<User, HandleError> {
if let Some(cache) = &self.cache {
let user = cache.get_user_by_id(user_id).await?;
if let Some(user) = user {
return Ok(user);
}
}
self.fetch_user(user_id).await
}
pub async fn add_reaction(
&self,
channel_id: Id<ChannelMarker>,
message_id: Id<MessageMarker>,
emoji: impl IntoRequestReactionType<'_>,
) -> Result<(), HandleError> {
self.client
.create_reaction(channel_id, message_id, &emoji.into_request_reaction_type()?)
.await
.map_err(TwilightError::from)?;
Ok(())
}
}
pub trait IntoRequestReactionType<'a> {
fn into_request_reaction_type(self) -> Result<RequestReactionType<'a>, HandleError>;
}
impl<'a, T> IntoRequestReactionType<'a> for T
where
T: Into<String>,
{
fn into_request_reaction_type(self) -> Result<RequestReactionType<'a>, HandleError> {
let string = self.into();
match emojis::get(&string) {
Some(emoji) => Ok(RequestReactionType::Unicode {
name: emoji.as_str(),
}),
None => match emojis::get_by_shortcode(&string) {
Some(emoji) => Ok(RequestReactionType::Unicode {
name: emoji.as_str(),
}),
None => Ok(RequestReactionType::Custom {
id: string.parse().map_err(|_| HandleError::InvalidEmoji)?,
name: None,
}),
},
}
}
}