stoat/commands/
context.rs1use 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}