diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index 04267b580fc3bf889c5b7e0b02e87eec573ccd21..5138793bf99d0aefbd51e006b66b08ebb08f2d60 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -1,7 +1,8 @@
 use crate::database::*;
 use crate::notifications::events::ClientboundNotification;
 use crate::util::result::{Error, Result};
-use mongodb::bson::to_document;
+use mongodb::bson::{doc, to_document};
+use rocket_contrib::json::JsonValue;
 use serde::{Deserialize, Serialize};
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
@@ -62,4 +63,50 @@ impl Channel {
 
         Ok(())
     }
+
+    pub async fn publish_update(&self, partial: JsonValue) -> Result<()> {
+        let id = self.id().to_string();
+        ClientboundNotification::ChannelUpdate(partial)
+            .publish(id)
+            .await
+            .ok();
+
+        Ok(())
+    }
+
+    pub async fn delete(&self) -> Result<()> {
+        let id = self.id();
+        get_collection("messages")
+            .delete_many(
+                doc! {
+                    "channel": id
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_many",
+                with: "messages",
+            })?;
+
+        get_collection("channels")
+            .delete_one(
+                doc! {
+                    "_id": id
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_one",
+                with: "channel",
+            })?;
+
+        ClientboundNotification::ChannelDelete { id: id.to_string() }
+            .publish(id.to_string())
+            .await
+            .ok();
+
+        Ok(())
+    }
 }
diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs
index ed3bd0fc0811d2642a7dda0abf51fa17de6d76d6..fd38f940fca3c99761e7a03dff4ea290c7fe8840 100644
--- a/src/database/entities/message.rs
+++ b/src/database/entities/message.rs
@@ -3,30 +3,11 @@ use crate::{
     notifications::events::ClientboundNotification,
     util::result::{Error, Result},
 };
-use mongodb::bson::{to_bson, DateTime};
+use mongodb::bson::{doc, to_bson, DateTime};
+use rocket_contrib::json::JsonValue;
 use serde::{Deserialize, Serialize};
 use ulid::Ulid;
 
