feat: Chat request implementation #14

Manually merged
awiteb merged 55 commits from awiteb/chat-request-and-response into master 2024-07-18 14:21:39 +02:00 AGit
5 changed files with 301 additions and 0 deletions
Showing only changes of commit 90cd947e71 - Show all commits

View file

@ -0,0 +1,52 @@
// OxideTalis Messaging Protocol homeserver implementation
// Copyright (C) 2024 OxideTalis Developers <otmp@4rs.nl>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
//! Database extension to work with the balcklist table
use oxidetalis_core::types::PublicKey;
use oxidetalis_entities::prelude::*;
use sea_orm::DatabaseConnection;
use crate::errors::ApiResult;
/// Extension trait for the `DatabaseConnection` to work with the blacklist
/// table
pub trait BlackListExt {
/// Returns ture if the `blacklister` are blacklisted the
/// `target_public_key`
async fn is_blacklisted(
&self,
blacklister: &UserModel,
awiteb marked this conversation as resolved
Review

ture -> true (use find and replace all, there are multiple of these)
blacklister has blacklisted

`ture` -> `true` (use find and replace all, there are multiple of these) `blacklister has blacklisted`
target_public_key: &PublicKey,
) -> ApiResult<bool>;
}
impl BlackListExt for DatabaseConnection {
#[logcall::logcall]
async fn is_blacklisted(
&self,
blacklister: &UserModel,
target_public_key: &PublicKey,
) -> ApiResult<bool> {
blacklister
.find_related(BlacklistEntity)
.filter(BlacklistColumn::Target.eq(target_public_key.to_string()))
.one(self)
.await
.map(|u| u.is_some())
.map_err(Into::into)
}
}

View file

@ -0,0 +1,66 @@
// OxideTalis Messaging Protocol homeserver implementation
// Copyright (C) 2024 OxideTalis Developers <otmp@4rs.nl>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
//! Database extension for the `in_chat_requests` table.
use chrono::Utc;
use oxidetalis_core::types::PublicKey;
use oxidetalis_entities::prelude::*;
use sea_orm::DatabaseConnection;
use crate::websocket::errors::{WsError, WsResult};
/// Extension trait for the `in_chat_requests` table.
pub trait InChatRequestsExt {
/// Save the chat request in the recipient table
async fn save_in_chat_request(
&self,
requester: &PublicKey,
recipient: &UserModel,
) -> WsResult<()>;
}
impl InChatRequestsExt for DatabaseConnection {
#[logcall::logcall]
async fn save_in_chat_request(
&self,
sender: &PublicKey,
recipient: &UserModel,
) -> WsResult<()> {
if sender.to_string() == recipient.public_key {
return Err(WsError::CannotSendChatRequestToSelf);
}
if recipient
.find_related(InChatRequestsEntity)
.filter(InChatRequestsColumn::Sender.eq(sender.to_string()))
awiteb marked this conversation as resolved
Review

since we are not dealing with the result. maybe a one statement approach to do

InChatRequestsEntity::insert(InChatRequestsActiveModel {
    recipient_id: Set(recipient.id),
    sender: Set(sender.to_string()),
    in_on: Set(Utc::now()),
    ..Default::default()
})
.on_conflict(
    OnConflict::columns([
        InChatRequestsColumn::RecipientId,
        InChatRequestsColumn::Sender,
    ])
    .update_column(InChatRequestsColumn::InOn)
    .to_owned(),
)
.exec(self)

here i'm updating InOn if needed, but can also be changed to do_nothing()

since we are not dealing with the result. maybe a one statement approach to do ```rust InChatRequestsEntity::insert(InChatRequestsActiveModel { recipient_id: Set(recipient.id), sender: Set(sender.to_string()), in_on: Set(Utc::now()), ..Default::default() }) .on_conflict( OnConflict::columns([ InChatRequestsColumn::RecipientId, InChatRequestsColumn::Sender, ]) .update_column(InChatRequestsColumn::InOn) .to_owned(), ) .exec(self) ``` here i'm updating `InOn` if needed, but can also be changed to `do_nothing()`
.one(self)
.await
.map_err(|_| WsError::InternalServerError)?
.is_none()
{
InChatRequestsActiveModel {
recipient_id: Set(recipient.id),
sender: Set(sender.to_string()),
in_on: Set(Utc::now()),
..Default::default()
}
.save(self)
.await
.map_err(|_| WsError::InternalServerError)?;
}
Ok(())
}
}

View file

