1use std::borrow::Cow;
2
3use async_trait::async_trait;
4use stoat_models::v0::{Channel, Member, Server, User};
5use stoat_permissions::{
6 ChannelType, DEFAULT_PERMISSION_DIRECT_MESSAGE, Override, RelationshipStatus,
7};
8
9use crate::{GlobalCache, HttpClient};
10
11pub use stoat_permissions::{
12 ChannelPermission, UserPermission, calculate_channel_permissions, calculate_server_permissions,
13 calculate_user_permissions,
14};
15
16pub struct PermissionQuery<'a> {
17 cache: GlobalCache,
18 http: HttpClient,
19
20 perspective: Cow<'a, User>,
21 user: Option<Cow<'a, User>>,
22 channel: Option<Cow<'a, Channel>>,
23 server: Option<Cow<'a, Server>>,
24 member: Option<Cow<'a, Member>>,
25}
26
27impl<'a> PermissionQuery<'a> {
28 pub fn new(cache: GlobalCache, http: HttpClient, perspective: Cow<'a, User>) -> Self {
29 Self {
30 cache,
31 http,
32 perspective,
33 user: None,
34 channel: None,
35 server: None,
36 member: None,
37 }
38 }
39
40 pub fn user(mut self, user: Cow<'a, User>) -> Self {
42 self.user = Some(user);
43
44 self
45 }
46
47 pub fn channel(mut self, channel: Cow<'a, Channel>) -> Self {
49 self.channel = Some(channel);
50
51 self
52 }
53
54 pub fn server(mut self, server: Cow<'a, Server>) -> Self {
56 self.server = Some(server);
57
58 self
59 }
60
61 pub fn member(mut self, member: Cow<'a, Member>) -> Self {
63 self.member = Some(member);
64
65 self
66 }
67}
68
69#[async_trait]
70impl stoat_permissions::PermissionQuery for PermissionQuery<'_> {
71 async fn are_we_privileged(&mut self) -> bool {
72 self.perspective.privileged
73 }
74
75 async fn are_we_a_bot(&mut self) -> bool {
77 self.perspective.bot.is_some()
78 }
79
80 async fn are_the_users_same(&mut self) -> bool {
82 if let Some(other_user) = &self.user {
83 self.perspective.id == other_user.id
84 } else {
85 false
86 }
87 }
88
89 async fn user_relationship(&mut self) -> RelationshipStatus {
91 if let Some(other_user) = &self.user {
92 if self.perspective.id == other_user.id {
93 return RelationshipStatus::User;
94 } else if let Some(bot) = &other_user.bot {
95 if self.perspective.id == bot.owner_id {
96 return RelationshipStatus::User;
97 }
98 }
99
100 for entry in &self.perspective.relations {
101 if entry.user_id == other_user.id {
102 return match entry.status {
103 stoat_models::v0::RelationshipStatus::None => RelationshipStatus::None,
104 stoat_models::v0::RelationshipStatus::User => RelationshipStatus::User,
105 stoat_models::v0::RelationshipStatus::Friend => RelationshipStatus::Friend,
106 stoat_models::v0::RelationshipStatus::Outgoing => {
107 RelationshipStatus::Outgoing
108 }
109 stoat_models::v0::RelationshipStatus::Incoming => {
110 RelationshipStatus::Incoming
111 }
112 stoat_models::v0::RelationshipStatus::Blocked => {
113 RelationshipStatus::Blocked
114 }
115 stoat_models::v0::RelationshipStatus::BlockedOther => {
116 RelationshipStatus::BlockedOther
117 }
118 };
119 }
120 }
121 }
122
123 RelationshipStatus::None
124 }
125
126 async fn user_is_bot(&mut self) -> bool {
128 if let Some(other_user) = &self.user {
129 other_user.bot.is_some()
130 } else {
131 false
132 }
133 }
134
135 async fn have_mutual_connection(&mut self) -> bool {
136 true
137 }
138
139 async fn are_we_server_owner(&mut self) -> bool {
143 if let Some(server) = &self.server {
144 server.owner == self.perspective.id
145 } else {
146 false
147 }
148 }
149
150 async fn are_we_a_member(&mut self) -> bool {
152 if let Some(server) = &self.server {
153 if self.member.is_some() {
154 true
155 } else if let Some(member) = self.cache.get_member(&server.id, &self.perspective.id) {
156 self.member = Some(Cow::Owned(member.clone()));
157
158 true
159 } else if let Ok(member) = self
160 .http
161 .fetch_member(&server.id, &self.perspective.id)
162 .await
163 {
164 self.member = Some(Cow::Owned(member));
165 true
166 } else {
167 false
168 }
169 } else {
170 false
171 }
172 }
173
174 async fn get_default_server_permissions(&mut self) -> u64 {
176 if let Some(server) = &self.server {
177 server.default_permissions as u64
178 } else {
179 0
180 }
181 }
182
183 async fn get_our_server_role_overrides(&mut self) -> Vec<Override> {
185 if let Some(server) = &self.server {
186 let member_roles = self
187 .member
188 .as_ref()
189 .map(|member| member.roles.clone())
190 .unwrap_or_default();
191
192 let mut roles = server
193 .roles
194 .iter()
195 .filter(|(id, _)| member_roles.contains(id))
196 .map(|(_, role)| {
197 let v: Override = role.permissions.into();
198 (role.rank, v)
199 })
200 .collect::<Vec<(i64, Override)>>();
201
202 roles.sort_by(|a, b| b.0.cmp(&a.0));
203 roles.into_iter().map(|(_, v)| v).collect()
204 } else {
205 vec![]
206 }
207 }
208
209 async fn are_we_timed_out(&mut self) -> bool {
211 if let Some(member) = &self.member {
212 member.timeout.is_some()
213 } else {
214 false
215 }
216 }
217
218 async fn do_we_have_publish_overwrites(&mut self) -> bool {
220 self.member.as_ref().is_none_or(|member| member.can_publish)
221 }
222
223 async fn do_we_have_receive_overwrites(&mut self) -> bool {
225 self.member.as_ref().is_none_or(|member| member.can_receive)
226 }
227
228 async fn get_channel_type(&mut self) -> ChannelType {
232 if let Some(channel) = &self.channel {
233 match channel {
234 Cow::Borrowed(Channel::DirectMessage { .. })
235 | Cow::Owned(Channel::DirectMessage { .. }) => ChannelType::DirectMessage,
236 Cow::Borrowed(Channel::Group { .. }) | Cow::Owned(Channel::Group { .. }) => {
237 ChannelType::Group
238 }
239 Cow::Borrowed(Channel::SavedMessages { .. })
240 | Cow::Owned(Channel::SavedMessages { .. }) => ChannelType::SavedMessages,
241 Cow::Borrowed(Channel::TextChannel { .. })
242 | Cow::Owned(Channel::TextChannel { .. }) => ChannelType::ServerChannel,
243 }
244 } else {
245 ChannelType::Unknown
246 }
247 }
248
249 async fn get_default_channel_permissions(&mut self) -> Override {
252 if let Some(channel) = &self.channel {
253 match channel {
254 Cow::Borrowed(Channel::Group { permissions, .. })
255 | Cow::Owned(Channel::Group { permissions, .. }) => Override {
256 allow: permissions.unwrap_or(*DEFAULT_PERMISSION_DIRECT_MESSAGE as i64) as u64,
257 deny: 0,
258 },
259 Cow::Borrowed(Channel::TextChannel {
260 default_permissions,
261 ..
262 })
263 | Cow::Owned(Channel::TextChannel {
264 default_permissions,
265 ..
266 }) => default_permissions.unwrap_or_default().into(),
267 _ => Default::default(),
268 }
269 } else {
270 Default::default()
271 }
272 }
273
274 async fn get_our_channel_role_overrides(&mut self) -> Vec<Override> {
276 if let Some(channel) = &self.channel {
277 match channel {
278 Cow::Borrowed(Channel::TextChannel {
279 role_permissions, ..
280 })
281 | Cow::Owned(Channel::TextChannel {
282 role_permissions, ..
283 }) => {
284 if let Some(server) = &self.server {
285 let member_roles = self
286 .member
287 .as_ref()
288 .map(|member| member.roles.clone())
289 .unwrap_or_default();
290
291 let mut roles = role_permissions
292 .iter()
293 .filter(|(id, _)| member_roles.contains(id))
294 .filter_map(|(id, permission)| {
295 server.roles.get(id).map(|role| {
296 let v: Override = (*permission).into();
297 (role.rank, v)
298 })
299 })
300 .collect::<Vec<(i64, Override)>>();
301
302 roles.sort_by(|a, b| b.0.cmp(&a.0));
303 roles.into_iter().map(|(_, v)| v).collect()
304 } else {
305 vec![]
306 }
307 }
308 _ => vec![],
309 }
310 } else {
311 vec![]
312 }
313 }
314
315 async fn do_we_own_the_channel(&mut self) -> bool {
317 if let Some(channel) = &self.channel {
318 match channel {
319 Cow::Borrowed(Channel::Group { owner, .. })
320 | Cow::Owned(Channel::Group { owner, .. }) => owner == &self.perspective.id,
321 Cow::Borrowed(Channel::SavedMessages { user, .. })
322 | Cow::Owned(Channel::SavedMessages { user, .. }) => user == &self.perspective.id,
323 _ => false,
324 }
325 } else {
326 false
327 }
328 }
329
330 async fn are_we_part_of_the_channel(&mut self) -> bool {
332 if let Some(
333 Cow::Borrowed(Channel::DirectMessage { recipients, .. })
334 | Cow::Owned(Channel::DirectMessage { recipients, .. })
335 | Cow::Borrowed(Channel::Group { recipients, .. })
336 | Cow::Owned(Channel::Group { recipients, .. }),
337 ) = &self.channel
338 {
339 recipients.contains(&self.perspective.id)
340 } else {
341 false
342 }
343 }
344
345 async fn set_recipient_as_user(&mut self) {
348 if let Some(channel) = &self.channel {
349 match channel {
350 Cow::Borrowed(Channel::DirectMessage { recipients, .. })
351 | Cow::Owned(Channel::DirectMessage { recipients, .. }) => {
352 let recipient_id = recipients
353 .iter()
354 .find(|recipient| recipient != &&self.perspective.id)
355 .expect("Missing recipient for DM");
356
357 if let Some(user) = self.cache.get_user(recipient_id) {
358 self.user.replace(Cow::Owned(user.clone()));
359 } else if let Ok(user) = self.http.fetch_user(recipient_id).await {
360 self.user.replace(Cow::Owned(user));
361 }
362 }
363 _ => unimplemented!(),
364 }
365 }
366 }
367
368 async fn set_server_from_channel(&mut self) {
371 if let Some(channel) = &self.channel {
372 match channel {
373 Cow::Borrowed(Channel::TextChannel { server, .. })
374 | Cow::Owned(Channel::TextChannel { server, .. }) => {
375 if let Some(known_server) = self.server.as_ref().map(|server| server.as_ref()) {
376 if server == &known_server.id {
377 return;
379 }
380 }
381
382 if let Some(server) = self.cache.get_server(server) {
383 self.server.replace(Cow::Owned(server.clone()));
384 }
385 }
386 _ => unimplemented!(),
387 }
388 }
389 }
390}
391
392pub fn user_permissions_query<'a>(
393 cache: GlobalCache,
394 http: HttpClient,
395 user: Cow<'a, User>,
396) -> PermissionQuery<'a> {
397 let ourself = cache.get_current_user().unwrap();
398
399 PermissionQuery::new(cache, http, Cow::Owned(ourself)).user(user)
400}