use std::collections::BTreeSet;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
use serde::{Deserialize, Serialize};
use crate::{
SignerDaemon, SignerDaemonCore,
entity::message,
model::viewobject::{MessageContent, Null},
};
use super::MessageVO;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "poem-openapi", derive(poem_openapi::Union))]
pub enum ChatVO {
Private(PrivateChatVO),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "poem-openapi", derive(poem_openapi::Object))]
pub struct PrivateChatVO {
pub peers: Vec<String>,
}
impl ChatVO {
pub fn chat_variant(&self) -> String {
match self {
ChatVO::Private(_) => format!("Private"),
}
}
pub async fn chat_key(&self, core: &SignerDaemonCore) -> crate::DaemonResult<String> {
match self {
ChatVO::Private(private) => {
let self_key = &core.pub_key;
if private.peers.len() == 0 || private.peers.len() > 2 {
return Err(crate::DaemonError::Signer(crate::SignerError::Msg(
"private chat peers length must less than 2 and greater than 0".to_string(),
)));
}
let peers = &private.peers;
if peers.len() == 1 {
return Ok(peers[0].clone());
}
if *self_key == peers[0] {
Ok(peers[1].clone())
} else {
Ok(peers[0].clone())
}
}
}
}
pub async fn uncheck_message_count(&self, core: &SignerDaemonCore) -> crate::DaemonResult<u64> {
let chat_variant = self.chat_variant();
let chat_key = self.chat_key(core).await?;
let user_key = core.pub_key.clone();
let id_vec: Vec<String> = message::Entity::find()
.select_only()
.column(message::Column::Id)
.filter(
message::Column::ChatVariant
.eq(&chat_variant)
.and(message::Column::ChatKey.eq(&chat_key))
.and(message::Column::ReceiverKeys.contains(user_key.clone())),
)
.into_tuple()
.all(&core.db)
.await?;
let checked = serde_json::to_string(&MessageContent::Check(Null {}))?;
let checked_id_vec: Vec<String> = message::Entity::find()
.select_only()
.column(message::Column::ParentId)
.filter(
message::Column::ChatVariant
.eq(&chat_variant)
.and(message::Column::ChatKey.eq(&chat_key))
.and(message::Column::Content.eq(&checked))
.and(message::Column::UserKey.eq(&user_key))
.and(message::Column::ParentId.is_in(id_vec.clone())),
)
.into_tuple()
.all(&core.db)
.await?;
let mut uncheck_set = BTreeSet::from_iter(id_vec);
for checked_id in checked_id_vec {
uncheck_set.remove(&checked_id);
}
Ok(uncheck_set.len() as u64)
}
pub async fn latest_message(
&self,
core: &SignerDaemonCore,
) -> crate::DaemonResult<Option<MessageVO>> {
let chat_variant = self.chat_variant();
let chat_key = self.chat_key(core).await?;
let message = match self {
ChatVO::Private { .. } => {
message::Entity::find()
.filter(
message::Column::ChatVariant
.eq(chat_variant)
.and(message::Column::ChatKey.eq(chat_key))
.and(
message::Column::ContentType
.ne(MessageContent::Check(Null {}).ty()),
),
)
.order_by_desc(message::Column::CreateTime)
.one(&core.db)
.await?
}
};
let message_vo = match message {
None => None,
Some(message) => Some(MessageVO::from_model(&core.db, &message).await?),
};
Ok(message_vo)
}
pub async fn destinations(&self, daemon: &SignerDaemon) -> crate::DaemonResult<Vec<String>> {
match self {
ChatVO::Private { .. } => {
let chat_key = self.chat_key(&daemon.core).await?;
let user_vo = daemon.store.user.get(&chat_key).await?;
let user_vo = match user_vo {
None => {
return Err(crate::DaemonError::Signer(crate::SignerError::Msg(
"user not found".to_string(),
)));
}
Some(user_vo) => user_vo,
};
let public = user_vo.public()?;
Ok(public.allow_endpoints)
}
}
}
pub async fn receiver_keys(&self, core: &SignerDaemonCore) -> crate::DaemonResult<Vec<String>> {
let receiver_keys = match self {
ChatVO::Private { .. } => {
let chat_key = self.chat_key(core).await?;
vec![chat_key]
}
};
Ok(receiver_keys)
}
}
#[cfg(test)]
mod test {
use sea_orm::EntityTrait;
use signer_core::SignerUser;
use crate::{
SignerDaemon,
entity::message,
model::viewobject::{ChatVO, Envelope, MessageContent, MessageVO, PrivateChatVO},
signer_remote::SignerRemote,
};
#[tokio::test]
async fn test_uncheck_message_count() -> crate::DaemonResult<()> {
let alice = SignerUser::generete("alice")?;
let alice_daemon = SignerDaemon::from_memory(&alice, "test-uncheck").await?;
let bob = SignerUser::generete("bob")?;
let bob_daemon = SignerDaemon::from_memory(&bob, "test-uncheck").await?;
let remote = SignerRemote::new("http://localhost:8080");
remote.ping(&alice).await?;
remote.ping(&bob).await?;
let message = MessageContent::Text(format!("Hello Bob!"));
let message_id = uuid::Uuid::new_v4().to_string();
let message = MessageVO {
chat: ChatVO::Private(PrivateChatVO {
peers: vec![bob.public.pub_key.clone(), alice.public.pub_key.clone()],
}),
id: message_id.clone(),
parent_id: None,
parent_user_key: None,
user_key: alice.public.pub_key.clone(),
create_time: chrono::Utc::now().timestamp_millis(),
receiver_keys: vec![bob.public.pub_key.clone()],
content: message,
};
let envelope = Envelope::create(
&*alice_daemon.read().await,
&message,
vec!["http://localhost:8080".to_string()],
)
.await?;
envelope.send(&alice).await?;
remote.pull_message(&mut *bob_daemon.write().await).await?;
let uncheck = message
.chat
.uncheck_message_count(&bob_daemon.read().await.core)
.await?;
assert_eq!(uncheck, 1);
let check = message
.create_check_message(&*bob_daemon.read().await)
.await?;
bob_daemon.write().await.store.message.put(check).await?;
let bob_messages = message::Entity::find()
.all(&bob_daemon.read().await.core.db)
.await?;
assert_eq!(bob_messages.len(), 2);
let uncheck = message
.chat
.uncheck_message_count(&bob_daemon.read().await.core)
.await?;
assert_eq!(uncheck, 0);
Ok(())
}
}