From 4fbd6c816d5f0878fe62db684db6ef47de2f3e14 Mon Sep 17 00:00:00 2001
From: Paul Makles <paulmakles@gmail.com>
Date: Sun, 12 Apr 2020 16:42:13 +0100
Subject: [PATCH] Add join / leave messages for groups.

---
 src/database/message.rs |  68 ++++++++++++++++-
 src/database/mutual.rs  |   2 +-
 src/routes/channel.rs   | 157 ++++++++++++++++++++--------------------
 src/routes/mod.rs       |   5 +-
 src/routes/root.rs      |   2 +-
 5 files changed, 149 insertions(+), 85 deletions(-)

diff --git a/src/database/message.rs b/src/database/message.rs
index dd273a7..e47be11 100644
--- a/src/database/message.rs
+++ b/src/database/message.rs
@@ -1,4 +1,8 @@
-use bson::UtcDateTime;
+use super::get_collection;
+use crate::guards::channel::ChannelRef;
+use crate::routes::channel::ChannelType;
+
+use bson::{doc, to_bson, UtcDateTime};
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize, Debug)]
@@ -11,7 +15,7 @@ pub struct PreviousEntry {
 pub struct Message {
     #[serde(rename = "_id")]
     pub id: String,
-    // pub nonce: String, used internally
+    pub nonce: Option<String>,
     pub channel: String,
     pub author: String,
 
@@ -24,3 +28,63 @@ pub struct Message {
 // ? TODO: write global send message
 // ? pub fn send_message();
 // ? handle websockets?
+impl Message {
+    pub fn send(&self, target: &ChannelRef) -> bool {
+        if get_collection("messages")
+            .insert_one(to_bson(&self).unwrap().as_document().unwrap().clone(), None)
+            .is_ok()
+        {
+            let short_content: String = self.content.chars().take(24).collect();
+
+            // !! this stuff can be async
+            if target.channel_type == ChannelType::DM as u8
+                || target.channel_type == ChannelType::GROUPDM as u8
+            {
+                let mut update = doc! {
+                    "$set": {
+                        "last_message": {
+                            "id": &self.id,
+                            "user_id": &self.author,
+                            "short_content": short_content,
+                        }
+                    }
+                };
+
+                if target.channel_type == ChannelType::DM as u8 {
+                    update
+                        .get_document_mut("$set")
+                        .unwrap()
+                        .insert("active", true);
+                }
+
+                if get_collection("channels")
+                    .update_one(doc! { "_id": &target.id }, update, None)
+                    .is_ok()
+                {
+                    true
+                } else {
+                    false
+                }
+            } else {
+                true
+            }
+
+        /*websocket::queue_message(
+            get_recipients(&target),
+            json!({
+                "type": "message",
+                "data": {
+                    "id": id.clone(),
+                    "nonce": nonce,
+                    "channel": target.id,
+                    "author": user.id,
+                    "content": content,
+                },
+            })
+            .to_string(),
+        );*/
+        } else {
+            false
+        }
+    }
+}
diff --git a/src/database/mutual.rs b/src/database/mutual.rs
index 162dcc8..412cb1d 100644
--- a/src/database/mutual.rs
+++ b/src/database/mutual.rs
@@ -111,7 +111,7 @@ pub fn has_mutual_connection(user_id: &str, target_id: &str, with_permission: bo
 
                     let permissions = guild.get_i32("default_permissions").unwrap() as u32;
 
-                    if MemberPermissions([ permissions ]).get_send_direct_messages() {
+                    if MemberPermissions([permissions]).get_send_direct_messages() {
                         return true;
                     }
                 }
diff --git a/src/routes/channel.rs b/src/routes/channel.rs
index bbee4f5..07c12f4 100644
--- a/src/routes/channel.rs
+++ b/src/routes/channel.rs
@@ -183,7 +183,7 @@ pub fn add_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<
 
     with_permissions!(user, target);
 
-    let recp = target.recipients.unwrap();
+    let recp = target.recipients.as_ref().unwrap();
     if recp.len() == 50 {
         return Some(Response::BadRequest(
             json!({ "error": "Maximum group size is 50." }),
@@ -201,7 +201,7 @@ pub fn add_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<
         Relationship::Friend => {
             if database::get_collection("channels")
                 .update_one(
-                    doc! { "_id": &target.id },
+                    doc! { "_id": target.id.clone() },
                     doc! {
                         "$push": {
                             "recipients": &member.id
@@ -211,7 +211,23 @@ pub fn add_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<
                 )
                 .is_ok()
             {
-                Some(Response::Result(super::Status::Ok))
+                if (Message {
+                    id: Ulid::new().to_string(),
+                    nonce: None,
+                    channel: target.id.clone(),
+                    author: "system".to_string(),
+                    content: format!("<@{}> added <@{}> to the group.", &user.id, &member.id),
+                    edited: None,
+                    previous_content: None,
+                })
+                .send(&target)
+                {
+                    Some(Response::Result(super::Status::Ok))
+                } else {
+                    Some(Response::PartialStatus(
+                        json!({ "error": "Failed to send join message, but user has been added." }),
+                    ))
+                }
             } else {
                 Some(Response::InternalServerError(
                     json!({ "error": "Failed to add user to group." }),
@@ -243,7 +259,7 @@ pub fn remove_member(user: UserRef, target: ChannelRef, member: UserRef) -> Opti
         return Some(Response::LackingPermission(Permission::KickMembers));
     }
 
-    let set = vec_to_set(&target.recipients.unwrap());
+    let set = vec_to_set(target.recipients.as_ref().unwrap());
     if set.get(&member.id).is_none() {
         return Some(Response::BadRequest(
             json!({ "error": "User not in group!" }),
@@ -262,7 +278,23 @@ pub fn remove_member(user: UserRef, target: ChannelRef, member: UserRef) -> Opti
         )
         .is_ok()
     {
-        Some(Response::Result(super::Status::Ok))
+        if (Message {
+            id: Ulid::new().to_string(),
+            nonce: None,
+            channel: target.id.clone(),
+            author: "system".to_string(),
+            content: format!("<@{}> removed <@{}> from the group.", &user.id, &member.id),
+            edited: None,
+            previous_content: None,
+        })
+        .send(&target)
+        {
+            Some(Response::Result(super::Status::Ok))
+        } else {
+            Some(Response::PartialStatus(
+                json!({ "error": "Failed to send join message, but user has been removed." }),
+            ))
+        }
     } else {
         Some(Response::InternalServerError(
             json!({ "error": "Failed to add user to group." }),
@@ -323,15 +355,19 @@ pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> {
             }
         }
         1 => {
-            let mut recipients =
-                vec_to_set(&target.recipients.expect("Missing recipients on Group DM."));
-            let owner = target.owner.expect("Missing owner on Group DM.");
+            let mut recipients = vec_to_set(
+                target
+                    .recipients
+                    .as_ref()
+                    .expect("Missing recipients on Group DM."),
+            );
+            let owner = target.owner.as_ref().expect("Missing owner on Group DM.");
 
             if recipients.len() == 1 {
                 try_delete()
             } else {
                 recipients.remove(&user.id);
-                let new_owner = if owner == user.id {
+                let new_owner = if owner == &user.id {
                     recipients.iter().next().unwrap()
                 } else {
                     &owner
@@ -352,7 +388,23 @@ pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> {
                     )
                     .is_ok()
                 {
-                    Some(Response::Result(super::Status::Ok))
+                    if (Message {
+                        id: Ulid::new().to_string(),
+                        nonce: None,
+                        channel: target.id.clone(),
+                        author: "system".to_string(),
+                        content: format!("<@{}> left the group.", &user.id),
+                        edited: None,
+                        previous_content: None,
+                    })
+                    .send(&target)
+                    {
+                        Some(Response::Result(super::Status::Ok))
+                    } else {
+                        Some(Response::PartialStatus(
+                            json!({ "error": "Failed to send leave message, but you have left the group." }),
+                        ))
+                    }
                 } else {
                     Some(Response::InternalServerError(
                         json!({ "error": "Failed to remove you from the group." }),
@@ -453,76 +505,23 @@ pub fn send_message(
     }
 
     let id = Ulid::new().to_string();
-    Some(
-        if col
-            .insert_one(
-                doc! {
-                    "_id": &id,
-                    "nonce": nonce,
-                    "channel": &target.id,
-                    "author": &user.id,
-                    "content": &content,
-                },
-                None,
-            )
-            .is_ok()
-        {
-            let short_content: String = content.chars().take(24).collect();
-            let col = database::get_collection("channels");
-
-            // !! this stuff can be async
-            if target.channel_type == ChannelType::DM as u8
-                || target.channel_type == ChannelType::GROUPDM as u8
-            {
-                let mut update = doc! {
-                    "$set": {
-                        "last_message": {
-                            "id": &id,
-                            "user_id": &user.id,
-                            "short_content": short_content,
-                        }
-                    }
-                };
-
-                if target.channel_type == ChannelType::DM as u8 {
-                    update
-                        .get_document_mut("$set")
-                        .unwrap()
-                        .insert("active", true);
-                }
-
-                if col
-                    .update_one(doc! { "_id": &target.id }, update, None)
-                    .is_ok()
-                {
-                    Response::Success(json!({ "id": id }))
-                } else {
-                    Response::InternalServerError(json!({ "error": "Failed to update channel." }))
-                }
-            } else {
-                Response::Success(json!({ "id": id }))
-            }
+    let message = Message {
+        id: id.clone(),
+        nonce: Some(nonce),
+        channel: target.id.clone(),
+        author: user.id,
+        content,
+        edited: None,
+        previous_content: None,
+    };
 
-        /*websocket::queue_message(
-            get_recipients(&target),
-            json!({
-                "type": "message",
-                "data": {
-                    "id": id.clone(),
-                    "nonce": nonce,
-                    "channel": target.id,
-                    "author": user.id,
-                    "content": content,
-                },
-            })
-            .to_string(),
-        );*/
-        } else {
-            Response::InternalServerError(json!({
-                "error": "Failed database query."
-            }))
-        },
-    )
+    if message.send(&target) {
+        Some(Response::Success(json!({ "id": id })))
+    } else {
+        Some(Response::BadRequest(
+            json!({ "error": "Failed to send message." }),
+        ))
+    }
 }
 
 /// get a message
diff --git a/src/routes/mod.rs b/src/routes/mod.rs
index aeeac18..ebb40d2 100644
--- a/src/routes/mod.rs
+++ b/src/routes/mod.rs
@@ -19,6 +19,8 @@ pub enum Response {
     Success(JsonValue),
     #[response()]
     Redirect(Redirect),
+    #[response(status = 207)]
+    PartialStatus(JsonValue),
     #[response(status = 400)]
     BadRequest(JsonValue),
     #[response(status = 401)]
@@ -51,8 +53,7 @@ impl<'a> rocket::response::Responder<'a> for Permission {
             .header(ContentType::JSON)
             .sized_body(Cursor::new(format!(
                 "{{\"error\":\"Lacking permission: {:?}.\",\"permission\":{}}}",
-                self,
-                self as u32,
+                self, self as u32,
             )))
             .ok()
     }
diff --git a/src/routes/root.rs b/src/routes/root.rs
index 798d295..e9fc5b7 100644
--- a/src/routes/root.rs
+++ b/src/routes/root.rs
@@ -6,6 +6,6 @@ use bson::doc;
 #[get("/")]
 pub fn root() -> Response {
     Response::Success(json!({
-        "revolt": "0.0.1"
+        "revolt": "0.1.0"
     }))
 }
-- 
GitLab