revolt_database/models/channels/
model.rs

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