@ -16,6 +16,14 @@
//! Database utilities for the OxideTalis homeserver. //! Database utilities for the OxideTalis homeserver.
mod blacklist;
mod in_chat_requests;
mod out_chat_requests;
mod user; mod user;
mod whitelist;
pub use blacklist::*;
pub use in_chat_requests::*;
pub use out_chat_requests::*;
pub use user::*; pub use user::*;
pub use whitelist::*;

View file

@ -0,0 +1,86 @@
// OxideTalis Messaging Protocol homeserver implementation
// Copyright (C) 2024 OxideTalis Developers <otmp@4rs.nl>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
//! Database extension for the `out_chat_requests` table.
use chrono::Utc;
use oxidetalis_core::types::PublicKey;
use oxidetalis_entities::prelude::*;
use sea_orm::DatabaseConnection;
use crate::{
errors::ApiResult,
websocket::errors::{WsError, WsResult},
};
/// Extension trait for the `out_chat_requests` table.
pub trait OutChatRequestsExt {
/// Returns true if the `user` have a sended chat request to the `recipient`
async fn have_chat_request_to(
&self,
awiteb marked this conversation as resolved
Review

should return true based on the name and description of the func

should return `true` based on the name and description of the func
Review

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

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
requester: &UserModel,
recipient: &PublicKey,
) -> ApiResult<bool>;
/// Save the chat request in the requester table
async fn save_out_chat_request(
&self,
requester: &UserModel,
recipient: &PublicKey,
) -> WsResult<()>;
}
impl OutChatRequestsExt for DatabaseConnection {
#[logcall::logcall]
async fn have_chat_request_to(
&self,
requester: &UserModel,
recipient: &PublicKey,
) -> ApiResult<bool> {
requester
.find_related(OutChatRequestsEntity)
.filter(OutChatRequestsColumn::Recipient.eq(recipient.to_string()))
.one(self)
.await
.map(|user| user.is_some())
.map_err(Into::into)
}
#[logcall::logcall]
async fn save_out_chat_request(
&self,
requester: &UserModel,
recipient: &PublicKey,
) -> WsResult<()> {
if self
.have_chat_request_to(requester, recipient)
.await
.map_err(|_| WsError::InternalServerError)?
{
return Err(WsError::AlreadySendChatRequest);
}
OutChatRequestsActiveModel {
sender_id: Set(requester.id),
recipient: Set(recipient.to_string()),
out_on: Set(Utc::now()),
..Default::default()
}
.save(self)
.await
.map_err(|_| WsError::InternalServerError)?;
Ok(())
}
}

View file

@ -0,0 +1,89 @@
// OxideTalis Messaging Protocol homeserver implementation
// Copyright (C) 2024 OxideTalis Developers <otmp@4rs.nl>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://gnu.org/licenses/agpl-3.0>.
//! 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::ApiResult,
websocket::errors::{WsError, WsResult},
};
/// Extension trait for the `DatabaseConnection` to work with the whitelist
/// table
pub trait WhiteListExt {
/// Returns ture if the `whitelister` are whitelisted the
awiteb marked this conversation as resolved
Review

ture
The whitelister has whitelisted

ture The whitelister has whitelisted
/// `target_public_key`
async fn is_whitelisted(
&self,
whitelister: &UserModel,
target_public_key: &PublicKey,
) -> ApiResult<bool>;
/// Add the `target_public_key` to the whitelist of the `whitelister`
async fn add_to_whitelist(
&self,
whitelister: &UserModel,
target_public_key: &PublicKey,
) -> WsResult<()>;
}
impl WhiteListExt for DatabaseConnection {
async fn is_whitelisted(
&self,
whitelister: &UserModel,
target_public_key: &PublicKey,
) -> ApiResult<bool> {
whitelister
.find_related(WhitelistEntity)
.filter(WhitelistColumn::Target.eq(target_public_key.to_string()))
.one(self)
.await
.map(|u| u.is_some())
.map_err(Into::into)
}
async fn add_to_whitelist(
&self,
whitelister: &UserModel,
target_public_key: &PublicKey,
) -> WsResult<()> {
if self
.is_whitelisted(whitelister, target_public_key)
.await
.map_err(|_| WsError::InternalServerError)?
{
return Err(WsError::AlreadyOnTheWhitelist);
}
if whitelister.public_key == target_public_key.to_string() {
return Err(WsError::CannotAddSelfToWhitelist);
}
WhitelistActiveModel {
user_id: Set(whitelister.id),
target: Set(target_public_key.to_string()),
whitelisted_at: Set(Utc::now()),
..Default::default()
}
.save(self)
.await
.map_err(|_| WsError::InternalServerError)?;
Ok(())
}
}