From 24e038f482c97e7ad75de2f496c14626df6420da Mon Sep 17 00:00:00 2001 From: Awiteb Date: Sun, 14 Jul 2024 15:04:33 +0300 Subject: [PATCH] feat: ChatRequestResponse event Signed-off-by: Awiteb --- crates/oxidetalis/src/database/blacklist.rs | 31 +++++++++- .../src/database/out_chat_requests.rs | 29 +++++++-- crates/oxidetalis/src/websocket/errors.rs | 6 +- .../oxidetalis/src/websocket/events/client.rs | 5 ++ .../oxidetalis/src/websocket/events/server.rs | 7 +++ .../src/websocket/handlers/chat_request.rs | 59 ++++++++++++++++++- crates/oxidetalis/src/websocket/mod.rs | 3 + 7 files changed, 133 insertions(+), 7 deletions(-) diff --git a/crates/oxidetalis/src/database/blacklist.rs b/crates/oxidetalis/src/database/blacklist.rs index 8448843..9a4a712 100644 --- a/crates/oxidetalis/src/database/blacklist.rs +++ b/crates/oxidetalis/src/database/blacklist.rs @@ -16,11 +16,12 @@ //! Database extension to work with the balcklist table +use chrono::Utc; use oxidetalis_core::types::PublicKey; use oxidetalis_entities::prelude::*; use sea_orm::DatabaseConnection; -use crate::errors::ServerResult; +use crate::{errors::ServerResult, websocket::errors::WsError}; /// Extension trait for the `DatabaseConnection` to work with the blacklist /// table @@ -32,6 +33,12 @@ pub trait BlackListExt { blacklister: &UserModel, target_public_key: &PublicKey, ) -> ServerResult; + /// Add the `target_public_key` to the blacklist of the `blacklister` + async fn add_to_blacklist( + &self, + blacklister: &UserModel, + target_public_key: &PublicKey, + ) -> ServerResult<()>; } impl BlackListExt for DatabaseConnection { @@ -49,4 +56,26 @@ impl BlackListExt for DatabaseConnection { .map(|u| u.is_some()) .map_err(Into::into) } + + async fn add_to_blacklist( + &self, + blacklister: &UserModel, + target_public_key: &PublicKey, + ) -> ServerResult<()> { + if self.is_blacklisted(blacklister, target_public_key).await? { + return Err(WsError::AlreadyOnTheblacklist.into()); + } + if blacklister.public_key == target_public_key.to_string() { + return Err(WsError::CannotAddSelfToBlacklist.into()); + } + BlacklistActiveModel { + user_id: Set(blacklister.id), + target: Set(target_public_key.to_string()), + blacklisted_at: Set(Utc::now()), + ..Default::default() + } + .save(self) + .await?; + Ok(()) + } } diff --git a/crates/oxidetalis/src/database/out_chat_requests.rs b/crates/oxidetalis/src/database/out_chat_requests.rs index b51274d..0994e4b 100644 --- a/crates/oxidetalis/src/database/out_chat_requests.rs +++ b/crates/oxidetalis/src/database/out_chat_requests.rs @@ -30,7 +30,7 @@ pub trait OutChatRequestsExt { &self, requester: &UserModel, recipient: &PublicKey, - ) -> ServerResult; + ) -> ServerResult>; /// Save the chat request in the requester table async fn save_out_chat_request( @@ -38,6 +38,13 @@ pub trait OutChatRequestsExt { requester: &UserModel, recipient: &PublicKey, ) -> ServerResult<()>; + + /// Remove the chat request from requester table + async fn remove_out_chat_request( + &self, + requester: &UserModel, + recipient: &PublicKey, + ) -> ServerResult<()>; } impl OutChatRequestsExt for DatabaseConnection { @@ -46,13 +53,12 @@ impl OutChatRequestsExt for DatabaseConnection { &self, requester: &UserModel, recipient: &PublicKey, - ) -> ServerResult { + ) -> ServerResult> { requester .find_related(OutChatRequestsEntity) .filter(OutChatRequestsColumn::Recipient.eq(recipient.to_string())) .one(self) .await - .map(|user| user.is_some()) .map_err(Into::into) } @@ -62,7 +68,11 @@ impl OutChatRequestsExt for DatabaseConnection { requester: &UserModel, recipient: &PublicKey, ) -> ServerResult<()> { - if self.have_chat_request_to(requester, recipient).await? { + if self + .have_chat_request_to(requester, recipient) + .await? + .is_some() + { return Err(WsError::AlreadySendChatRequest.into()); } OutChatRequestsActiveModel { @@ -75,4 +85,15 @@ impl OutChatRequestsExt for DatabaseConnection { .await?; Ok(()) } + + async fn remove_out_chat_request( + &self, + requester: &UserModel, + recipient: &PublicKey, + ) -> ServerResult<()> { + if let Some(out_model) = self.have_chat_request_to(requester, recipient).await? { + out_model.delete(self).await?; + } + Ok(()) + } } diff --git a/crates/oxidetalis/src/websocket/errors.rs b/crates/oxidetalis/src/websocket/errors.rs index ec39321..d9ce4c7 100644 --- a/crates/oxidetalis/src/websocket/errors.rs +++ b/crates/oxidetalis/src/websocket/errors.rs @@ -30,7 +30,11 @@ ws_errors! { RegistredUserEvent = "The event is only for registred users", UserNotFound = "The user is not registered in the server", AlreadyOnTheWhitelist = "The user is already on your whitelist", + CannotAddSelfToWhitelist = "You cannot add yourself to the whitelist", + AlreadyOnTheblacklist = "The user is already on your blacklist", + CannotAddSelfToBlacklist = "You cannot add yourself to the blacklist", AlreadySendChatRequest = "You have already sent a chat request to this user", CannotSendChatRequestToSelf = "You cannot send a chat request to yourself", - CannotAddSelfToWhitelist = "You cannot add yourself to the whitelist", + CannotRespondToOwnChatRequest = "You cannot respond to your own chat request", + NoChatRequestFromRecipient = "You do not have a chat request from the recipient", } diff --git a/crates/oxidetalis/src/websocket/events/client.rs b/crates/oxidetalis/src/websocket/events/client.rs index 23b8586..524c3e1 100644 --- a/crates/oxidetalis/src/websocket/events/client.rs +++ b/crates/oxidetalis/src/websocket/events/client.rs @@ -29,6 +29,9 @@ pub struct ClientEvent { signature: Signature, } +// ## Important for contuributors +// Please make sure to order the event data alphabetically. + /// Client websocket event type #[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)] #[serde(rename_all = "PascalCase", tag = "event", content = "data")] @@ -39,6 +42,8 @@ pub enum ClientEventType { Pong { timestamp: u64 }, /// Request to chat with a user ChatRequest { to: PublicKey }, + /// Response to a chat request + ChatRequestResponse { accepted: bool, to: PublicKey }, } impl ClientEventType { diff --git a/crates/oxidetalis/src/websocket/events/server.rs b/crates/oxidetalis/src/websocket/events/server.rs index 84e7e15..9824931 100644 --- a/crates/oxidetalis/src/websocket/events/server.rs +++ b/crates/oxidetalis/src/websocket/events/server.rs @@ -55,6 +55,8 @@ pub enum ServerEventType { Message { msg: String }, /// New chat request from someone ChatRequest { from: PublicKey }, + /// New chat request response from someone + ChatRequestResponse { from: PublicKey, accepted: bool }, /// Error event Error { name: &'static str, @@ -105,6 +107,11 @@ impl ServerEvent { Self::new(ServerEventType::ChatRequest { from: *from }) } + /// Create chat request response event + pub fn chat_request_response(from: PublicKey, accepted: bool) -> Self { + Self::new(ServerEventType::ChatRequestResponse { from, accepted }) + } + /// Sign the event pub fn sign(self, shared_secret: &[u8; 32]) -> ServerEvent { ServerEvent:: { diff --git a/crates/oxidetalis/src/websocket/handlers/chat_request.rs b/crates/oxidetalis/src/websocket/handlers/chat_request.rs index 0f43230..9519347 100644 --- a/crates/oxidetalis/src/websocket/handlers/chat_request.rs +++ b/crates/oxidetalis/src/websocket/handlers/chat_request.rs @@ -53,7 +53,7 @@ pub async fn handle_chat_request( "You are unable to send a chat request because you are on the recipient's blacklist.", ); } - if try_ws!(db.have_chat_request_to(from_user, to_public_key).await) { + if try_ws!(db.have_chat_request_to(from_user, to_public_key).await).is_some() { return ServerEvent::message("You have already sent a chat request to this user."); } @@ -75,3 +75,60 @@ pub async fn handle_chat_request( } ServerEvent::message("Chat request sent successfully.") } + +pub async fn handle_chat_response( + db: &DatabaseConnection, + recipient: Option<&UserModel>, + sender_public_key: &PublicKey, + accepted: bool, +) -> ServerEvent { + let Some(recipient_user) = recipient else { + return WsError::RegistredUserEvent.into(); + }; + let Some(sender_user) = try_ws!(db.get_user_by_pubk(sender_public_key).await) else { + return WsError::UserNotFound.into(); + }; + if recipient_user.id == sender_user.id { + return WsError::CannotRespondToOwnChatRequest.into(); + } + + // FIXME: When change the entity public key to a PublicKey type, change this + let recipient_public_key = + PublicKey::from_str(&recipient_user.public_key).expect("Is valid public key"); + + if try_ws!( + db.have_chat_request_to(&sender_user, &recipient_public_key) + .await + ) + .is_none() + { + return WsError::NoChatRequestFromRecipient.into(); + } + + if let Some(conn_id) = ONLINE_USERS.is_online(sender_public_key).await { + ONLINE_USERS + .send( + &conn_id, + ServerEvent::chat_request_response(recipient_public_key, accepted), + ) + .await; + } else { + // TODO: Create a table for chat request responses, and send them when + // the user logs in + } + + // We don't need to handle the case where the sender is blacklisted or + // whitelisted already, just add it if it is not already there + let _ = if accepted { + db.add_to_whitelist(recipient_user, sender_public_key).await + } else { + db.add_to_blacklist(recipient_user, sender_public_key).await + }; + + try_ws!( + db.remove_out_chat_request(&sender_user, &recipient_public_key) + .await + ); + + ServerEvent::message("Chat request response sent successfully.") +} diff --git a/crates/oxidetalis/src/websocket/mod.rs b/crates/oxidetalis/src/websocket/mod.rs index 8607236..8702700 100644 --- a/crates/oxidetalis/src/websocket/mod.rs +++ b/crates/oxidetalis/src/websocket/mod.rs @@ -219,6 +219,9 @@ async fn handle_events( ClientEventType::ChatRequest { to } => { Some(handlers::handle_chat_request(db, user, to).await) } + ClientEventType::ChatRequestResponse { to, accepted } => { + Some(handlers::handle_chat_response(db, user, to, *accepted).await) + } } }