feat: Chat request implementation #14
7 changed files with 133 additions and 7 deletions
|
@ -16,11 +16,12 @@
|
||||||
|
|
||||||
//! Database extension to work with the balcklist table
|
//! Database extension to work with the balcklist table
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
use oxidetalis_core::types::PublicKey;
|
use oxidetalis_core::types::PublicKey;
|
||||||
use oxidetalis_entities::prelude::*;
|
use oxidetalis_entities::prelude::*;
|
||||||
use sea_orm::DatabaseConnection;
|
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
|
/// Extension trait for the `DatabaseConnection` to work with the blacklist
|
||||||
/// table
|
/// table
|
||||||
|
@ -32,6 +33,12 @@ pub trait BlackListExt {
|
||||||
blacklister: &UserModel,
|
blacklister: &UserModel,
|
||||||
target_public_key: &PublicKey,
|
target_public_key: &PublicKey,
|
||||||
) -> ServerResult<bool>;
|
) -> ServerResult<bool>;
|
||||||
|
/// 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 {
|
impl BlackListExt for DatabaseConnection {
|
||||||
|
@ -49,4 +56,26 @@ impl BlackListExt for DatabaseConnection {
|
||||||
.map(|u| u.is_some())
|
.map(|u| u.is_some())
|
||||||
.map_err(Into::into)
|
.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? {
|
||||||
awiteb marked this conversation as resolved
|
|||||||
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub trait OutChatRequestsExt {
|
||||||
&self,
|
&self,
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> ServerResult<bool>;
|
) -> ServerResult<Option<OutChatRequestsModel>>;
|
||||||
awiteb marked this conversation as resolved
Amjad50
commented
should return should return `true` based on the name and description of the func
Amjad50
commented
small nitpick (also applies to small nitpick (also applies to `have_chat_request_to` below)
I would name it `get_chat_request_to`, as `have` seems that it would be boolean
|
|||||||
|
|
||||||
/// Save the chat request in the requester table
|
/// Save the chat request in the requester table
|
||||||
async fn save_out_chat_request(
|
async fn save_out_chat_request(
|
||||||
|
@ -38,6 +38,13 @@ pub trait OutChatRequestsExt {
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> ServerResult<()>;
|
) -> ServerResult<()>;
|
||||||
|
|
||||||
|
/// Remove the chat request from requester table
|
||||||
|
async fn remove_out_chat_request(
|
||||||
|
&self,
|
||||||
|
requester: &UserModel,
|
||||||
|
recipient: &PublicKey,
|
||||||
|
) -> ServerResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutChatRequestsExt for DatabaseConnection {
|
impl OutChatRequestsExt for DatabaseConnection {
|
||||||
|
@ -46,13 +53,12 @@ impl OutChatRequestsExt for DatabaseConnection {
|
||||||
&self,
|
&self,
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> ServerResult<bool> {
|
) -> ServerResult<Option<OutChatRequestsModel>> {
|
||||||
requester
|
requester
|
||||||
.find_related(OutChatRequestsEntity)
|
.find_related(OutChatRequestsEntity)
|
||||||
.filter(OutChatRequestsColumn::Recipient.eq(recipient.to_string()))
|
.filter(OutChatRequestsColumn::Recipient.eq(recipient.to_string()))
|
||||||
.one(self)
|
.one(self)
|
||||||
.await
|
.await
|
||||||
.map(|user| user.is_some())
|
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +68,11 @@ impl OutChatRequestsExt for DatabaseConnection {
|
||||||
requester: &UserModel,
|
requester: &UserModel,
|
||||||
recipient: &PublicKey,
|
recipient: &PublicKey,
|
||||||
) -> ServerResult<()> {
|
) -> 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());
|
return Err(WsError::AlreadySendChatRequest.into());
|
||||||
}
|
}
|
||||||
OutChatRequestsActiveModel {
|
OutChatRequestsActiveModel {
|
||||||
|
@ -75,4 +85,15 @@ impl OutChatRequestsExt for DatabaseConnection {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,11 @@ ws_errors! {
|
||||||
RegistredUserEvent = "The event is only for registred users",
|
RegistredUserEvent = "The event is only for registred users",
|
||||||
UserNotFound = "The user is not registered in the server",
|
UserNotFound = "The user is not registered in the server",
|
||||||
AlreadyOnTheWhitelist = "The user is already on your whitelist",
|
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",
|
AlreadySendChatRequest = "You have already sent a chat request to this user",
|
||||||
CannotSendChatRequestToSelf = "You cannot send a chat request to yourself",
|
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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@ pub struct ClientEvent {
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ## Important for contuributors
|
||||||
|
// Please make sure to order the event data alphabetically.
|
||||||
|
|
||||||
/// Client websocket event type
|
/// Client websocket event type
|
||||||
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug)]
|
||||||
#[serde(rename_all = "PascalCase", tag = "event", content = "data")]
|
#[serde(rename_all = "PascalCase", tag = "event", content = "data")]
|
||||||
|
@ -39,6 +42,8 @@ pub enum ClientEventType {
|
||||||
Pong { timestamp: u64 },
|
Pong { timestamp: u64 },
|
||||||
/// Request to chat with a user
|
/// Request to chat with a user
|
||||||
ChatRequest { to: PublicKey },
|
ChatRequest { to: PublicKey },
|
||||||
|
/// Response to a chat request
|
||||||
|
ChatRequestResponse { accepted: bool, to: PublicKey },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientEventType {
|
impl ClientEventType {
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub enum ServerEventType {
|
||||||
Message { msg: String },
|
Message { msg: String },
|
||||||
/// New chat request from someone
|
/// New chat request from someone
|
||||||
ChatRequest { from: PublicKey },
|
ChatRequest { from: PublicKey },
|
||||||
|
/// New chat request response from someone
|
||||||
|
ChatRequestResponse { from: PublicKey, accepted: bool },
|
||||||
awiteb marked this conversation as resolved
Amjad50
commented
in the client, you mentioned to make it alphabetical, should it be the same here? in the client, you mentioned to make it alphabetical, should it be the same here?
awiteb
commented
Yes, I forgot it Yes, I forgot it
|
|||||||
/// Error event
|
/// Error event
|
||||||
Error {
|
Error {
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
@ -105,6 +107,11 @@ impl ServerEvent<Unsigned> {
|
||||||
Self::new(ServerEventType::ChatRequest { from: *from })
|
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
|
/// Sign the event
|
||||||
pub fn sign(self, shared_secret: &[u8; 32]) -> ServerEvent<Signed> {
|
pub fn sign(self, shared_secret: &[u8; 32]) -> ServerEvent<Signed> {
|
||||||
ServerEvent::<Signed> {
|
ServerEvent::<Signed> {
|
||||||
|
|
|
@ -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.",
|
"You are unable to send a chat request because you are on the recipient's blacklist.",
|
||||||
awiteb marked this conversation as resolved
Amjad50
commented
Maybe would be better to move this to Maybe would be better to move this to `WsError` easier to manage errors
there are also others below this
awiteb
commented
Right, this should be an error, not a message. I don't know how is this happened Right, this should be an error, not a message. I don't know how is this happened
awiteb
commented
I'll remove I'll remove `Message` event it is actually useless. I don't know why I thought it was good.
awiteb
commented
Well, I remembered when I added it, it was a pain to add a new error (before > I don't know why I thought it was good.
Well, I remembered when I added it, it was a pain to add a new error (before `ws_error` macro) so I thought it was good for simple messages.
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
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.");
|
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.")
|
ServerEvent::message("Chat request sent successfully.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_chat_response(
|
||||||
|
db: &DatabaseConnection,
|
||||||
|
recipient: Option<&UserModel>,
|
||||||
|
sender_public_key: &PublicKey,
|
||||||
|
accepted: bool,
|
||||||
|
) -> ServerEvent<Unsigned> {
|
||||||
|
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.")
|
||||||
|
}
|
||||||
|
|
|
@ -219,6 +219,9 @@ async fn handle_events(
|
||||||
ClientEventType::ChatRequest { to } => {
|
ClientEventType::ChatRequest { to } => {
|
||||||
Some(handlers::handle_chat_request(db, user, to).await)
|
Some(handlers::handle_chat_request(db, user, to).await)
|
||||||
}
|
}
|
||||||
|
ClientEventType::ChatRequestResponse { to, accepted } => {
|
||||||
|
Some(handlers::handle_chat_response(db, user, to, *accepted).await)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue
I noticed that several methods have
logcall
but others don't, is it remaining from debugging session? or planning to keep itIt was for debugging session, but I will use it on more functions
There are a lot of functions that need to be checked, 300+ functions to check if the log is necessary or not, I hate this
Ok, it is just 100 functions