Skip to main content

stoat/commands/
context.rs

1use std::{borrow::Cow, fmt::Debug, ops::Deref, sync::Arc};
2
3use state::TypeMap;
4use stoat_models::v0::{Channel, Member, Message, Server, User};
5use stoat_permissions::{
6    PermissionValue, calculate_channel_permissions, calculate_server_permissions,
7};
8
9use crate::{
10    Context as MessageContext, Error, GlobalCache, HttpClient, UserExt,
11    commands::{Command, HelpCommand, Words, handler::Commands},
12    context::Events,
13    notifiers::Notifiers,
14    permissions::user_permissions_query,
15};
16
17type SendSyncMap = TypeMap![Send + Sync];
18
19#[derive(Debug, Clone)]
20pub struct Context<
21    E: From<Error> + Clone + Debug + Send + Sync + 'static,
22    S: Debug + Clone + Send + Sync + 'static,
23> {
24    pub(crate) inner: MessageContext,
25    pub prefix: Option<String>,
26    pub command: Option<Command<E, S>>,
27    pub message: Message,
28    pub state: S,
29    pub words: Words,
30    pub commands: Commands<E, S>,
31    pub help_command: Arc<dyn HelpCommand<E, S>>,
32    pub(crate) local_state: Arc<SendSyncMap>,
33}
34
35impl<
36    E: From<Error> + Clone + Debug + Send + Sync + 'static,
37    S: Debug + Clone + Send + Sync + 'static,
38> Context<E, S>
39{
40    pub fn local_cache<F: FnOnce() -> T, T: Send + Sync + 'static>(&self, f: F) -> &T {
41        self.local_state.try_get().unwrap_or_else(|| {
42            self.local_state.set(f());
43            self.local_state.get()
44        })
45    }
46
47    pub async fn local_cache_async<Fut: Future<Output = T>, T: Send + Sync + 'static>(
48        &self,
49        fut: Fut,
50    ) -> &T {
51        match self.local_state.try_get() {
52            Some(s) => s,
53            None => {
54                self.local_state.set(fut.await);
55                self.local_state.get()
56            }
57        }
58    }
59
60    pub fn get_current_channel(&self) -> Result<Channel, Error> {
61        self.local_cache(|| {
62            struct CurrentChannel(Result<Channel, Error>);
63
64            CurrentChannel(
65                self.cache
66                    .get_channel(&self.message.channel)
67                    .ok_or(Error::InternalError),
68            )
69        })
70        .0
71        .clone()
72    }
73
74    pub fn get_current_server(&self) -> Result<Server, Error> {
75        self.local_cache(|| {
76            struct CurrentServer(Result<Server, Error>);
77
78            CurrentServer(
79                if let Ok(Channel::TextChannel { server, .. }) = self.get_current_channel() {
80                    self.cache.get_server(&server).ok_or(Error::InternalError)
81                } else {
82                    Err(Error::NotInServer)
83                },
84            )
85        })
86        .0
87        .clone()
88    }
89
90    pub async fn get_user(&self) -> Result<User, Error> {
91        self.local_cache_async({
92            struct CurrentUser(Result<User, Error>);
93
94            async move {
95                CurrentUser(if let Some(user) = self.message.user.as_ref() {
96                    Ok(user.clone())
97                } else if let Some(user) = self.cache.get_user(&self.message.author) {
98                    Ok(user.clone())
99                } else {
100                    self.http.fetch_user(&self.message.author).await
101                })
102            }
103        })
104        .await
105        .0
106        .clone()
107    }
108
109    pub async fn get_member(&self) -> Result<Member, Error> {
110        self.local_cache_async({
111            struct CurrentMember(Result<Member, Error>);
112
113            async move {
114                CurrentMember(if let Some(member) = self.message.member.as_ref() {
115                    Ok(member.clone())
116                } else {
117                    match self.get_current_server() {
118                        Ok(server) => {
119                            if let Some(member) =
120                                self.cache.get_member(&server.id, &self.message.author)
121                            {
122                                Ok(member.clone())
123                            } else {
124                                self.http
125                                    .fetch_member(&server.id, &self.message.author)
126                                    .await
127                            }
128                        }
129                        Err(e) => Err(e),
130                    }
131                })
132            }
133        })
134        .await
135        .0
136        .clone()
137    }
138
139    pub async fn get_author_channel_permissions(&self) -> PermissionValue {
140        self.local_cache_async(async {
141            struct ChannelPermissions(PermissionValue);
142
143            let Ok(user) = self.get_user().await else {
144                return ChannelPermissions(0u64.into());
145            };
146            let member = self.get_member().await;
147            let Ok(channel) = self.get_current_channel() else {
148                return ChannelPermissions(0u64.into());
149            };
150            let server = self.get_current_server();
151
152            let mut query =
153                user_permissions_query(self.cache.clone(), self.http.clone(), Cow::Owned(user))
154                    .channel(Cow::Owned(channel));
155
156            if let Ok(server) = server {
157                query = query.server(Cow::Owned(server))
158            };
159
160            if let Ok(member) = member {
161                query = query.member(Cow::Owned(member))
162            };
163
164            ChannelPermissions(calculate_channel_permissions(&mut query).await)
165        })
166        .await
167        .0
168    }
169
170    pub async fn get_author_server_permissions(&self) -> PermissionValue {
171        self.local_cache_async(async {
172            struct ServerPermissions(PermissionValue);
173
174            let Ok(user) = self.get_user().await else {
175                return ServerPermissions(0u64.into());
176            };
177            let member = self.get_member().await;
178            let server = self.get_current_server();
179
180            let mut query =
181                user_permissions_query(self.cache.clone(), self.http.clone(), Cow::Owned(user));
182
183            if let Ok(server) = server {
184                query = query.server(Cow::Owned(server))
185            };
186
187            if let Ok(member) = member {
188                query = query.member(Cow::Owned(member))
189            };
190
191            ServerPermissions(calculate_server_permissions(&mut query).await)
192        })
193        .await
194        .0
195    }
196
197    pub fn clean_prefix(&self) -> String {
198        let Some(ref prefix) = self.prefix else {
199            return String::new();
200        };
201
202        let user = self.cache.get_current_user().unwrap();
203
204        prefix.replace(
205            &format!("<@{}>", &user.id),
206            &user.name().replace("\\", "\\\\"),
207        )
208    }
209}
210
211impl<
212    E: From<Error> + Clone + Debug + Send + Sync + 'static,
213    S: Debug + Clone + Send + Sync + 'static,
214> Deref for Context<E, S>
215{
216    type Target = MessageContext;
217
218    fn deref(&self) -> &Self::Target {
219        &self.inner
220    }
221}
222
223impl<
224    E: From<Error> + Clone + Debug + Send + Sync + 'static,
225    S: Debug + Clone + Send + Sync + 'static,
226> AsRef<GlobalCache> for Context<E, S>
227{
228    fn as_ref(&self) -> &GlobalCache {
229        &self.cache
230    }
231}
232
233impl<
234    E: From<Error> + Clone + Debug + Send + Sync + 'static,
235    S: Debug + Clone + Send + Sync + 'static,
236> AsRef<HttpClient> for Context<E, S>
237{
238    fn as_ref(&self) -> &HttpClient {
239        &self.http
240    }
241}
242
243impl<
244    E: From<Error> + Clone + Debug + Send + Sync + 'static,
245    S: Debug + Clone + Send + Sync + 'static,
246> AsRef<Notifiers> for Context<E, S>
247{
248    fn as_ref(&self) -> &Notifiers {
249        &self.notifiers
250    }
251}
252
253impl<
254    E: From<Error> + Clone + Debug + Send + Sync + 'static,
255    S: Debug + Clone + Send + Sync + 'static,
256> AsRef<Events> for Context<E, S>
257{
258    fn as_ref(&self) -> &Events {
259        &self.events
260    }
261}