From 3d3db80e6197fa8b774769c2788a1ad1aa52ab67 Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Mon, 18 Jan 2021 14:50:17 +0000
Subject: [PATCH] Channel subscription, message sending, channel delete.

---
 src/database/entities/channel.rs      |  8 ++++++
 src/database/entities/message.rs      | 38 +++++++++++++++++++++++++--
 src/database/permissions/channel.rs   | 17 ++++++++++++
 src/notifications/events.rs           |  2 ++
 src/notifications/subscriptions.rs    | 37 +++++++++++++++++++++++++-
 src/routes/channels/delete_channel.rs | 37 ++++++++++++++++++++++++++
 src/routes/channels/mod.rs            |  4 ++-
 7 files changed, 139 insertions(+), 4 deletions(-)
 create mode 100644 src/routes/channels/delete_channel.rs

diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index 42bc1fc..30e24b7 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -28,6 +28,14 @@ pub enum Channel {
 }
 
 impl Channel {
+    pub fn id(&self) -> &str {
+        match self {
+            Channel::SavedMessages { id, .. } => id,
+            Channel::DirectMessage { id, .. } => id,
+            Channel::Group { id, .. } => id,
+        }
+    }
+
     pub async fn save(&self) -> Result<()> {
         get_collection("channels")
             .insert_one(
diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs
index 59f51cb..d5a1197 100644
--- a/src/database/entities/message.rs
+++ b/src/database/entities/message.rs
@@ -1,5 +1,6 @@
-// use mongodb::bson::DateTime;
-// use serde::{Deserialize, Serialize};
+use crate::{database::*, notifications::events::ClientboundNotification, util::result::Result};
+use mongodb::bson::{DateTime, to_bson};
+use serde::{Deserialize, Serialize};
 
 /*#[derive(Serialize, Deserialize, Debug)]
 pub struct PreviousEntry {
@@ -20,3 +21,36 @@ pub struct Message {
 
     pub previous_content: Vec<PreviousEntry>,
 }*/
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Message {
+    #[serde(rename = "_id")]
+    pub id: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub nonce: Option<String>,
+    pub channel: String,
+    pub author: String,
+
+    pub content: String,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub edited: Option<DateTime>,
+}
+
+impl Message {
+    pub async fn send(self) -> Result<()> {
+        get_collection("messages")
+            .insert_one(
+                to_bson(&self).unwrap().as_document().unwrap().clone(),
+                None
+            )
+            .await;
+        
+        let channel = self.channel.clone();
+        ClientboundNotification::Message(self)
+            .publish(channel)
+            .await
+            .ok();
+
+        Ok(())
+    }
+}
diff --git a/src/database/permissions/channel.rs b/src/database/permissions/channel.rs
index cfb4ebd..37ff81e 100644
--- a/src/database/permissions/channel.rs
+++ b/src/database/permissions/channel.rs
@@ -28,6 +28,23 @@ pub async fn calculate(user: &User, target: &Channel) -> ChannelPermissions<[u32
                 ChannelPermissions([ 0 ])
             }
         }
+        Channel::DirectMessage { recipients, .. } => {
+            if recipients.iter().find(|x| *x == &user.id).is_some() {
+                if let Some(recipient) = recipients
+                    .iter()
+                    .find(|x| *x != &user.id) {
+                    let perms = super::user::calculate(&user, recipient).await;
+
+                    if perms.get_send_message() {
+                        return ChannelPermissions([ ChannelPermission::View + ChannelPermission::SendMessage ]);
+                    }
+                    
+                    return ChannelPermissions([ ChannelPermission::View as u32 ]);
+                }
+            }
+
+            ChannelPermissions([ 0 ])
+        }
         _ => unreachable!()
     }
 }
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 0e3be5d..cf4f0e4 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -36,6 +36,8 @@ pub enum ClientboundNotification {
         channels: Vec<Channel>
     },
 
+    Message(Message),
+
     /*MessageCreate {
         id: String,
         nonce: Option<String>,
diff --git a/src/notifications/subscriptions.rs b/src/notifications/subscriptions.rs
index b099e48..0c9105c 100644
--- a/src/notifications/subscriptions.rs
+++ b/src/notifications/subscriptions.rs
@@ -1,7 +1,10 @@
 use crate::database::*;
 
-use super::hive::get_hive;
+use futures::StreamExt;
+use mongodb::bson::doc;
 use hive_pubsub::PubSub;
+use super::hive::get_hive;
+use mongodb::options::FindOptions;
 
 pub async fn generate_subscriptions(user: &User) -> Result<(), String> {
     let hive = get_hive();
@@ -13,5 +16,37 @@ pub async fn generate_subscriptions(user: &User) -> Result<(), String> {
         }
     }
 
+    let mut cursor = get_collection("channels")
+        .find(
+            doc! {
+                "$or": [
+                    {
+                        "type": "SavedMessages",
+                        "user": &user.id
+                    },
+                    {
+                        "type": "DirectMessage",
+                        "recipients": &user.id,
+                        "active": true
+                    },
+                    {
+                        "type": "Group",
+                        "recipients": &user.id
+                    }
+                ]
+            },
+            FindOptions::builder()
+                .projection(doc! { "_id": 1 })
+                .build()
+        )
+        .await
+        .map_err(|_| "Failed to fetch channels.".to_string())?;
+    
+    while let Some(result) = cursor.next().await {
+        if let Ok(doc) = result {
+            hive.subscribe(user.id.clone(), doc.get_str("_id").unwrap().to_string())?;
+        }
+    }
+
     Ok(())
 }
diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs
new file mode 100644
index 0000000..8704d81
--- /dev/null
+++ b/src/routes/channels/delete_channel.rs
@@ -0,0 +1,37 @@
+use crate::database::*;
+use crate::util::result::{Error, Result};
+
+use mongodb::bson::doc;
+
+#[delete("/<target>")]
+pub async fn req(user: User, target: Ref) -> Result<()> {
+    let target = target.fetch_channel().await?;
+
+    let perm = permissions::channel::calculate(&user, &target).await;
+    if !perm.get_view() {
+        Err(Error::LabelMe)?
+    }
+
+    match target {
+        Channel::SavedMessages { .. } => Err(Error::NoEffect),
+        Channel::DirectMessage { .. } => {
+            get_collection("channels")
+                .update_one(
+                    doc! {
+                        "_id": target.id()
+                    },
+                    doc! {
+                        "$set": {
+                            "active": false
+                        }
+                    },
+                    None
+                )
+                .await
+                .map_err(|_| Error::DatabaseError { operation: "update_one", with: "channel" })?;
+            
+            Ok(())
+        },
+        _ => unimplemented!()
+    }
+}
diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs
index 2137b3c..2f2d3b9 100644
--- a/src/routes/channels/mod.rs
+++ b/src/routes/channels/mod.rs
@@ -1,9 +1,11 @@
 use rocket::Route;
 
 mod fetch_channel;
+mod delete_channel;
 
 pub fn routes() -> Vec<Route> {
     routes![
-        fetch_channel::req
+        fetch_channel::req,
+        delete_channel::req
     ]
 }
-- 
GitLab