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