From 7293abfc53df3a8f6a56aad4530102e491e66c3d Mon Sep 17 00:00:00 2001
From: Paul <paulmakles@gmail.com>
Date: Fri, 7 May 2021 18:00:21 +0100
Subject: [PATCH] Add january support.

---
 Cargo.lock                                 |  10 ++
 Cargo.toml                                 |   1 +
 set_version.sh                             |   2 +-
 src/database/entities/channel.rs           |  12 +-
 src/database/entities/january.rs           |  71 ++++++++++
 src/database/entities/message.rs           | 151 ++++++++++++---------
 src/database/entities/mod.rs               |   4 +-
 src/notifications/events.rs                |   8 +-
 src/notifications/websocket.rs             |  16 +--
 src/routes/channels/delete_channel.rs      |   4 +-
 src/routes/channels/edit_channel.rs        |   4 +-
 src/routes/channels/group_add_member.rs    |   4 +-
 src/routes/channels/group_remove_member.rs |   4 +-
 src/routes/users/add_friend.rs             |  57 ++++----
 src/routes/users/block_user.rs             |  60 ++++----
 src/routes/users/change_username.rs        |   4 +-
 src/routes/users/edit_user.rs              |  12 +-
 src/routes/users/remove_friend.rs          |  28 ++--
 src/routes/users/unblock_user.rs           |  32 ++---
 src/util/variables.rs                      |   2 +
 20 files changed, 279 insertions(+), 207 deletions(-)
 create mode 100644 src/database/entities/january.rs

diff --git a/Cargo.lock b/Cargo.lock
index 6f366a9..c27ed1a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1389,6 +1389,15 @@ version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
 
+[[package]]
+name = "linkify"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1986921c3c13e81df623c66a298d4b130c061bcb98a01f5b2d3ac402b1649a7f"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "lock_api"
 version = "0.3.4"
@@ -2490,6 +2499,7 @@ dependencies = [
  "impl_ops",
  "lazy_static",
  "lettre",
+ "linkify",
  "log",
  "many-to-many",
  "md5",
diff --git a/Cargo.toml b/Cargo.toml
index cbde4f2..8d8a3ba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,7 @@ ulid = "0.4.1"
 rand = "0.7.3"
 time = "0.2.16"
 base64 = "0.13.0"
+linkify = "0.6.0"
 dotenv = "0.15.0"
 futures = "0.3.8"
 chrono = "0.4.15"
diff --git a/set_version.sh b/set_version.sh
index ffe5b03..bc53540 100755
--- a/set_version.sh
+++ b/set_version.sh
@@ -1,3 +1,3 @@
 #!/bin/bash
-export version=0.4.1-alpha.7-patch.2
+export version=0.4.1-alpha.8
 echo "pub const VERSION: &str = \"${version}\";" > src/version.rs
diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs
index 1c7c96a..7f61b70 100644
--- a/src/database/entities/channel.rs
+++ b/src/database/entities/channel.rs
@@ -94,9 +94,7 @@ impl Channel {
 
         let channel_id = self.id().to_string();
         ClientboundNotification::ChannelCreate(self)
-            .publish(channel_id)
-            .await
-            .ok();
+            .publish(channel_id);
 
         Ok(())
     }
@@ -108,9 +106,7 @@ impl Channel {
             data,
             clear: None
         }
-        .publish(id)
-        .await
-        .ok();
+        .publish(id);
 
         Ok(())
     }
@@ -193,9 +189,7 @@ impl Channel {
             })?;
 
         ClientboundNotification::ChannelDelete { id: id.to_string() }
