Skip to main content

revolt_database/models/channels/
model.rs

1#![allow(deprecated)]
2use std::{borrow::Cow, collections::HashMap};
3
4use redis_kiss::get_connection;
5use revolt_config::config;
6use revolt_models::v0::{self, MessageAuthor};
7use revolt_permissions::OverrideField;
8use revolt_result::Result;
9use serde::{Deserialize, Serialize};
10use ulid::Ulid;
11
12use crate::{
13    events::client::EventV1, Database, File, PartialServer, Server, SystemMessage, User, AMQP,
14};
15
16#[cfg(feature = "mongodb")]
17use crate::IntoDocumentPath;
18
19auto_derived!(
20    #[serde(tag = "channel_type")]
21    pub enum Channel {
22        /// Personal "Saved Notes" channel which allows users to save messages
23        SavedMessages {
24            /// Unique Id
25            #[serde(rename = "_id")]
26            id: String,
27            /// Id of the user this channel belongs to
28            user: String,
29        },
30        /// Direct message channel between two users
31        DirectMessage {
32            /// Unique Id
33            #[serde(rename = "_id")]
34            id: String,
35
36            /// Whether this direct message channel is currently open on both sides
37            active: bool,
38            /// 2-tuple of user ids participating in direct message
39            recipients: Vec<String>,
40            /// Id of the last message sent in this channel
41            #[serde(skip_serializing_if = "Option::is_none")]
42            last_message_id: Option<String>,
43        },
44        /// Group channel between 1 or more participants
45        Group {
46            /// Unique Id
47            #[serde(rename = "_id")]
48            id: String,
49
50            /// Display name of the channel
51            name: String,
52            /// User id of the owner of the group
53            owner: String,
54            /// Channel description
55            #[serde(skip_serializing_if = "Option::is_none")]
56            description: Option<String>,
57            /// Array of user ids participating in channel
58            recipients: Vec<String>,
59
60            /// Custom icon attachment
61            #[serde(skip_serializing_if = "Option::is_none")]
62            icon: Option<File>,
63            /// Id of the last message sent in this channel
64            #[serde(skip_serializing_if = "Option::is_none")]
65            last_message_id: Option<String>,
66
67            /// Permissions assigned to members of this group
68            /// (does not apply to the owner of the group)
69            #[serde(skip_serializing_if = "Option::is_none")]
70            permissions: Option<i64>,
71
72            /// Whether this group is marked as not safe for work
73            #[serde(skip_serializing_if = "crate::if_false", default)]
74            nsfw: bool,
75        },
76        /// Text channel belonging to a server
77        TextChannel {
78            /// Unique Id
79            #[serde(rename = "_id")]
80            id: String,
81            /// Id of the server this channel belongs to
82            server: String,
83
84            /// Display name of the channel
85            name: String,
86            /// Channel description
87            #[serde(skip_serializing_if = "Option::is_none")]
88            description: Option<String>,
89
90            /// Custom icon attachment
91            #[serde(skip_serializing_if = "Option::is_none")]
92            icon: Option<File>,
93            /// Id of the last message sent in this channel
94            #[serde(skip_serializing_if = "Option::is_none")]
95            last_message_id: Option<String>,
96
97            /// Default permissions assigned to users in this channel
98            #[serde(skip_serializing_if = "Option::is_none")]
99            default_permissions: Option<OverrideField>,
100            /// Permissions assigned based on role to this channel
101            #[serde(
102                default = "HashMap::<String, OverrideField>::new",
103                skip_serializing_if = "HashMap::<String, OverrideField>::is_empty"
104            )]
105            role_permissions: HashMap<String, OverrideField>,
106
107            /// Whether this channel is marked as not safe for work
108            #[serde(skip_serializing_if = "crate::if_false", default)]
109            nsfw: bool,
110
111            /// Voice Information for when this channel is also a voice channel
112            #[serde(skip_serializing_if = "Option::is_none")]
113            voice: Option<VoiceInformation>,
114
115            /// The channel's slowmode delay in seconds
116            #[serde(skip_serializing_if = "Option::is_none")]
117            slowmode: Option<u64>,
118        },
119    }
120
121    #[derive(Default)]
122    pub struct VoiceInformation {
123        /// Maximium amount of users allowed in the voice channel at once
124        #[serde(skip_serializing_if = "Option::is_none")]
125        pub max_users: Option<usize>,
126    }
127);
128
129auto_derived!(
130    #[derive(Default)]
131    pub struct PartialChannel {
132        #[serde(skip_serializing_if = "Option::is_none")]
133        pub name: Option<String>,
134        #[serde(skip_serializing_if = "Option::is_none")]
135        pub owner: Option<String>,
136        #[serde(skip_serializing_if = "Option::is_none")]
137        pub description: Option<String>,
138        #[serde(skip_serializing_if = "Option::is_none")]
139        pub icon: Option<File>,
140        #[serde(skip_serializing_if = "Option::is_none")]
141        pub nsfw: Option<bool>,
142        #[serde(skip_serializing_if = "Option::is_none")]
143        pub active: Option<bool>,
144        #[serde(skip_serializing_if = "Option::is_none")]
145        pub permissions: Option<i64>,
146        #[serde(skip_serializing_if = "Option::is_none")]
147        pub role_permissions: Option<HashMap<String, OverrideField>>,
148        #[serde(skip_serializing_if = "Option::is_none")]
149        pub default_permissions: Option<OverrideField>,
150        #[serde(skip_serializing_if = "Option::is_none")]
151        pub last_message_id: Option<String>,
152        #[serde(skip_serializing_if = "Option::is_none")]
153        pub voice: Option<VoiceInformation>,
154        #[serde(skip_serializing_if = "Option::is_none")]
155        pub slowmode: Option<u64>,
156    }
157
158    /// Optional fields on channel object
159    pub enum FieldsChannel {
160        Description,
161        Icon,
162        DefaultPermissions,
163        Voice,
164        Slowmode,
165    }
166);
167
168#[allow(clippy::disallowed_methods)]
169impl Channel {
170    /* /// Create a channel
171    pub async fn create(&self, db: &Database) -> Result<()> {
172        db.insert_channel(self).await?;
173
174        let event = EventV1::ChannelCreate(self.clone().into());
175        match self {
176            Self::SavedMessages { user, .. } => event.private(user.clone()).await,
177            Self::DirectMessage { recipients, .. } | Self::Group { recipients, .. } => {
178                for recipient in recipients {
179                    event.clone().private(recipient.clone()).await;
180                }
181            }
182            Self::TextChannel { server, .. } | Self::VoiceChannel { server, .. } => {
183                event.p(server.clone()).await;
184            }
185        }
186
187        Ok(())
188    }*/
189
190    /// Create a new server channel
191    pub async fn create_server_channel(
192        db: &Database,
193        server: &mut Server,
194        data: v0::DataCreateServerChannel,
195        update_server: bool,
196    ) -> Result<Channel> {
197        let config = config().await;
198        if server.channels.len() > config.features.limits.global.server_channels {
199            return Err(create_error!(TooManyChannels {
200                max: config.features.limits.global.server_channels,
201            }));
202        };
203
204        let id = ulid::Ulid::new().to_string();
205        let channel = match data.channel_type {
206            v0::LegacyServerChannelType::Text => Channel::TextChannel {
207                id: id.clone(),
208                server: server.id.to_owned(),
209                name: data.name,
210                description: data.description,
211                icon: None,
212                last_message_id: None,
213                default_permissions: None,
214                role_permissions: HashMap::new(),
215                nsfw: data.nsfw.unwrap_or(false),
216                voice: data.voice.map(|voice| voice.into()),
217                slowmode: None,
218            },
219            v0::LegacyServerChannelType::Voice => Channel::TextChannel {
220                id: id.clone(),
221                server: server.id.to_owned(),
222                name: data.name,
223                description: data.description,
224                icon: None,
225                last_message_id: None,
226                default_permissions: None,
227                role_permissions: HashMap::new(),
228                nsfw: data.nsfw.unwrap_or(false),
229                voice: Some(data.voice.unwrap_or_default().into()),
230                slowmode: None,
231            },
232        };
233
234        db.insert_channel(&channel).await?;
235
236        if update_server {
237            server
238                .update(
239                    db,
240                    PartialServer {
241                        channels: Some([server.channels.clone(), [id].into()].concat()),
242                        ..Default::default()
243                    },
244                    vec![],
245                )
246                .await?;
247
248            EventV1::ChannelCreate(channel.clone().into())
249                .p(server.id.clone())
250                .await;
251        }
252
253        Ok(channel)
254    }
255
256    /// Create a group
257    pub async fn create_group(
258        db: &Database,
259        mut data: v0::DataCreateGroup,
260        owner_id: String,
261    ) -> Result<Channel> {
262        data.users.insert(owner_id.to_string());
263
264        let config = config().await;
265        if data.users.len() > config.features.limits.global.group_size {
266            return Err(create_error!(GroupTooLarge {
267                max: config.features.limits.global.group_size,
268            }));
269        }
270
271        let id = ulid::Ulid::new().to_string();
272
273        let icon = if let Some(icon_id) = data.icon {
274            Some(File::use_channel_icon(db, &icon_id, &id, &owner_id).await?)
275        } else {
276            None
277        };
278
279        let recipients = data.users.into_iter().collect::<Vec<String>>();
280        let channel = Channel::Group {
281            id,
282
283            name: data.name,
284            owner: owner_id,
285            description: data.description,
286            recipients: recipients.clone(),
287
288            icon,
289            last_message_id: None,
290
291            permissions: None,
292
293            nsfw: data.nsfw.unwrap_or(false),
294        };
295
296        db.insert_channel(&channel).await?;
297
298        let event = EventV1::ChannelCreate(channel.clone().into());
299        for recipient in recipients {
300            event.clone().private(recipient).await;
301        }
302
303        Ok(channel)
304    }
305
306    /// Create a DM (or return the existing one / saved messages)
307    pub async fn create_dm(db: &Database, user_a: &User, user_b: &User) -> Result<Channel> {
308        // Try to find existing channel
309        if let Ok(channel) = db.find_direct_message_channel(&user_a.id, &user_b.id).await {
310            Ok(channel)
311        } else {
312            let channel = if user_a.id == user_b.id {
313                // Create a new saved messages channel
314                Channel::SavedMessages {
315                    id: Ulid::new().to_string(),
316                    user: user_a.id.to_string(),
317                }
318            } else {
319                // Create a new DM channel
320                Channel::DirectMessage {
321                    id: Ulid::new().to_string(),
322                    active: true, // show by default
323                    recipients: vec![user_a.id.clone(), user_b.id.clone()],
324                    last_message_id: None,
325                }
326            };
327
328            db.insert_channel(&channel).await?;
329
330            if let Channel::DirectMessage { .. } = &channel {
331                let event = EventV1::ChannelCreate(channel.clone().into());
332                event.clone().private(user_a.id.clone()).await;
333                event.private(user_b.id.clone()).await;
334            };
335
336            Ok(channel)
337        }
338    }
339
340    /// Add user to a group
341    pub async fn add_user_to_group(
342        &mut self,
343        db: &Database,
344        amqp: &AMQP,
345        user: &User,
346        by_id: &str,
347    ) -> Result<()> {
348        if let Channel::Group { recipients, .. } = self {
349            if recipients.contains(&String::from(&user.id)) {
350                return Err(create_error!(AlreadyInGroup));
351            }
352
353            let config = config().await;
354            if recipients.len() >= config.features.limits.global.group_size {
355                return Err(create_error!(GroupTooLarge {
356                    max: config.features.limits.global.group_size
357                }));
358            }
359
360            recipients.push(String::from(&user.id));
361        }
362
363        match &self {
364            Channel::Group { id, .. } => {
365                db.add_user_to_group(id, &user.id).await?;
366
367                EventV1::ChannelGroupJoin {
368                    id: id.to_string(),
369                    user: user.id.to_string(),
370                }
371                .p(id.to_string())
372                .await;
373
374                SystemMessage::UserAdded {
375                    id: user.id.to_string(),
376                    by: by_id.to_string(),
377                }
378                .into_message(id.to_string())
379                .send(
380                    db,
381                    Some(amqp),
382                    MessageAuthor::System {
383                        username: &user.username,
384                        avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
385                    },
386                    None,
387                    None,
388                    self,
389                    false,
390                )
391                .await
392                .ok();
393
394                EventV1::ChannelCreate(self.clone().into())
395                    .private(user.id.to_string())
396                    .await;
397
398                Ok(())
399            }
400            _ => Err(create_error!(InvalidOperation)),
401        }
402    }
403
404    /// Map out whether it is a direct DM
405    pub fn is_direct_dm(&self) -> bool {
406        matches!(self, Channel::DirectMessage { .. })
407    }
408
409    /// Check whether has a user as a recipient
410    pub fn contains_user(&self, user_id: &str) -> bool {
411        match self {
412            Channel::Group { recipients, .. } | Channel::DirectMessage { recipients, .. } => {
413                recipients.iter().any(|recipient| recipient == user_id)
414            }
415            Channel::SavedMessages { user, .. } => user == user_id,
416            _ => false,
417        }
418    }
419
420    /// Get list of recipients
421    pub fn users(&self) -> Result<Vec<String>> {
422        match self {
423            Channel::Group { recipients, .. } | Channel::DirectMessage { recipients, .. } => {
424                Ok(recipients.to_owned())
425            }
426            _ => Err(create_error!(NotFound)),
427        }
428    }
429
430    /// Clone this channel's id
431    pub fn id(&self) -> &str {
432        match self {
433            Channel::DirectMessage { id, .. }
434            | Channel::Group { id, .. }
435            | Channel::SavedMessages { id, .. }
436            | Channel::TextChannel { id, .. } => id,
437        }
438    }
439
440    /// Clone this channel's server id
441    pub fn server(&self) -> Option<&str> {
442        match self {
443            Channel::TextChannel { server, .. } => Some(server),
444            _ => None,
445        }
446    }
447
448    /// Gets this channel's voice information
449    pub fn voice(&self) -> Option<Cow<VoiceInformation>> {
450        match self {
451            Self::DirectMessage { .. } | Self::Group { .. } => {
452                Some(Cow::Owned(VoiceInformation::default()))
453            }
454            Self::TextChannel {
455                voice: Some(voice), ..
456            } => Some(Cow::Borrowed(voice)),
457            _ => None,
458        }
459    }
460
461    /// Set role permission on a channel
462    pub async fn set_role_permission(
463        &mut self,
464        db: &Database,
465        role_id: &str,
466        permissions: OverrideField,
467    ) -> Result<()> {
468        match self {
469            Channel::TextChannel {
470                id,
471                server,
472                role_permissions,
473                ..
474            } => {
475                db.set_channel_role_permission(id, role_id, permissions)
476                    .await?;
477
478                role_permissions.insert(role_id.to_string(), permissions);
479
480                EventV1::ChannelUpdate {
481                    id: id.clone(),
482                    data: PartialChannel {
483                        role_permissions: Some(role_permissions.clone()),
484                        ..Default::default()
485                    }
486                    .into(),
487                    clear: vec![],
488                }
489                .p(server.clone())
490                .await;
491
492                Ok(())
493            }
494            _ => Err(create_error!(InvalidOperation)),
495        }
496    }
497
498    /// Update channel data
499    pub async fn update(
500        &mut self,
501        db: &Database,
502        partial: PartialChannel,
503        remove: Vec<FieldsChannel>,
504    ) -> Result<()> {
505        for field in &remove {
506            self.remove_field(field);
507        }
508
509        self.apply_options(partial.clone());
510
511        let id = self.id().to_string();
512        db.update_channel(&id, &partial, remove.clone()).await?;
513
514        EventV1::ChannelUpdate {
515            id: id.clone(),
516            data: partial.into(),
517            clear: remove.into_iter().map(|v| v.into()).collect(),
518        }
519        .p(match self {
520            Self::TextChannel { server, .. } => server.clone(),
521            _ => id,
522        })
523        .await;
524
525        Ok(())
526    }
527
528    /// Remove a field from Channel object
529    pub fn remove_field(&mut self, field: &FieldsChannel) {
530        match field {
531            FieldsChannel::Description => match self {
532                Self::Group { description, .. } | Self::TextChannel { description, .. } => {
533                    description.take();
534                }
535                _ => {}
536            },
537            FieldsChannel::Icon => match self {
538                Self::Group { icon, .. } | Self::TextChannel { icon, .. } => {
539                    icon.take();
540                }
541                _ => {}
542            },
543            FieldsChannel::DefaultPermissions => match self {
544                Self::TextChannel {
545                    default_permissions,
546                    ..
547                } => {
548                    default_permissions.take();
549                }
550                _ => {}
551            },
552            FieldsChannel::Voice => match self {
553                Self::TextChannel { voice, .. } => {
554                    voice.take();
555                }
556                _ => {}
557            },
558            FieldsChannel::Slowmode => match self {
559                Self::TextChannel { slowmode, .. } => {
560                    slowmode.take();
561                }
562                _ => {}
563            }
564        }
565    }
566
567    /// Remove multiple fields from Channel object
568    pub fn remove_fields(&mut self, partial: Vec<FieldsChannel>) {
569        for field in partial {
570            self.remove_field(&field)
571        }
572    }
573
574    /// Apply partial channel to channel
575    #[allow(deprecated)]
576    pub fn apply_options(&mut self, partial: PartialChannel) {
577        match self {
578            Self::SavedMessages { .. } => {}
579            Self::DirectMessage { active, .. } => {
580                if let Some(v) = partial.active {
581                    *active = v;
582                }
583            }
584            Self::Group {
585                name,
586                owner,
587                description,
588                icon,
589                nsfw,
590                permissions,
591                ..
592            } => {
593                if let Some(v) = partial.name {
594                    *name = v;
595                }
596
597                if let Some(v) = partial.owner {
598                    *owner = v;
599                }
600
601                if let Some(v) = partial.description {
602                    description.replace(v);
603                }
604
605                if let Some(v) = partial.icon {
606                    icon.replace(v);
607                }
608
609                if let Some(v) = partial.nsfw {
610                    *nsfw = v;
611                }
612
613                if let Some(v) = partial.permissions {
614                    permissions.replace(v);
615                }
616            }
617            Self::TextChannel {
618                name,
619                description,
620                icon,
621                nsfw,
622                default_permissions,
623                role_permissions,
624                voice,
625                ..
626            } => {
627                if let Some(v) = partial.name {
628                    *name = v;
629                }
630
631                if let Some(v) = partial.description {
632                    description.replace(v);
633                }
634
635                if let Some(v) = partial.icon {
636                    icon.replace(v);
637                }
638
639                if let Some(v) = partial.nsfw {
640                    *nsfw = v;
641                }
642
643                if let Some(v) = partial.role_permissions {
644                    *role_permissions = v;
645                }
646
647                if let Some(v) = partial.default_permissions {
648                    default_permissions.replace(v);
649                }
650
651                if let Some(v) = partial.voice {
652                    voice.replace(v);
653                }
654            }
655        }
656    }
657
658    /// Generates a PartialChannel containing the data which has changed in an update
659    pub fn generate_diff(
660        &self,
661        partial: &PartialChannel,
662        remove: &[FieldsChannel],
663    ) -> PartialChannel {
664        let mut before = PartialChannel::default();
665
666        match self {
667            Channel::SavedMessages { .. } => {}
668            Channel::DirectMessage {
669                active,
670                last_message_id,
671                ..
672            } => {
673                if partial.active.is_some() {
674                    before.active = Some(*active);
675                };
676
677                if partial.last_message_id.is_some() {
678                    before.last_message_id = last_message_id.clone()
679                };
680            }
681            Channel::Group {
682                name,
683                owner,
684                description,
685                icon,
686                last_message_id,
687                permissions,
688                nsfw,
689                ..
690            } => {
691                if partial.name.is_some() {
692                    before.name = Some(name.clone());
693                };
694
695                if partial.owner.is_some() {
696                    before.owner = Some(owner.clone());
697                };
698
699                if partial.description.is_some() || remove.contains(&FieldsChannel::Description) {
700                    before.description = description.clone();
701                };
702
703                if partial.icon.is_some() || remove.contains(&FieldsChannel::Icon) {
704                    before.icon = icon.clone();
705                };
706
707                if partial.last_message_id.is_some() {
708                    before.last_message_id = last_message_id.clone()
709                };
710
711                if partial.permissions.is_some() {
712                    before.permissions = *permissions;
713                };
714
715                if partial.nsfw.is_some() {
716                    before.nsfw = Some(*nsfw);
717                };
718            }
719            Channel::TextChannel {
720                name,
721                description,
722                icon,
723                last_message_id,
724                default_permissions,
725                role_permissions,
726                nsfw,
727                voice,
728                slowmode,
729                ..
730            } => {
731                if partial.name.is_some() {
732                    before.name = Some(name.clone());
733                };
734
735                if partial.description.is_some() || remove.contains(&FieldsChannel::Description) {
736                    before.description = description.clone();
737                };
738
739                if partial.icon.is_some() || remove.contains(&FieldsChannel::Icon) {
740                    before.icon = icon.clone();
741                };
742
743                if partial.last_message_id.is_some() {
744                    before.last_message_id = last_message_id.clone()
745                };
746
747                if partial.default_permissions.is_some()
748                    || remove.contains(&FieldsChannel::DefaultPermissions)
749                {
750                    before.default_permissions = *default_permissions;
751                };
752
753                if partial.role_permissions.is_some() {
754                    before.role_permissions = Some(role_permissions.clone());
755                };
756
757                if partial.nsfw.is_some() {
758                    before.nsfw = Some(*nsfw);
759                };
760
761                if partial.voice.is_some() || remove.contains(&FieldsChannel::Voice) {
762                    before.voice = voice.clone();
763                };
764
765                if partial.slowmode.is_some() {
766                    before.slowmode = *slowmode;
767                }
768            }
769        }
770
771        before
772    }
773
774    /// Acknowledge a message
775    pub async fn ack(&self, user: &str, message: &str, amqp: &AMQP) -> Result<()> {
776        EventV1::ChannelAck {
777            id: self.id().to_string(),
778            user: user.to_string(),
779            message_id: message.to_string(),
780        }
781        .private(user.to_string())
782        .await;
783
784        crate::util::acker::ack_channel(user, self.id(), message, amqp).await
785    }
786
787    /// Remove user from a group
788    pub async fn remove_user_from_group(
789        &self,
790        db: &Database,
791        amqp: &AMQP,
792        user: &User,
793        by_id: Option<&str>,
794        silent: bool,
795    ) -> Result<()> {
796        match &self {
797            Channel::Group {
798                id,
799                name,
800                owner,
801                recipients,
802                ..
803            } => {
804                if &user.id == owner {
805                    if let Some(new_owner) = recipients.iter().find(|x| *x != &user.id) {
806                        db.update_channel(
807                            id,
808                            &PartialChannel {
809                                owner: Some(new_owner.into()),
810                                ..Default::default()
811                            },
812                            vec![],
813                        )
814                        .await?;
815
816                        SystemMessage::ChannelOwnershipChanged {
817                            from: owner.to_string(),
818                            to: new_owner.to_string(),
819                        }
820                        .into_message(id.to_string())
821                        .send(
822                            db,
823                            Some(amqp),
824                            MessageAuthor::System {
825                                username: name,
826                                avatar: None,
827                            },
828                            None,
829                            None,
830                            self,
831                            false,
832                        )
833                        .await
834                        .ok();
835                    } else {
836                        return self.delete(db).await;
837                    }
838                }
839
840                db.remove_user_from_group(id, &user.id).await?;
841
842                EventV1::ChannelGroupLeave {
843                    id: id.to_string(),
844                    user: user.id.to_string(),
845                }
846                .p(id.to_string())
847                .await;
848
849                if !silent {
850                    if let Some(by) = by_id {
851                        SystemMessage::UserRemove {
852                            id: user.id.to_string(),
853                            by: by.to_string(),
854                        }
855                    } else {
856                        SystemMessage::UserLeft {
857                            id: user.id.to_string(),
858                        }
859                    }
860                    .into_message(id.to_string())
861                    .send(
862                        db,
863                        Some(amqp),
864                        MessageAuthor::System {
865                            username: &user.username,
866                            avatar: user.avatar.as_ref().map(|file| file.id.as_ref()),
867                        },
868                        None,
869                        None,
870                        self,
871                        false,
872                    )
873                    .await
874                    .ok();
875                }
876
877                Ok(())
878            }
879
880            _ => Err(create_error!(InvalidOperation)),
881        }
882    }
883
884    /// Delete a channel
885    pub async fn delete(&self, db: &Database) -> Result<()> {
886        let id = self.id().to_string();
887        EventV1::ChannelDelete { id: id.clone() }.p(id).await;
888        // TODO: missing functionality:
889        // - group invites
890        // - channels list / categories list on server
891        db.delete_channel(self).await
892    }
893}
894
895#[cfg(feature = "mongodb")]
896impl IntoDocumentPath for FieldsChannel {
897    fn as_path(&self) -> Option<&'static str> {
898        Some(match self {
899            FieldsChannel::Description => "description",
900            FieldsChannel::Icon => "icon",
901            FieldsChannel::DefaultPermissions => "default_permissions",
902            FieldsChannel::Voice => "voice",
903            FieldsChannel::Slowmode => "slowmode",
904        })
905    }
906}
907
908#[cfg(test)]
909mod tests {
910    use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
911
912    use crate::{fixture, util::permissions::DatabasePermissionQuery};
913
914    #[tokio::test]
915    async fn permissions_group_channel() {
916        database_test!(|db| async move {
917            fixture!(db, "group_with_members",
918                owner user 0
919                member1 user 1
920                member2 user 2
921                channel channel 3);
922
923            let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
924            assert!(calculate_channel_permissions(&mut query)
925                .await
926                .has_channel_permission(ChannelPermission::SendMessage));
927
928            let mut query = DatabasePermissionQuery::new(&db, &member1).channel(&channel);
929            assert!(calculate_channel_permissions(&mut query)
930                .await
931                .has_channel_permission(ChannelPermission::SendMessage));
932
933            let mut query = DatabasePermissionQuery::new(&db, &member2).channel(&channel);
934            assert!(!calculate_channel_permissions(&mut query)
935                .await
936                .has_channel_permission(ChannelPermission::SendMessage));
937        });
938    }
939
940    #[tokio::test]
941    async fn permissions_text_channel() {
942        database_test!(|db| async move {
943            fixture!(db, "server_with_roles",
944                owner user 0
945                moderator user 1
946                user user 2
947                channel channel 3);
948
949            let mut query = DatabasePermissionQuery::new(&db, &owner).channel(&channel);
950            assert!(calculate_channel_permissions(&mut query)
951                .await
952                .has_channel_permission(ChannelPermission::SendMessage));
953
954            let mut query = DatabasePermissionQuery::new(&db, &moderator).channel(&channel);
955            assert!(calculate_channel_permissions(&mut query)
956                .await
957                .has_channel_permission(ChannelPermission::SendMessage));
958
959            let mut query = DatabasePermissionQuery::new(&db, &user).channel(&channel);
960            assert!(!calculate_channel_permissions(&mut query)
961                .await
962                .has_channel_permission(ChannelPermission::SendMessage));
963        });
964    }
965}