Skip to main content

revolt_database/models/channels/ops/
mongodb.rs

1use super::AbstractChannels;
2use crate::{AbstractServers, Channel, FieldsChannel, IntoDocumentPath, MongoDb, PartialChannel, util::ChunkedDatabaseGenerator};
3use bson::{Bson, Document};
4use futures::StreamExt;
5use mongodb::options::ReadConcern;
6use revolt_permissions::OverrideField;
7use revolt_result::Result;
8
9static COL: &str = "channels";
10
11#[async_trait]
12impl AbstractChannels for MongoDb {
13    /// Insert a new channel in the database
14    async fn insert_channel(&self, channel: &Channel) -> Result<()> {
15        query!(self, insert_one, COL, &channel).map(|_| ())
16    }
17
18    /// Fetch a channel from the database
19    async fn fetch_channel(&self, channel_id: &str) -> Result<Channel> {
20        query!(self, find_one_by_id, COL, channel_id)?.ok_or_else(|| create_error!(NotFound))
21    }
22
23    /// Fetch all channels from the database
24    async fn fetch_channels<'a>(&self, ids: &'a [String]) -> Result<Vec<Channel>> {
25        Ok(self
26            .col::<Channel>(COL)
27            .find(doc! {
28                "_id": {
29                    "$in": ids
30                }
31            })
32            .await
33            .map_err(|_| create_database_error!("fetch", "channels"))?
34            .filter_map(|s| async {
35                if cfg!(debug_assertions) {
36                    Some(s.unwrap())
37                } else {
38                    s.ok()
39                }
40            })
41            .collect()
42            .await)
43    }
44
45    /// Fetch all direct messages for a user
46    async fn find_direct_messages(&self, user_id: &str) -> Result<Vec<Channel>> {
47        query!(
48            self,
49            find,
50            COL,
51            doc! {
52                "$or": [
53                    {
54                        "$or": [
55                            {
56                                "channel_type": "DirectMessage"
57                            },
58                            {
59                                "channel_type": "Group"
60                            }
61                        ],
62                        "recipients": user_id
63                    },
64                    {
65                        "channel_type": "SavedMessages",
66                        "user": user_id
67                    }
68                ]
69            }
70        )
71    }
72
73    // Fetch all group dms for a user
74    async fn find_group_message_channels(&self, user_id: &str) -> Result<ChunkedDatabaseGenerator<Channel>> {
75        let mut session = self
76            .start_session()
77            .await
78            .map_err(|_| create_database_error!("start_session", COL))?;
79
80        session
81            .start_transaction()
82            .read_concern(ReadConcern::snapshot())
83            .await
84            .map_err(|_| create_database_error!("start_transaction", COL))?;
85
86        let cursor = self.col(COL)
87            .find(doc! {
88                "channel_type": "Group",
89                "recipients": user_id
90            })
91            .session(&mut session)
92            .batch_size(100)
93            .await
94            .map_err(|_| create_database_error!("find", COL))?;
95
96        Ok(ChunkedDatabaseGenerator::new_mongo(session, cursor))
97    }
98
99    // Fetch saved messages channel
100    async fn find_saved_messages_channel(&self, user_id: &str) -> Result<Channel> {
101        query!(
102            self,
103            find_one,
104            COL,
105            doc! {
106                "channel_type": "SavedMessages",
107                "user": user_id
108            }
109        )?
110        .ok_or_else(|| create_error!(InternalError))
111    }
112
113    // Fetch direct message channel (DM or Saved Messages)
114    async fn find_direct_message_channel(&self, user_a: &str, user_b: &str) -> Result<Channel> {
115        let doc = match (user_a, user_b) {
116            self_user if self_user.0 == self_user.1 => {
117                doc! {
118                    "channel_type": "SavedMessages",
119                    "user": self_user.0
120                }
121            }
122            users => {
123                doc! {
124                    "channel_type": "DirectMessage",
125                    "recipients": {
126                        "$all": [ users.0, users.1 ]
127                    }
128                }
129            }
130        };
131        query!(self, find_one, COL, doc)?.ok_or_else(|| create_error!(NotFound))
132    }
133
134    /// Insert a user to a group
135    async fn add_user_to_group(&self, channel: &str, user: &str) -> Result<()> {
136        self.col::<Document>(COL)
137            .update_one(
138                doc! {
139                    "_id": channel
140                },
141                doc! {
142                    "$push": {
143                        "recipients": user
144                    }
145                },
146            )
147            .await
148            .map(|_| ())
149            .map_err(|_| create_database_error!("update_one", "channel"))
150    }
151
152    /// Insert channel role permissions
153    async fn set_channel_role_permission(
154        &self,
155        channel: &str,
156        role: &str,
157        permissions: OverrideField,
158    ) -> Result<()> {
159        self.col::<Document>(COL)
160            .update_one(
161                doc! { "_id": channel },
162                doc! {
163                "$set": {
164                    "role_permissions.".to_owned() + role: permissions
165                }
166                },
167            )
168            .await
169            .map(|_| ())
170            .map_err(|_| create_database_error!("update_one", "channel"))
171    }
172
173    // Update channel
174    async fn update_channel(
175        &self,
176        id: &str,
177        channel: &PartialChannel,
178        remove: Vec<FieldsChannel>,
179    ) -> Result<()> {
180        query!(
181            self,
182            update_one_by_id,
183            COL,
184            id,
185            channel,
186            remove.iter().map(|x| x as &dyn IntoDocumentPath).collect(),
187            None
188        )
189        .map(|_| ())
190    }
191
192    // Remove a user from a group
193    async fn remove_user_from_group(&self, channel: &str, user: &str) -> Result<()> {
194        self.col::<Document>(COL)
195            .update_one(
196                doc! {
197                    "_id": channel
198                },
199                doc! {
200                    "$pull": {
201                        "recipients": user
202                    }
203                },
204            )
205            .await
206            .map(|_| ())
207            .map_err(|_| create_database_error!("update_one", "channels"))
208    }
209
210    // Remove a user from all specified groups
211    async fn remove_user_from_groups(&self, channel_ids: Vec<String>, user_id: &str) -> Result<()> {
212        self.col::<Document>(COL)
213            .update_many(
214                doc! {
215                    "_id": { "$in": channel_ids },
216                },
217                doc! {
218                    "$pull": {
219                        "recipients": user_id
220                    }
221                },
222            )
223            .await
224            .map(|_| ())
225            .map_err(|_| create_database_error!("update_many", COL))
226    }
227
228    // Delete a channel
229    async fn delete_channel(&self, channel: &Channel) -> Result<()> {
230        let id = channel.id().to_string();
231        let server_id = match channel {
232            Channel::TextChannel { server, .. } => Some(server),
233            _ => None,
234        };
235
236        // Delete invites and unreads.
237        self.delete_associated_channel_objects(Bson::String(id.to_string()))
238            .await?;
239
240        // Delete messages.
241        self.delete_bulk_messages(doc! {
242            "channel": &id
243        })
244        .await?;
245
246        // Remove from server object.
247        if let Some(server) = server_id {
248            let server = self.fetch_server(server).await?;
249            let mut update = doc! {
250                "$pull": {
251                    "channels": &id
252                }
253            };
254
255            if let Some(sys) = &server.system_messages {
256                let mut unset = doc! {};
257
258                if let Some(cid) = &sys.user_joined {
259                    if &id == cid {
260                        unset.insert("system_messages.user_joined", 1_i32);
261                    }
262                }
263
264                if let Some(cid) = &sys.user_left {
265                    if &id == cid {
266                        unset.insert("system_messages.user_left", 1_i32);
267                    }
268                }
269
270                if let Some(cid) = &sys.user_kicked {
271                    if &id == cid {
272                        unset.insert("system_messages.user_kicked", 1_i32);
273                    }
274                }
275
276                if let Some(cid) = &sys.user_banned {
277                    if &id == cid {
278                        unset.insert("system_messages.user_banned", 1_i32);
279                    }
280                }
281
282                if !unset.is_empty() {
283                    update.insert("$unset", unset);
284                }
285            }
286
287            self.col::<Document>("servers")
288                .update_one(
289                    doc! {
290                        "_id": server.id
291                    },
292                    update,
293                )
294                .await
295                .map_err(|_| create_database_error!("update_one", "servers"))?;
296        }
297
298        // Delete associated attachments
299        self.delete_many_attachments(doc! {
300            "used_for.id": &id
301        })
302        .await?;
303
304        // Delete the channel itself
305        query!(self, delete_one_by_id, COL, channel.id()).map(|_| ())
306    }
307}
308
309impl MongoDb {
310    pub async fn delete_associated_channel_objects(&self, id: Bson) -> Result<()> {
311        // Delete all invites to these channels.
312        self.col::<Document>("channel_invites")
313            .delete_many(doc! {
314                "channel": &id
315            })
316            .await
317            .map_err(|_| create_database_error!("delete_many", "channel_invites"))?;
318
319        // Delete unread message objects on channels.
320        self.col::<Document>("channel_unreads")
321            .delete_many(doc! {
322                "_id.channel": &id
323            })
324            .await
325            .map_err(|_| create_database_error!("delete_many", "channel_unreads"))
326            .map(|_| ())?;
327
328        // update many attachments with parent id
329
330        // Delete all webhooks on this channel.
331        self.col::<Document>("webhooks")
332            .delete_many(doc! {
333                "channel": &id
334            })
335            .await
336            .map_err(|_| create_database_error!("delete_many", "webhooks"))
337            .map(|_| ())
338    }
339}