Skip to main content

stoat/commands/
checks.rs

1use std::{fmt::Debug, sync::Arc};
2
3use async_fn_traits::AsyncFn1;
4use async_trait::async_trait;
5use stoat_models::v0::Channel;
6use stoat_permissions::ChannelPermission;
7
8use crate::{Error, commands::Context};
9
10#[async_trait]
11pub trait Check<
12    E: From<Error> + Clone + Debug + Send + Sync + 'static,
13    S: Debug + Clone + Send + Sync + 'static,
14>: Send + Sync + 'static
15{
16    async fn run(&self, context: Context<E, S>) -> Result<bool, E>;
17}
18
19#[async_trait]
20impl<E, S, F> Check<E, S> for F
21where
22    E: From<Error> + Clone + Debug + Send + Sync + 'static,
23    S: Debug + Clone + Send + Sync + 'static,
24    F: AsyncFn1<Context<E, S>, Output = Result<bool, E>> + Send + Sync + 'static,
25    F::OutputFuture: Send + Sync,
26{
27    async fn run(&self, context: Context<E, S>) -> Result<bool, E> {
28        (self)(context).await
29    }
30}
31
32pub struct HasChannelPermissions(Vec<ChannelPermission>);
33
34impl HasChannelPermissions {
35    pub fn new(permissions: Vec<ChannelPermission>) -> Self {
36        Self(permissions)
37    }
38}
39
40#[async_trait]
41impl<
42    E: From<Error> + Clone + Debug + Send + Sync + 'static,
43    S: Debug + Clone + Send + Sync + 'static,
44> Check<E, S> for HasChannelPermissions
45{
46    async fn run(&self, context: Context<E, S>) -> Result<bool, E> {
47        let permissions = context.get_author_channel_permissions().await;
48
49        for perm in &self.0 {
50            if !permissions.has(*perm as u64) {
51                return Err(Error::MissingChannelPermission { permissions: *perm }.into());
52            };
53        }
54
55        Ok(true)
56    }
57}
58
59pub struct HasServerPermissions(Vec<ChannelPermission>);
60
61impl HasServerPermissions {
62    pub fn new(permissions: Vec<ChannelPermission>) -> Self {
63        Self(permissions)
64    }
65}
66
67#[async_trait]
68impl<
69    E: From<Error> + Clone + Debug + Send + Sync + 'static,
70    S: Debug + Clone + Send + Sync + 'static,
71> Check<E, S> for HasServerPermissions
72{
73    async fn run(&self, context: Context<E, S>) -> Result<bool, E> {
74        let permissions = context.get_author_server_permissions().await;
75
76        for perm in &self.0 {
77            if !permissions.has(*perm as u64) {
78                return Err(Error::MissingChannelPermission { permissions: *perm }.into());
79            };
80        }
81
82        Ok(true)
83    }
84}
85
86pub struct CheckAny<E, S>(pub Arc<Vec<Box<dyn Check<E, S>>>>);
87
88#[async_trait]
89impl<
90    E: From<Error> + Clone + Debug + Send + Sync + 'static,
91    S: Debug + Clone + Send + Sync + 'static,
92> Check<E, S> for CheckAny<E, S>
93{
94    async fn run(&self, context: Context<E, S>) -> Result<bool, E> {
95        for check in self.0.iter() {
96            if check.run(context.clone()).await.unwrap_or_default() == true {
97                return Ok(true);
98            }
99        }
100
101        Err(Error::CheckFailure.into())
102    }
103}
104
105impl<E, S> CheckAny<E, S> {
106    pub fn new(checks: Vec<Box<dyn Check<E, S>>>) -> Self {
107        Self(Arc::new(checks))
108    }
109}
110
111pub async fn server_only<
112    E: From<Error> + Clone + Debug + Send + Sync + 'static,
113    S: Debug + Clone + Send + Sync + 'static,
114>(
115    context: Context<E, S>,
116) -> Result<bool, E> {
117    match context.get_current_channel() {
118        Ok(Channel::TextChannel { .. }) => Ok(true),
119        _ => Err(Error::NotInServer.into()),
120    }
121}
122
123pub async fn dm_only<
124    E: From<Error> + Clone + Debug + Send + Sync + 'static,
125    S: Debug + Clone + Send + Sync + 'static,
126>(
127    context: Context<E, S>,
128) -> Result<bool, E> {
129    match context.get_current_channel() {
130        Ok(
131            Channel::DirectMessage { .. } | Channel::Group { .. } | Channel::SavedMessages { .. },
132        ) => Ok(true),
133        _ => Err(Error::NotInDM.into()),
134    }
135}
136
137pub async fn is_owner<
138    E: From<Error> + Clone + Debug + Send + Sync + 'static,
139    S: Debug + Clone + Send + Sync + 'static,
140>(
141    context: Context<E, S>,
142) -> Result<bool, E> {
143    if let Some(user) = context.cache.get_current_user() {
144        if let Some(bot) = user.bot {
145            if &bot.owner_id == &context.message.author {
146                return Ok(true);
147            };
148        };
149    };
150
151    Err(Error::NotOwner.into())
152}
153
154pub async fn is_nsfw<
155    E: From<Error> + Clone + Debug + Send + Sync + 'static,
156    S: Debug + Clone + Send + Sync + 'static,
157>(
158    context: Context<E, S>,
159) -> Result<bool, E> {
160    let channel = context.get_current_channel()?;
161
162    let is_nsfw = match channel {
163        Channel::DirectMessage { .. } | Channel::SavedMessages { .. } => true,
164        Channel::Group { nsfw, .. } | Channel::TextChannel { nsfw, .. } => nsfw,
165    };
166
167    if is_nsfw {
168        Ok(true)
169    } else {
170        Err(Error::NotNsfw.into())
171    }
172}