mirror of https://github.com/CympleTech/ESSE.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
9.5 KiB
293 lines
9.5 KiB
use std::path::PathBuf; |
|
use std::time::{SystemTime, UNIX_EPOCH}; |
|
use tdn::types::{ |
|
group::{EventId, GroupId}, |
|
primitive::{PeerId, Result}, |
|
rpc::{json, RpcParam}, |
|
}; |
|
use tdn_storage::local::{DStorage, DsValue}; |
|
|
|
use chat_types::{MessageType, NetworkMessage}; |
|
|
|
use crate::storage::{ |
|
read_avatar_sync, read_file_sync, read_image_sync, read_record_sync, write_avatar_sync, |
|
write_file_sync, write_image_sync, write_record_sync, |
|
}; |
|
|
|
pub(crate) fn handle_nmsg( |
|
nmsg: NetworkMessage, |
|
is_me: bool, |
|
gid: GroupId, |
|
base: &PathBuf, |
|
db: &DStorage, |
|
fid: i64, |
|
hash: EventId, |
|
) -> Result<(Message, String)> { |
|
// handle event. |
|
let (m_type, raw) = match nmsg { |
|
NetworkMessage::String(content) => (MessageType::String, content), |
|
NetworkMessage::Image(bytes) => { |
|
let image_name = write_image_sync(base, &gid, bytes)?; |
|
(MessageType::Image, image_name) |
|
} |
|
NetworkMessage::File(old_name, bytes) => { |
|
let filename = write_file_sync(base, &gid, &old_name, bytes)?; |
|
(MessageType::File, filename) |
|
} |
|
NetworkMessage::Contact(name, rgid, addr, avatar_bytes) => { |
|
write_avatar_sync(base, &gid, &rgid, avatar_bytes)?; |
|
let tmp_name = name.replace(";", "-;"); |
|
let contact_values = format!("{};;{};;{}", tmp_name, rgid.to_hex(), addr.to_hex()); |
|
(MessageType::Contact, contact_values) |
|
} |
|
NetworkMessage::Emoji => { |
|
// TODO |
|
(MessageType::Emoji, "".to_owned()) |
|
} |
|
NetworkMessage::Record(bytes, time) => { |
|
let record_name = write_record_sync(base, &gid, fid, time, bytes)?; |
|
(MessageType::Record, record_name) |
|
} |
|
NetworkMessage::Phone => { |
|
// TODO |
|
(MessageType::Phone, "".to_owned()) |
|
} |
|
NetworkMessage::Video => { |
|
// TODO |
|
(MessageType::Video, "".to_owned()) |
|
} |
|
NetworkMessage::Invite(content) => (MessageType::Invite, content), |
|
}; |
|
|
|
let scontent = match m_type { |
|
MessageType::String => { |
|
format!("{}:{}", m_type.to_int(), raw) |
|
} |
|
_ => format!("{}:", m_type.to_int()), |
|
}; |
|
|
|
let mut msg = Message::new_with_id(hash, fid, is_me, m_type, raw, true); |
|
msg.insert(db)?; |
|
|
|
Ok((msg, scontent)) |
|
} |
|
|
|
pub fn from_model(base: &PathBuf, gid: &GroupId, model: Message) -> Result<NetworkMessage> { |
|
// handle message's type. |
|
match model.m_type { |
|
MessageType::String => Ok(NetworkMessage::String(model.content)), |
|
MessageType::Image => { |
|
let bytes = read_image_sync(base, gid, &model.content)?; |
|
Ok(NetworkMessage::Image(bytes)) |
|
} |
|
MessageType::File => { |
|
let bytes = read_file_sync(base, gid, &model.content)?; |
|
Ok(NetworkMessage::File(model.content, bytes)) |
|
} |
|
MessageType::Contact => { |
|
let v: Vec<&str> = model.content.split(";;").collect(); |
|
if v.len() != 3 { |
|
return Err(anyhow!("message is invalid")); |
|
} |
|
let cname = v[0].to_owned(); |
|
let cgid = GroupId::from_hex(v[1])?; |
|
let caddr = PeerId::from_hex(v[2])?; |
|
let avatar_bytes = read_avatar_sync(base, gid, &cgid)?; |
|
Ok(NetworkMessage::Contact(cname, cgid, caddr, avatar_bytes)) |
|
} |
|
MessageType::Record => { |
|
let (bytes, time) = if let Some(i) = model.content.find('-') { |
|
let time = model.content[0..i].parse().unwrap_or(0); |
|
let bytes = read_record_sync(base, gid, &model.content[i + 1..])?; |
|
(bytes, time) |
|
} else { |
|
(vec![], 0) |
|
}; |
|
Ok(NetworkMessage::Record(bytes, time)) |
|
} |
|
MessageType::Invite => Ok(NetworkMessage::Invite(model.content)), |
|
MessageType::Emoji => Ok(NetworkMessage::Emoji), |
|
MessageType::Phone => Ok(NetworkMessage::Phone), |
|
MessageType::Video => Ok(NetworkMessage::Video), |
|
} |
|
} |
|
|
|
pub(crate) struct Message { |
|
pub id: i64, |
|
pub hash: EventId, |
|
pub fid: i64, |
|
pub is_me: bool, |
|
pub m_type: MessageType, |
|
pub content: String, |
|
pub is_delivery: bool, |
|
pub datetime: i64, |
|
pub is_deleted: bool, |
|
} |
|
|
|
impl Message { |
|
pub fn new( |
|
gid: &GroupId, |
|
fid: i64, |
|
is_me: bool, |
|
m_type: MessageType, |
|
content: String, |
|
is_delivery: bool, |
|
) -> Message { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
let mut bytes = [0u8; 32]; |
|
bytes[0..8].copy_from_slice(&gid.0[0..8]); |
|
bytes[8..16].copy_from_slice(&(fid as u64).to_le_bytes()); // 8-bytes. |
|
bytes[16..24].copy_from_slice(&(datetime as u64).to_le_bytes()); // 8-bytes. |
|
let content_bytes = content.as_bytes(); |
|
if content_bytes.len() >= 8 { |
|
bytes[24..32].copy_from_slice(&content_bytes[0..8]); |
|
} else { |
|
bytes[24..(24 + content_bytes.len())].copy_from_slice(&content_bytes); |
|
} |
|
|
|
Message { |
|
id: 0, |
|
hash: EventId(bytes), |
|
is_deleted: false, |
|
fid, |
|
is_me, |
|
m_type, |
|
content, |
|
is_delivery, |
|
datetime, |
|
} |
|
} |
|
|
|
pub fn new_with_id( |
|
hash: EventId, |
|
fid: i64, |
|
is_me: bool, |
|
m_type: MessageType, |
|
content: String, |
|
is_delivery: bool, |
|
) -> Message { |
|
let start = SystemTime::now(); |
|
let datetime = start |
|
.duration_since(UNIX_EPOCH) |
|
.map(|s| s.as_secs()) |
|
.unwrap_or(0) as i64; // safe for all life. |
|
|
|
Message { |
|
id: 0, |
|
is_deleted: false, |
|
hash, |
|
fid, |
|
is_me, |
|
m_type, |
|
content, |
|
is_delivery, |
|
datetime, |
|
} |
|
} |
|
|
|
/// here is zero-copy and unwrap is safe. checked. |
|
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Message { |
|
let is_deleted = if contains_deleted { |
|
v.pop().unwrap().as_bool() |
|
} else { |
|
false |
|
}; |
|
|
|
Message { |
|
is_deleted, |
|
datetime: v.pop().unwrap().as_i64(), |
|
is_delivery: v.pop().unwrap().as_bool(), |
|
content: v.pop().unwrap().as_string(), |
|
m_type: MessageType::from_int(v.pop().unwrap().as_i64()), |
|
is_me: v.pop().unwrap().as_bool(), |
|
fid: v.pop().unwrap().as_i64(), |
|
hash: EventId::from_hex(v.pop().unwrap().as_str()).unwrap_or(EventId::default()), |
|
id: v.pop().unwrap().as_i64(), |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.hash.to_hex(), |
|
self.fid, |
|
self.is_me, |
|
self.m_type.to_int(), |
|
self.content, |
|
self.is_delivery, |
|
self.datetime, |
|
]) |
|
} |
|
|
|
pub fn get(db: &DStorage, fid: &i64) -> Result<Vec<Message>> { |
|
let sql = format!("SELECT id, hash, fid, is_me, m_type, content, is_delivery, datetime FROM messages WHERE fid = {} and is_deleted = false ORDER BY id DESC", fid); |
|
let matrix = db.query(&sql)?; |
|
let mut messages = vec![]; |
|
for values in matrix { |
|
messages.push(Message::from_values(values, false)); |
|
} |
|
Ok(messages) |
|
} |
|
|
|
pub fn get_id(db: &DStorage, id: i64) -> Result<Option<Message>> { |
|
let sql = format!("SELECT id, hash, fid, is_me, m_type, content, is_delivery, datetime, is_deleted FROM messages WHERE id = {}", id); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(Message::from_values(values, true))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn get_it(db: &DStorage, hash: &EventId) -> Result<Option<Message>> { |
|
let sql = format!("SELECT id, hash, fid, is_me, m_type, content, is_delivery, datetime, is_deleted FROM messages WHERE hash = {}", hash.to_hex()); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(Message::from_values(values, true))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!( |
|
"INSERT INTO messages (hash, fid, is_me, m_type, content, is_delivery, datetime, is_deleted) VALUES ('{}',{},{},{},'{}',{},{},false)", |
|
self.hash.to_hex(), |
|
self.fid, |
|
self.is_me, |
|
self.m_type.to_int(), |
|
self.content, |
|
self.is_delivery, |
|
self.datetime, |
|
); |
|
self.id = db.insert(&sql)?; |
|
Ok(()) |
|
} |
|
|
|
pub fn delivery(db: &DStorage, id: i64, is_delivery: bool) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE messages SET is_delivery={} WHERE id = {}", |
|
is_delivery, id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn delete(&self, db: &DStorage) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE messages SET is_deleted = true WHERE id = {}", |
|
self.id |
|
); |
|
db.delete(&sql) |
|
} |
|
|
|
pub fn exist(db: &DStorage, hash: &EventId) -> Result<bool> { |
|
let sql = format!("SELECT id FROM messages WHERE hash = '{}'", hash.to_hex()); |
|
let matrix = db.query(&sql)?; |
|
Ok(matrix.len() > 0) |
|
} |
|
}
|
|
|