-/*#[derive(Serialize, Deserialize, Debug)]
-pub struct PreviousEntry {
-    pub content: String,
-    pub time: DateTime,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub struct Message {
-    #[serde(rename = "_id")]
-    pub id: String,
-    pub nonce: Option<String>,
-    pub channel: String,
-    pub author: String,
-
-    pub content: String,
-    pub edited: Option<DateTime>,
-
-    pub previous_content: Vec<PreviousEntry>,
-}*/
-
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct Message {
     #[serde(rename = "_id")]
@@ -72,9 +53,9 @@ impl Message {
         Ok(())
     }
 
-    pub async fn publish_edit(self) -> Result<()> {
+    pub async fn publish_update(&self, partial: JsonValue) -> Result<()> {
         let channel = self.channel.clone();
-        ClientboundNotification::MessageEdit(self)
+        ClientboundNotification::MessageUpdate(partial)
             .publish(channel)
             .await
             .ok();
@@ -82,9 +63,22 @@ impl Message {
         Ok(())
     }
 
-    pub async fn publish_delete(self) -> Result<()> {
+    pub async fn delete(&self) -> Result<()> {
+        get_collection("messages")
+            .delete_one(
+                doc! {
+                    "_id": &self.id
+                },
+                None,
+            )
+            .await
+            .map_err(|_| Error::DatabaseError {
+                operation: "delete_one",
+                with: "message",
+            })?;
+
         let channel = self.channel.clone();
-        ClientboundNotification::MessageDelete { id: self.id }
+        ClientboundNotification::MessageDelete { id: self.id.clone() }
             .publish(channel)
             .await
             .ok();
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 9056ce04da1d1a0d05cea36a569a96a64601cc63..e51ff9a4e05d1427a54c54671c9ef897bc2130bb 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -1,5 +1,6 @@
 use hive_pubsub::PubSub;
 use rauth::auth::Session;
+use rocket_contrib::json::JsonValue;
 use serde::{Deserialize, Serialize};
 use snafu::Snafu;
 
@@ -38,12 +39,13 @@ pub enum ClientboundNotification {
     },
 
     Message(Message),
-    MessageEdit(Message),
+    MessageUpdate(JsonValue),
     MessageDelete {
         id: String,
     },
 
     ChannelCreate(Channel),
+    ChannelUpdate(JsonValue),
     ChannelGroupJoin {
         id: String,
         user: String,
@@ -52,6 +54,9 @@ pub enum ClientboundNotification {
         id: String,
         user: String,
     },
+    ChannelDelete {
+        id: String,
+    },
 
     UserRelationship {
         id: String,
@@ -95,17 +100,31 @@ pub fn prehandle_hook(notification: &ClientboundNotification) {
                 .unsubscribe(&user.to_string(), &id.to_string())
                 .ok();
         }
-        ClientboundNotification::UserRelationship { id, user, status } => match status {
-            RelationshipStatus::None => {
+        ClientboundNotification::UserRelationship { id, user, status } => {
+            if status != &RelationshipStatus::None {
+                subscribe_if_exists(id.clone(), user.clone()).ok();
+            }
+        }
+        _ => {}
+    }
+}
+
+pub fn posthandle_hook(notification: &ClientboundNotification) {
+    match &notification {
+        ClientboundNotification::ChannelDelete { id } => {
+            get_hive()
+                .hive
+                .drop_topic(&id)
+                .ok();
+        }
+        ClientboundNotification::UserRelationship { id, user, status } => {
+            if status == &RelationshipStatus::None {
                 get_hive()
                     .hive
                     .unsubscribe(&id.to_string(), &user.to_string())
                     .ok();
-            }
-            _ => {
-                subscribe_if_exists(id.clone(), user.clone()).ok();
-            }
-        },
+            }   
+        }
         _ => {}
     }
 }
diff --git a/src/notifications/hive.rs b/src/notifications/hive.rs
index e7d79bfad092e9ee4a5d40f803d15f3fec5b8d94..3541e5308f7bab1344195280d20bf193a73eaf0b 100644
--- a/src/notifications/hive.rs
+++ b/src/notifications/hive.rs
@@ -14,6 +14,8 @@ static HIVE: OnceCell<Hive> = OnceCell::new();
 pub async fn init_hive() {
     let hive = MongodbPubSub::new(
         |ids, notification| {
+            super::events::posthandle_hook(&notification);
+
             if let Ok(data) = to_string(&notification) {
                 debug!("Pushing out notification. {}", data);
                 websocket::publish(ids, notification);
diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs
index aea50711ae6e663310bf756468ff7e642aa76166..ac871c59bcf1ad5f165dad4768d1cdc319526135 100644
--- a/src/routes/channels/delete_channel.rs
+++ b/src/routes/channels/delete_channel.rs
@@ -1,5 +1,5 @@
-use crate::database::*;
 use crate::util::result::{Error, Result};
+use crate::{database::*, notifications::events::ClientboundNotification};
 
 use mongodb::bson::doc;
 
@@ -12,7 +12,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
         Err(Error::LabelMe)?
     }
 
-    match target {
+    match &target {
         Channel::SavedMessages { .. } => Err(Error::NoEffect),
         Channel::DirectMessage { .. } => {
             get_collection("channels")
@@ -35,6 +35,77 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
 
             Ok(())
         }
-        _ => unimplemented!(),
+        Channel::Group {
+            id,
+            owner,
+            recipients,
+            ..
+        } => {
+            if &user.id == owner {
+                if let Some(new_owner) = recipients.iter().find(|x| *x != &user.id) {
+                    get_collection("channels")
+                        .update_one(
+                            doc! {
+                                "_id": &id
+                            },
+                            doc! {
+                                "$set": {
+                                    "owner": new_owner
+                                },
+                                "$pull": {
+                                    "recipients": &user.id
+                                }
+                            },
+                            None,
+                        )
+                        .await
+                        .map_err(|_| Error::DatabaseError {
+                            operation: "update_one",
+                            with: "channel",
+                        })?;
+
+                    target.publish_update(json!({ "owner": new_owner })).await?;
+                } else {
+                    return target.delete().await
+                }
+            } else {
+                get_collection("channels")
+                    .update_one(
+                        doc! {
+                            "_id": &id
+                        },
+                        doc! {
+                            "$pull": {
+                                "recipients": &user.id
+                            }
+                        },
+                        None,
+                    )
+                    .await
+                    .map_err(|_| Error::DatabaseError {
+                        operation: "update_one",
+                        with: "channel",
+                    })?;
+            }
+
+            ClientboundNotification::ChannelGroupLeave {
+                id: id.clone(),
+                user: user.id.clone(),
+            }
+            .publish(id.clone())
+            .await
+            .ok();
+
+            Message::create(
+                "00000000000000000000000000".to_string(),
+                id.clone(),
+                format!("<@{}> left the group.", user.id),
+            )
+            .publish()
+            .await
+            .ok();
+
+            Ok(())
+        }
     }
 }
diff --git a/src/routes/channels/message_delete.rs b/src/routes/channels/message_delete.rs
index 61204e70d76bfaae7ddc04c17d69ddfee1a792fd..f47c635935754ff0724e41140073f5333a226543 100644
--- a/src/routes/channels/message_delete.rs
+++ b/src/routes/channels/message_delete.rs
@@ -20,20 +20,5 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
         }
     }
 
-    get_collection("messages")
-        .delete_one(
-            doc! {
-                "_id": &message.id
-            },
-            None,
-        )
-        .await
-        .map_err(|_| Error::DatabaseError {
-            operation: "delete_one",
-            with: "message",
-        })?;
-
-    message.publish_delete().await?;
-
-    Ok(())
+    message.delete().await
 }
diff --git a/src/routes/channels/message_edit.rs b/src/routes/channels/message_edit.rs
index 0e57c1868cd260a3c999b9ae8fb2dbde67acef06..cedba51e2011fd67215386b1a69210bbd7700088 100644
--- a/src/routes/channels/message_edit.rs
+++ b/src/routes/channels/message_edit.rs
@@ -25,7 +25,7 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
         Err(Error::LabelMe)?
     }
 
-    let mut message = msg.fetch_message().await?;
+    let message = msg.fetch_message().await?;
     if message.author != user.id {
         Err(Error::CannotEditMessage)?
     }
@@ -50,9 +50,5 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
             with: "message",
         })?;
 
-    message.content = edit.content.clone();
-    message.edited = Some(DateTime(edited));
-    message.publish_edit().await?;
-
-    Ok(())
+    message.publish_update(json!({ "content": edit.content, "edited": DateTime(edited) })).await
 }
diff --git a/src/routes/root.rs b/src/routes/root.rs
index 8f8f5e8368112da948a7ae12b17b283a7ada7f72..7b5d4163015ba8c75180a7cbb61592bbdc96738c 100644
--- a/src/routes/root.rs
+++ b/src/routes/root.rs
@@ -8,7 +8,7 @@ use rocket_contrib::json::JsonValue;
 #[get("/")]
 pub async fn root() -> JsonValue {
     json!({
-        "revolt": "0.3.1-alpha.0",
+        "revolt": "0.3.1",
         "features": {
             "registration": !*DISABLE_REGISTRATION,
             "captcha": {