-            .publish(id.to_string())
-            .await
-            .ok();
+            .publish(id.to_string());
         
         if let Channel::Group { icon, .. } = self {
             if let Some(attachment) = icon {
diff --git a/src/database/entities/january.rs b/src/database/entities/january.rs
new file mode 100644
index 0000000..8810908
--- /dev/null
+++ b/src/database/entities/january.rs
@@ -0,0 +1,71 @@
+use serde::{Serialize, Deserialize};
+use linkify::{LinkFinder, LinkKind};
+use crate::util::{result::{Error, Result}, variables::JANUARY_URL};
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub enum MediaSize {
+    Large,
+    Preview,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Media {
+    pub url: String,
+    pub width: isize,
+    pub height: isize,
+    pub size: MediaSize,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct Metadata {
+    #[serde(skip_serializing_if = "Option::is_none")]
+    title: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    description: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    image: Option<Media>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(tag = "type")]
+pub enum Embed {
+    Website(Metadata),
+    Image(Media),
+    None,
+}
+
+impl Embed {
+    pub async fn generate(content: String) -> Result<Vec<Embed>> {
+        // FIXME: allow multiple links
+        let mut finder = LinkFinder::new();
+        finder.kinds(&[LinkKind::Url]);
+        let links: Vec<_> = finder.links(&content).collect();
+
+        if links.len() == 0 {
+            return Err(Error::LabelMe);
+        }
+
+        let link = &links[0];
+
+        let client = reqwest::Client::new();
+        let result = client
+            .get(&format!("{}/embed", *JANUARY_URL))
+            .query(&[("url", link.as_str())])
+            .send()
+            .await;
+
+        match result {
+            Err(_) => return Err(Error::LabelMe),
+            Ok(result) => match result.status() {
+                reqwest::StatusCode::OK => {
+                    let res: Embed = result.json()
+                        .await
+                        .map_err(|_| Error::InvalidOperation)?;
+
+                    Ok(vec![ res ])
+                },
+                _ => return Err(Error::LabelMe),
+            },
+        }
+    }
+}
diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs
index 4960b78..5e23d41 100644
--- a/src/database/entities/message.rs
+++ b/src/database/entities/message.rs
@@ -17,12 +17,6 @@ use web_push::{
     ContentEncoding, SubscriptionInfo, VapidSignatureBuilder, WebPushClient, WebPushMessageBuilder,
 };
 
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(tag = "type")]
-pub enum MessageEmbed {
-    Dummy,
-}
-
 #[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(tag = "type")]
 pub enum SystemMessage {
@@ -60,7 +54,7 @@ pub struct Message {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub edited: Option<DateTime>,
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub embeds: Option<MessageEmbed>,
+    pub embeds: Option<Vec<Embed>>,
 }
 
 impl Message {
@@ -138,11 +132,12 @@ impl Message {
             _ => {}
         }
 
+        self.process_embed();
+
         let enc = serde_json::to_string(&self).unwrap();
         ClientboundNotification::Message(self)
-            .publish(channel.id().to_string())
-            .await
-            .unwrap();
+            .publish(channel.id().to_string());
+        
 
         /*
            Web Push Test Code
@@ -162,78 +157,114 @@ impl Message {
             _ => {}
         }
 
-        // Fetch their corresponding sessions.
-        if let Ok(mut cursor) = get_collection("accounts")
-            .find(
-                doc! {
-                    "_id": {
-                        "$in": target_ids
+        async_std::task::spawn(async move {
+            // Fetch their corresponding sessions.
+            if let Ok(mut cursor) = get_collection("accounts")
+                .find(
+                    doc! {
+                        "_id": {
+                            "$in": target_ids
+                        },
+                        "sessions.subscription": {
+                            "$exists": true
+                        }
                     },
-                    "sessions.subscription": {
-                        "$exists": true
-                    }
-                },
-                FindOptions::builder()
-                    .projection(doc! { "sessions": 1 })
-                    .build(),
-            )
-            .await
-        {
-            let mut subscriptions = vec![];
-            while let Some(result) = cursor.next().await {
-                if let Ok(doc) = result {
-                    if let Ok(sessions) = doc.get_array("sessions") {
-                        for session in sessions {
-                            if let Some(doc) = session.as_document() {
-                                if let Ok(sub) = doc.get_document("subscription") {
-                                    let endpoint = sub.get_str("endpoint").unwrap().to_string();
-                                    let p256dh = sub.get_str("p256dh").unwrap().to_string();
-                                    let auth = sub.get_str("auth").unwrap().to_string();
+                    FindOptions::builder()
+                        .projection(doc! { "sessions": 1 })
+                        .build(),
+                )
+                .await
+            {
+                let mut subscriptions = vec![];
+                while let Some(result) = cursor.next().await {
+                    if let Ok(doc) = result {
+                        if let Ok(sessions) = doc.get_array("sessions") {
+                            for session in sessions {
+                                if let Some(doc) = session.as_document() {
+                                    if let Ok(sub) = doc.get_document("subscription") {
+                                        let endpoint = sub.get_str("endpoint").unwrap().to_string();
+                                        let p256dh = sub.get_str("p256dh").unwrap().to_string();
+                                        let auth = sub.get_str("auth").unwrap().to_string();
 
-                                    subscriptions
-                                        .push(SubscriptionInfo::new(endpoint, p256dh, auth));
+                                        subscriptions
+                                            .push(SubscriptionInfo::new(endpoint, p256dh, auth));
+                                    }
                                 }
                             }
                         }
                     }
                 }
-            }
 
-            if subscriptions.len() > 0 {
-                let client = WebPushClient::new();
-                let key =
-                    base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE).unwrap();
+                if subscriptions.len() > 0 {
+                    let client = WebPushClient::new();
+                    let key =
+                        base64::decode_config(VAPID_PRIVATE_KEY.clone(), base64::URL_SAFE).unwrap();
 
-                for subscription in subscriptions {
-                    let mut builder = WebPushMessageBuilder::new(&subscription).unwrap();
-                    let sig_builder =
-                        VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription)
-                            .unwrap();
-                    let signature = sig_builder.build().unwrap();
-                    builder.set_vapid_signature(signature);
-                    builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes());
-                    let m = builder.build().unwrap();
-                    client.send(m).await.ok();
+                    for subscription in subscriptions {
+                        let mut builder = WebPushMessageBuilder::new(&subscription).unwrap();
+                        let sig_builder =
+                            VapidSignatureBuilder::from_pem(std::io::Cursor::new(&key), &subscription)
+                                .unwrap();
+                        let signature = sig_builder.build().unwrap();
+                        builder.set_vapid_signature(signature);
+                        builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes());
+                        let m = builder.build().unwrap();
+                        client.send(m).await.ok();
+                    }
                 }
             }
-        }
+        });
 
         Ok(())
     }
 
-    pub async fn publish_update(&self, data: JsonValue) -> Result<()> {
+    pub async fn publish_update(self, data: JsonValue) -> Result<()> {
         let channel = self.channel.clone();
         ClientboundNotification::MessageUpdate {
             id: self.id.clone(),
             data,
         }
-        .publish(channel)
-        .await
-        .ok();
+        .publish(channel);
+        self.process_embed();
 
         Ok(())
     }
 
+    pub fn process_embed(&self) {
+        if let Content::Text(text) = &self.content {
+            let id = self.id.clone();
+            let content = text.clone();
+            let channel = self.channel.clone();
+            async_std::task::spawn(async move {
+                if let Ok(embeds) = Embed::generate(content).await {
+                    if let Ok(bson) = to_bson(&embeds) {
+                        if let Ok(_) = get_collection("messages")
+                            .update_one(
+                                doc! {
+                                    "_id": &id
+                                },
+                                doc! {
+                                    "$set": {
+                                        "embeds": bson
+                                    }
+                                },
+                                None,
+                            )
+                            .await {
+                            ClientboundNotification::MessageUpdate {
+                                id,
+                                data: json!({
+                                    "embeds": embeds
+                                }),
+                            }
+                            .publish(channel);
+                        }
+                    }
+                }
+            });
+        }
+    }
+
     pub async fn delete(&self) -> Result<()> {
         if let Some(attachment) = &self.attachment {
             attachment.delete().await?;
@@ -256,9 +287,7 @@ impl Message {
         ClientboundNotification::MessageDelete {
             id: self.id.clone(),
         }
-        .publish(channel)
-        .await
-        .ok();
+        .publish(channel);
 
         if let Some(attachment) = &self.attachment {
             get_collection("attachments")
diff --git a/src/database/entities/mod.rs b/src/database/entities/mod.rs
index cdc9a43..90d99cb 100644
--- a/src/database/entities/mod.rs
+++ b/src/database/entities/mod.rs
@@ -1,9 +1,11 @@
+mod guild;
 mod autumn;
+mod january;
 mod channel;
-mod guild;
 mod message;
 mod user;
 
+pub use january::*;
 pub use autumn::*;
 pub use channel::*;
 pub use guild::*;
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 9ff4cf0..9366f16 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -107,9 +107,11 @@ pub enum ClientboundNotification {
 }
 
 impl ClientboundNotification {
-    pub async fn publish(self, topic: String) -> Result<(), String> {
-        prehandle_hook(&self); // ! TODO: this should be moved to pubsub
-        hive_pubsub::backend::mongo::publish(get_hive(), &topic, self).await
+    pub fn publish(self, topic: String) {
+        async_std::task::spawn(async move {
+            prehandle_hook(&self); // ! TODO: this should be moved to pubsub
+            hive_pubsub::backend::mongo::publish(get_hive(), &topic, self).await.ok();
+        });
     }
 }
 
diff --git a/src/notifications/websocket.rs b/src/notifications/websocket.rs
index 9df9b1b..cc0e06c 100644
--- a/src/notifications/websocket.rs
+++ b/src/notifications/websocket.rs
@@ -133,9 +133,7 @@ async fn accept(stream: TcpStream) {
                                                 id: id.clone(),
                                                 online: true,
                                             }
-                                            .publish(id)
-                                            .await
-                                            .ok();
+                                            .publish(id);
                                         }
                                     }
                                     Err(_) => {
@@ -173,9 +171,7 @@ async fn accept(stream: TcpStream) {
                                 id: channel.clone(),
                                 user,
                             }
-                            .publish(channel)
-                            .await
-                            .ok();
+                            .publish(channel);
                         } else {
                             send(ClientboundNotification::Error(
                                 WebSocketError::AlreadyAuthenticated,
@@ -196,9 +192,7 @@ async fn accept(stream: TcpStream) {
                                 id: channel.clone(),
                                 user,
                             }
-                            .publish(channel)
-                            .await
-                            .ok();
+                            .publish(channel);
                         } else {
                             send(ClientboundNotification::Error(
                                 WebSocketError::AlreadyAuthenticated,
@@ -238,9 +232,7 @@ async fn accept(stream: TcpStream) {
             id: id.clone(),
             online: false,
         }
-        .publish(id)
-        .await
-        .ok();
+        .publish(id);
     }
 }
 
diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs
index 28dd54b..9575ae6 100644
--- a/src/routes/channels/delete_channel.rs
+++ b/src/routes/channels/delete_channel.rs
@@ -95,9 +95,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
                 id: id.clone(),
                 user: user.id.clone(),
             }
-            .publish(id.clone())
-            .await
-            .ok();
+            .publish(id.clone());
 
             Message::create(
                 "00000000000000000000000000".to_string(),
diff --git a/src/routes/channels/edit_channel.rs b/src/routes/channels/edit_channel.rs
index 5242ec9..d5f114e 100644
--- a/src/routes/channels/edit_channel.rs
+++ b/src/routes/channels/edit_channel.rs
@@ -101,9 +101,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
                 data: json!(set),
                 clear: data.remove
             }
-            .publish(id.clone())
-            .await
-            .ok();
+            .publish(id.clone());
 
             if let Some(name) = data.name {
                 Message::create(
diff --git a/src/routes/channels/group_add_member.rs b/src/routes/channels/group_add_member.rs
index f0c2670..f95090a 100644
--- a/src/routes/channels/group_add_member.rs
+++ b/src/routes/channels/group_add_member.rs
@@ -52,9 +52,7 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
             id: id.clone(),
             user: member.id.clone(),
         }
-        .publish(id.clone())
-        .await
-        .ok();
+        .publish(id.clone());
 
         Message::create(
             "00000000000000000000000000".to_string(),
diff --git a/src/routes/channels/group_remove_member.rs b/src/routes/channels/group_remove_member.rs
index 63c0f20..f26922f 100644
--- a/src/routes/channels/group_remove_member.rs
+++ b/src/routes/channels/group_remove_member.rs
@@ -49,9 +49,7 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
             id: id.clone(),
             user: member.id.clone(),
         }
-        .publish(id.clone())
-        .await
-        .ok();
+        .publish(id.clone());
 
         Message::create(
             "00000000000000000000000000".to_string(),
diff --git a/src/routes/users/add_friend.rs b/src/routes/users/add_friend.rs
index 4b1441b..ad0a494 100644
--- a/src/routes/users/add_friend.rs
+++ b/src/routes/users/add_friend.rs
@@ -74,21 +74,19 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                         .from_override(&target_user, RelationshipStatus::Friend)
                         .await?;
 
-                    try_join!(
-                        ClientboundNotification::UserRelationship {
-                            id: user.id.clone(),
-                            user: target_user,
-                            status: RelationshipStatus::Friend
-                        }
-                        .publish(user.id.clone()),
-                        ClientboundNotification::UserRelationship {
-                            id: target_id.to_string(),
-                            user,
-                            status: RelationshipStatus::Friend
-                        }
-                        .publish(target_id.to_string())
-                    )
-                    .ok();
+                    ClientboundNotification::UserRelationship {
+                        id: user.id.clone(),
+                        user: target_user,
+                        status: RelationshipStatus::Friend
+                    }
+                    .publish(user.id.clone());
+
+                    ClientboundNotification::UserRelationship {
+                        id: target_id.to_string(),
+                        user,
+                        status: RelationshipStatus::Friend
+                    }
+                    .publish(target_id.to_string());
 
                     Ok(json!({ "status": "Friend" }))
                 }
@@ -136,21 +134,20 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                     let user = user
                         .from_override(&target_user, RelationshipStatus::Incoming)
                         .await?;
-                    try_join!(
-                        ClientboundNotification::UserRelationship {
-                            id: user.id.clone(),
-                            user: target_user,
-                            status: RelationshipStatus::Outgoing
-                        }
-                        .publish(user.id.clone()),
-                        ClientboundNotification::UserRelationship {
-                            id: target_id.to_string(),
-                            user,
-                            status: RelationshipStatus::Incoming
-                        }
-                        .publish(target_id.to_string())
-                    )
-                    .ok();
+                    
+                    ClientboundNotification::UserRelationship {
+                        id: user.id.clone(),
+                        user: target_user,
+                        status: RelationshipStatus::Outgoing
+                    }
+                    .publish(user.id.clone());
+                    
+                    ClientboundNotification::UserRelationship {
+                        id: target_id.to_string(),
+                        user,
+                        status: RelationshipStatus::Incoming
+                    }
+                    .publish(target_id.to_string());
 
                     Ok(json!({ "status": "Outgoing" }))
                 }
diff --git a/src/routes/users/block_user.rs b/src/routes/users/block_user.rs
index 10f69f2..6e06e0e 100644
--- a/src/routes/users/block_user.rs
+++ b/src/routes/users/block_user.rs
@@ -38,9 +38,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 user: target,
                 status: RelationshipStatus::Blocked,
             }
-            .publish(user.id.clone())
-            .await
-            .ok();
+            .publish(user.id.clone());
 
             Ok(json!({ "status": "Blocked" }))
         }
@@ -84,21 +82,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         .await?;
                     let target_id = target.id.clone();
 
-                    try_join!(
-                        ClientboundNotification::UserRelationship {
-                            id: user.id.clone(),
-                            user: target,
-                            status: RelationshipStatus::Blocked
-                        }
-                        .publish(user.id.clone()),
-                        ClientboundNotification::UserRelationship {
-                            id: target_id.clone(),
-                            user,
-                            status: RelationshipStatus::BlockedOther
-                        }
-                        .publish(target_id)
-                    )
-                    .ok();
+                    ClientboundNotification::UserRelationship {
+                        id: user.id.clone(),
+                        user: target,
+                        status: RelationshipStatus::Blocked
+                    }
+                    .publish(user.id.clone());
+
+                    ClientboundNotification::UserRelationship {
+                        id: target_id.clone(),
+                        user,
+                        status: RelationshipStatus::BlockedOther
+                    }
+                    .publish(target_id);
 
                     Ok(json!({ "status": "Blocked" }))
                 }
@@ -146,21 +142,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         .await?;
                     let target_id = target.id.clone();
 
-                    try_join!(
-                        ClientboundNotification::UserRelationship {
-                            id: user.id.clone(),
-                            user: target,
-                            status: RelationshipStatus::Blocked
-                        }
-                        .publish(user.id.clone()),
-                        ClientboundNotification::UserRelationship {
-                            id: target_id.clone(),
-                            user,
-                            status: RelationshipStatus::BlockedOther
-                        }
-                        .publish(target_id)
-                    )
-                    .ok();
+                    ClientboundNotification::UserRelationship {
+                        id: user.id.clone(),
+                        user: target,
+                        status: RelationshipStatus::Blocked
+                    }
+                    .publish(user.id.clone());
+                    
+                    ClientboundNotification::UserRelationship {
+                        id: target_id.clone(),
+                        user,
+                        status: RelationshipStatus::BlockedOther
+                    }
+                    .publish(target_id);
 
                     Ok(json!({ "status": "Blocked" }))
                 }
diff --git a/src/routes/users/change_username.rs b/src/routes/users/change_username.rs
index 1daa11f..461818a 100644
--- a/src/routes/users/change_username.rs
+++ b/src/routes/users/change_username.rs
@@ -60,9 +60,7 @@ pub async fn req(
         data: json!(data.0),
         clear: None
     }
-    .publish(user.id.clone())
-    .await
-    .ok();
+    .publish(user.id.clone());
 
     Ok(())
 }
diff --git a/src/routes/users/edit_user.rs b/src/routes/users/edit_user.rs
index 5ce6b24..2f1197e 100644
--- a/src/routes/users/edit_user.rs
+++ b/src/routes/users/edit_user.rs
@@ -135,9 +135,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
             data: json!({ "status": status }),
             clear: None
         }
-        .publish(user.id.clone())
-        .await
-        .ok();
+        .publish(user.id.clone());
     }
 
     if let Some(avatar) = attachment {
@@ -146,9 +144,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
             data: json!({ "avatar": avatar }),
             clear: None
         }
-        .publish(user.id.clone())
-        .await
-        .ok();
+        .publish(user.id.clone());
     }
 
     if let Some(clear) = data.remove {
@@ -157,9 +153,7 @@ pub async fn req(user: User, data: Json<Data>, _ignore_id: String) -> Result<()>
             data: json!({}),
             clear: Some(clear)
         }
-        .publish(user.id.clone())
-        .await
-        .ok();
+        .publish(user.id.clone());
     }
 
     if remove_avatar {
diff --git a/src/routes/users/remove_friend.rs b/src/routes/users/remove_friend.rs
index ec0176a..5426cf7 100644
--- a/src/routes/users/remove_friend.rs
+++ b/src/routes/users/remove_friend.rs
@@ -53,21 +53,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         .await?;
                     let target_id = target.id.clone();
 
-                    try_join!(
-                        ClientboundNotification::UserRelationship {
-                            id: user.id.clone(),
-                            user: target,
-                            status: RelationshipStatus::None
-                        }
-                        .publish(user.id.clone()),
-                        ClientboundNotification::UserRelationship {
-                            id: target_id.clone(),
-                            user,
-                            status: RelationshipStatus::None
-                        }
-                        .publish(target_id)
-                    )
-                    .ok();
+                    ClientboundNotification::UserRelationship {
+                        id: user.id.clone(),
+                        user: target,
+                        status: RelationshipStatus::None
+                    }
+                    .publish(user.id.clone());
+
+                    ClientboundNotification::UserRelationship {
+                        id: target_id.clone(),
+                        user,
+                        status: RelationshipStatus::None
+                    }
+                    .publish(target_id);
 
                     Ok(json!({ "status": "None" }))
                 }
diff --git a/src/routes/users/unblock_user.rs b/src/routes/users/unblock_user.rs
index 48db2e7..4e2e59c 100644
--- a/src/routes/users/unblock_user.rs
+++ b/src/routes/users/unblock_user.rs
@@ -40,9 +40,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                     user: target,
                     status: RelationshipStatus::BlockedOther,
                 }
-                .publish(user.id.clone())
-                .await
-                .ok();
+                .publish(user.id.clone());
 
                 Ok(json!({ "status": "BlockedOther" }))
             }
@@ -84,21 +82,19 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                             .await?;
                         let target_id = target.id.clone();
 
-                        try_join!(
-                            ClientboundNotification::UserRelationship {
-                                id: user.id.clone(),
-                                user: target,
-                                status: RelationshipStatus::None
-                            }
-                            .publish(user.id.clone()),
-                            ClientboundNotification::UserRelationship {
-                                id: target_id.clone(),
-                                user: user,
-                                status: RelationshipStatus::None
-                            }
-                            .publish(target_id)
-                        )
-                        .ok();
+                        ClientboundNotification::UserRelationship {
+                            id: user.id.clone(),
+                            user: target,
+                            status: RelationshipStatus::None
+                        }
+                        .publish(user.id.clone());
+                        
+                        ClientboundNotification::UserRelationship {
+                            id: target_id.clone(),
+                            user: user,
+                            status: RelationshipStatus::None
+                        }
+                        .publish(target_id);
 
                         Ok(json!({ "status": "None" }))
                     }
diff --git a/src/util/variables.rs b/src/util/variables.rs
index 241d2ab..1bd71e9 100644
--- a/src/util/variables.rs
+++ b/src/util/variables.rs
@@ -18,6 +18,8 @@ lazy_static! {
 
     pub static ref AUTUMN_URL: String =
         env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
+    pub static ref JANUARY_URL: String =
+        env::var("JANUARY_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
     pub static ref VOSO_URL: String =
         env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string());
     pub static ref VOSO_WS_HOST: String =
-- 
GitLab