use std::collections::BTreeSet;
use anyhow::bail;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
use serde::{Deserialize, Serialize};
use crate::{SignerDaemon, entity::message, model::viewobject::MessageContent};
use super::MessageVO;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ChatVO {
Private { peers: Vec<String> },
}
impl ChatVO {
pub fn chat_variant(&self) -> String {
match self {
ChatVO::Private { .. } => format!("Private"),
}
}
pub async fn chat_key(
&self,
daemon: &SignerDaemon,
) -> anyhow::Result<String> {
match self {
ChatVO::Private { peers } => {
let self_key = daemon.user.lock().await.public.pub_key.clone();
if peers.len() == 0 || peers.len() > 2 {
anyhow::bail!(
"private chat peers length must less than 2 and greater than 0"
);
}
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,
daemon: &SignerDaemon,
) -> anyhow::Result<u64> {
let chat_variant = self.chat_variant();
let chat_key = self.chat_key(daemon).await?;
let user_key = daemon.user.lock().await.public.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(&daemon.db)
.await?;
let checked = serde_json::to_string(&MessageContent::Check)?;
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(&daemon.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,
daemon: &SignerDaemon,
) -> anyhow::Result<Option<MessageVO>> {
let chat_variant = self.chat_variant();
let chat_key = self.chat_key(daemon).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.ty())),
)
.order_by_desc(message::Column::CreateTime)
.one(&daemon.db)
.await?
}
};
let message_vo = match message {
None => None,
Some(message) => Some(MessageVO::from_model(&daemon.db, &message).await?),
};
Ok(message_vo)
}
pub async fn destinations(
&self,
daemon: &SignerDaemon,
) -> anyhow::Result<Vec<String>> {
match self {
ChatVO::Private { .. } => {
let chat_key = self.chat_key(daemon).await?;
let user_vo = daemon.get_user(&chat_key).await?;
let user_vo = match user_vo {
None => bail!("user not found"),
Some(user_vo) => user_vo,
};
let public = user_vo.public()?;
Ok(public.allow_endpoints)
}
}
}
pub async fn receiver_keys(
&self,
daemon: &SignerDaemon,
) -> anyhow::Result<Vec<String>> {
let receiver_keys = match self {
ChatVO::Private { .. } => {
let chat_key = self.chat_key(daemon).await?;
vec![chat_key]
}
};
Ok(receiver_keys)
}
}
#[cfg(test)]
mod test {
use sea_orm::EntityTrait;
use signer_core::SignerUser;
use crate::{
SignerDaemon, SignerDaemonEvent,
entity::message,
model::viewobject::{ChatVO, Envelope, MessageContent, MessageVO},
};
#[tokio::test]
async fn test_uncheck_message_count() -> anyhow::Result<()> {
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?;
alice_daemon
.ping_server("http://localhost:8080/api/signer")
.await?;
bob_daemon
.ping_server("http://localhost:8080/api/signer")
.await?;
let message = MessageContent::Text(format!("Hello Bob!"));
let message_id = uuid::Uuid::new_v4().to_string();
let message = MessageVO {
chat: ChatVO::Private {
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,
&message,
vec!["http://localhost:8080/api/signer".to_string()],
)
.await?;
envelope.send(&alice).await?;
bob_daemon
.remote_pull_message("http://localhost:8080/api/signer")
.await?;
let uncheck = message.chat.uncheck_message_count(&bob_daemon).await?;
assert_eq!(uncheck, 1);
let mut rx = bob_daemon.event_receiver.subscribe();
let check = message.create_check_message(&bob_daemon).await?;
bob_daemon.put_message(check).await?;
loop {
let ev = rx.recv().await?;
match ev {
SignerDaemonEvent::ApplyEvent => break,
_ => {}
}
}
let bob_messages = message::Entity::find().all(&bob_daemon.db).await?;
assert_eq!(bob_messages.len(), 2);
let uncheck = message.chat.uncheck_message_count(&bob_daemon).await?;
assert_eq!(uncheck, 0);
Ok(())
}
}