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.
831 lines
24 KiB
831 lines
24 KiB
use rand::Rng; |
|
use std::path::PathBuf; |
|
use std::time::{SystemTime, UNIX_EPOCH}; |
|
use tdn::types::{ |
|
group::GroupId, |
|
primitive::{new_io_error, PeerAddr, Result}, |
|
rpc::{json, RpcParam}, |
|
}; |
|
use tdn_storage::local::{DStorage, DsValue}; |
|
|
|
use group_chat_types::{GroupInfo, GroupType, NetworkMessage}; |
|
|
|
use crate::apps::chat::MessageType; |
|
use crate::storage::{ |
|
group_chat_db, write_avatar_sync, write_file_sync, write_image_sync, write_record_sync, |
|
}; |
|
|
|
pub(crate) struct GroupChatKey(Vec<u8>); |
|
|
|
impl GroupChatKey { |
|
pub fn new(value: Vec<u8>) -> Self { |
|
Self(value) |
|
} |
|
|
|
pub fn key(&self) -> &[u8] { |
|
&self.0 |
|
} |
|
|
|
pub fn hash(&self) -> Vec<u8> { |
|
vec![] // TODO |
|
} |
|
|
|
pub fn from_hex(s: impl ToString) -> Result<Self> { |
|
let s = s.to_string(); |
|
if s.len() % 2 != 0 { |
|
return Err(new_io_error("Hex is invalid")); |
|
} |
|
let mut value = vec![]; |
|
|
|
for i in 0..(s.len() / 2) { |
|
let res = u8::from_str_radix(&s[2 * i..2 * i + 2], 16) |
|
.map_err(|_e| new_io_error("Hex is invalid"))?; |
|
value.push(res); |
|
} |
|
|
|
Ok(Self(value)) |
|
} |
|
|
|
pub fn to_hex(&self) -> String { |
|
let mut hex = String::new(); |
|
hex.extend(self.0.iter().map(|byte| format!("{:02x?}", byte))); |
|
hex |
|
} |
|
} |
|
|
|
/// Group Chat Model. |
|
pub(crate) struct GroupChat { |
|
/// db auto-increment id. |
|
pub id: i64, |
|
/// consensus height. |
|
pub height: i64, |
|
/// group chat owner. |
|
pub owner: GroupId, |
|
/// group chat id. |
|
pub g_id: GroupId, |
|
/// group chat type. |
|
g_type: GroupType, |
|
/// group chat server addresse. |
|
pub g_addr: PeerAddr, |
|
/// group chat name. |
|
g_name: String, |
|
/// group chat simple intro. |
|
g_bio: String, |
|
/// group chat is set top sessions. |
|
is_top: bool, |
|
/// group chat is created ok. |
|
is_ok: bool, |
|
/// group chat is closed. |
|
is_closed: bool, |
|
/// group chat need manager agree. |
|
is_need_agree: bool, |
|
/// group chat encrypted-key. |
|
key: GroupChatKey, |
|
/// group chat lastest message time. (only ESSE used) |
|
last_datetime: i64, |
|
/// group chat lastest message content. (only ESSE used) |
|
last_content: String, |
|
/// group chat lastest message readed. (only ESSE used) |
|
last_readed: bool, |
|
/// group chat created time. |
|
pub datetime: i64, |
|
/// group chat is online. |
|
pub online: bool, |
|
/// is deleted. |
|
is_deleted: bool, |
|
} |
|
|
|
impl GroupChat { |
|
pub fn new( |
|
owner: GroupId, |
|
g_type: GroupType, |
|
g_addr: PeerAddr, |
|
g_name: String, |
|
g_bio: String, |
|
is_need_agree: bool, |
|
) -> Self { |
|
let g_id = GroupId(rand::thread_rng().gen::<[u8; 32]>()); |
|
|
|
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 key = GroupChatKey(vec![]); |
|
|
|
Self { |
|
owner, |
|
g_id, |
|
g_type, |
|
g_addr, |
|
g_name, |
|
g_bio, |
|
is_need_agree, |
|
key, |
|
datetime, |
|
id: 0, |
|
height: 0, |
|
is_top: true, |
|
is_ok: false, |
|
is_closed: false, |
|
last_datetime: datetime, |
|
last_content: Default::default(), |
|
last_readed: true, |
|
online: false, |
|
is_deleted: false, |
|
} |
|
} |
|
|
|
fn new_from( |
|
g_id: GroupId, |
|
height: i64, |
|
owner: GroupId, |
|
g_type: GroupType, |
|
g_addr: PeerAddr, |
|
g_name: String, |
|
g_bio: String, |
|
is_need_agree: bool, |
|
key: GroupChatKey, |
|
) -> Self { |
|
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. |
|
|
|
Self { |
|
owner, |
|
g_id, |
|
g_type, |
|
g_addr, |
|
g_name, |
|
g_bio, |
|
is_need_agree, |
|
key, |
|
datetime, |
|
id: 0, |
|
height, |
|
is_top: true, |
|
is_ok: true, |
|
is_closed: false, |
|
last_datetime: datetime, |
|
last_content: Default::default(), |
|
last_readed: true, |
|
online: false, |
|
is_deleted: false, |
|
} |
|
} |
|
|
|
pub fn from_info( |
|
key: GroupChatKey, |
|
info: GroupInfo, |
|
height: i64, |
|
addr: PeerAddr, |
|
base: PathBuf, |
|
mgid: &GroupId, |
|
) -> Result<Self> { |
|
match info { |
|
GroupInfo::Common(owner, _, g_id, g_type, agree, name, g_bio, avatar) => { |
|
write_avatar_sync(&base, &mgid, &g_id, avatar)?; |
|
Ok(Self::new_from( |
|
g_id, height, owner, g_type, addr, name, g_bio, agree, key, |
|
)) |
|
} |
|
GroupInfo::Encrypted(owner, _, g_id, agree, _hash, _name, _bio, avatar) => { |
|
// TODO decrypted. |
|
|
|
let g_type = GroupType::Encrypted; |
|
let name = "".to_owned(); |
|
let bio = "".to_owned(); |
|
|
|
write_avatar_sync(&base, &mgid, &g_id, avatar)?; |
|
|
|
Ok(Self::new_from( |
|
g_id, height, owner, g_type, addr, name, bio, agree, key, |
|
)) |
|
} |
|
} |
|
} |
|
|
|
pub fn to_group_info(self, name: String, avatar: Vec<u8>) -> GroupInfo { |
|
match self.g_type { |
|
GroupType::Common | GroupType::Open => GroupInfo::Common( |
|
self.owner, |
|
name, |
|
self.g_id, |
|
self.g_type, |
|
self.is_need_agree, |
|
self.g_name, |
|
self.g_bio, |
|
avatar, |
|
), |
|
GroupType::Encrypted => GroupInfo::Common( |
|
// TODO encrypted |
|
self.owner, |
|
name, |
|
self.g_id, |
|
self.g_type, |
|
self.is_need_agree, |
|
self.g_name, |
|
self.g_bio, |
|
avatar, |
|
), |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.owner.to_hex(), |
|
self.g_id.to_hex(), |
|
self.g_type.to_u32(), |
|
self.g_addr.to_hex(), |
|
self.g_name, |
|
self.g_bio, |
|
if self.is_top { "1" } else { "0" }, |
|
if self.is_ok { "1" } else { "0" }, |
|
if self.is_closed { "1" } else { "0" }, |
|
if self.is_need_agree { "1" } else { "0" }, |
|
self.last_datetime, |
|
self.last_content, |
|
if self.last_readed { "1" } else { "0" }, |
|
if self.online { "1" } else { "0" }, |
|
]) |
|
} |
|
|
|
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Self { |
|
let is_deleted = if contains_deleted { |
|
v.pop().unwrap().as_bool() |
|
} else { |
|
false |
|
}; |
|
|
|
Self { |
|
is_deleted, |
|
online: false, |
|
datetime: v.pop().unwrap().as_i64(), |
|
last_readed: v.pop().unwrap().as_bool(), |
|
last_content: v.pop().unwrap().as_string(), |
|
last_datetime: v.pop().unwrap().as_i64(), |
|
key: GroupChatKey::from_hex(v.pop().unwrap().as_string()) |
|
.unwrap_or(GroupChatKey::new(vec![])), |
|
is_closed: v.pop().unwrap().as_bool(), |
|
is_need_agree: v.pop().unwrap().as_bool(), |
|
is_ok: v.pop().unwrap().as_bool(), |
|
is_top: v.pop().unwrap().as_bool(), |
|
g_bio: v.pop().unwrap().as_string(), |
|
g_name: v.pop().unwrap().as_string(), |
|
g_addr: PeerAddr::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()), |
|
g_type: GroupType::from_u32(v.pop().unwrap().as_i64() as u32), |
|
g_id: GroupId::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()), |
|
owner: GroupId::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()), |
|
height: v.pop().unwrap().as_i64(), |
|
id: v.pop().unwrap().as_i64(), |
|
} |
|
} |
|
|
|
/// use in rpc when load account friends. |
|
pub fn all(db: &DStorage) -> Result<Vec<GroupChat>> { |
|
let matrix = db.query("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime FROM groups WHERE is_deleted = false ORDER BY last_datetime DESC")?; |
|
let mut groups = vec![]; |
|
for values in matrix { |
|
groups.push(GroupChat::from_values(values, false)); |
|
} |
|
Ok(groups) |
|
} |
|
|
|
/// use in rpc when load account groups. |
|
pub fn all_ok(db: &DStorage) -> Result<Vec<GroupChat>> { |
|
let matrix = db.query("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime FROM groups WHERE is_closed = false ORDER BY last_datetime DESC")?; |
|
let mut groups = vec![]; |
|
for values in matrix { |
|
groups.push(GroupChat::from_values(values, false)); |
|
} |
|
Ok(groups) |
|
} |
|
|
|
pub fn get(db: &DStorage, gid: &GroupId) -> Result<Option<GroupChat>> { |
|
let sql = format!("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime FROM groups WHERE gcd = '{}' AND is_deleted = false", gid.to_hex()); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(GroupChat::from_values(values, false))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn get_id(db: &DStorage, id: &i64) -> Result<Option<GroupChat>> { |
|
let sql = format!("SELECT id, height, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime FROM groups WHERE id = {} AND is_deleted = false", id); |
|
let mut matrix = db.query(&sql)?; |
|
if matrix.len() > 0 { |
|
let values = matrix.pop().unwrap(); // safe unwrap() |
|
return Ok(Some(GroupChat::from_values(values, false))); |
|
} |
|
Ok(None) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!("INSERT INTO groups (height, owner, gcd, gtype, addr, name, bio, is_top, is_ok, is_need_agree, is_closed, key, last_datetime, last_content, last_readed, datetime, is_deleted) VALUES ({}, '{}', '{}', {}, '{}', '{}', '{}', {}, {}, {}, {}, '{}', {}, '{}', {}, {}, false)", |
|
self.height, |
|
self.owner.to_hex(), |
|
self.g_id.to_hex(), |
|
self.g_type.to_u32(), |
|
self.g_addr.to_hex(), |
|
self.g_name, |
|
self.g_bio, |
|
if self.is_top { 1 } else { 0 }, |
|
if self.is_ok { 1 } else { 0 }, |
|
if self.is_need_agree { 1 } else { 0 }, |
|
if self.is_closed { 1 } else { 0 }, |
|
self.key.to_hex(), |
|
self.last_datetime, |
|
self.last_content, |
|
if self.last_readed { 1 } else { 0 }, |
|
self.datetime, |
|
); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
Ok(()) |
|
} |
|
|
|
pub fn ok(&mut self, db: &DStorage) -> Result<usize> { |
|
self.is_ok = true; |
|
let sql = format!("UPDATE groups SET is_ok=1 WHERE id = {}", self.id); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn add_height(db: &DStorage, id: i64, height: i64) -> Result<usize> { |
|
let sql = format!("UPDATE groups SET height={} WHERE id = {}", height, id,); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn update_last_message( |
|
db: &DStorage, |
|
id: i64, |
|
msg: &Message, |
|
read: bool, |
|
height: i64, |
|
) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE groups SET height={}, last_datetime={}, last_content='{}', last_readed={} WHERE id = {}", |
|
height, |
|
msg.datetime, |
|
msg.content, |
|
if read { 1 } else { 0 }, |
|
id, |
|
); |
|
db.update(&sql) |
|
} |
|
|
|
pub fn readed(db: &DStorage, id: i64) -> Result<usize> { |
|
let sql = format!("UPDATE groups SET last_readed=1 WHERE id = {}", id); |
|
db.update(&sql) |
|
} |
|
} |
|
|
|
/// Group Join Request model. include my requests and other requests. |
|
/// When fid is 0, it's my requests. |
|
pub(crate) struct Request { |
|
id: i64, |
|
fid: i64, |
|
pub gid: GroupId, |
|
pub addr: PeerAddr, |
|
pub name: String, |
|
key: GroupChatKey, |
|
remark: String, |
|
is_ok: bool, |
|
is_over: bool, |
|
datetime: i64, |
|
} |
|
|
|
impl Request { |
|
pub fn new_by_remote( |
|
fid: i64, |
|
gid: GroupId, |
|
addr: PeerAddr, |
|
name: String, |
|
remark: String, |
|
datetime: i64, |
|
) -> Self { |
|
Self { |
|
fid, |
|
gid, |
|
addr, |
|
name, |
|
remark, |
|
datetime, |
|
key: GroupChatKey(vec![]), |
|
is_ok: false, |
|
is_over: false, |
|
id: 0, |
|
} |
|
} |
|
|
|
pub fn new_by_me( |
|
gid: GroupId, |
|
addr: PeerAddr, |
|
name: String, |
|
remark: String, |
|
key: GroupChatKey, |
|
) -> Self { |
|
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. |
|
|
|
Self { |
|
gid, |
|
addr, |
|
name, |
|
remark, |
|
datetime, |
|
key, |
|
is_ok: false, |
|
is_over: false, |
|
fid: 0, |
|
id: 0, |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.fid, |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
self.is_ok, |
|
self.is_over, |
|
self.datetime, |
|
]) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!("INSERT INTO requests (fid, gid, addr, name, remark, key, is_ok, is_over, datetime, is_deleted) VALUES ({}, '{}', '{}', '{}', '{}', '{}', {}, {}, {}, false)", |
|
self.fid, |
|
self.gid.to_hex(), |
|
self.addr.to_hex(), |
|
self.name, |
|
self.remark, |
|
self.key.to_hex(), |
|
if self.is_ok { 1 } else { 0 }, |
|
if self.is_over { 1 } else { 0 }, |
|
self.datetime, |
|
); |
|
println!("{}", sql); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
Ok(()) |
|
} |
|
|
|
pub fn over(db: &DStorage, gcd: &GroupId, is_ok: bool) -> Result<(i64, GroupChatKey)> { |
|
let matrix = db.query(&format!( |
|
"SELECT id, key from requests WHERE gid = '{}' AND is_over = 0 ORDER BY id", |
|
gcd.to_hex() |
|
))?; |
|
let mut requests = vec![]; |
|
for mut values in matrix { |
|
let id = values.pop().unwrap().as_i64(); |
|
let key = GroupChatKey::from_hex(values.pop().unwrap().as_string()) |
|
.unwrap_or(GroupChatKey::new(vec![])); |
|
requests.push((id, key)); |
|
} |
|
|
|
let sql = format!( |
|
"UPDATE requests SET is_ok={}, is_over=1 WHERE gid = '{}' AND is_over = 0", |
|
if is_ok { 1 } else { 0 }, |
|
gcd.to_hex(), |
|
); |
|
db.update(&sql)?; |
|
|
|
if requests.len() > 0 { |
|
Ok(requests.pop().unwrap()) // safe. |
|
} else { |
|
Err(new_io_error("no requests")) |
|
} |
|
} |
|
} |
|
|
|
/// Group Member Model. |
|
pub(crate) struct Member { |
|
/// db auto-increment id. |
|
id: i64, |
|
/// group's db id. |
|
fid: i64, |
|
/// member's Did(GroupId) |
|
m_id: GroupId, |
|
/// member's addresse. |
|
m_addr: PeerAddr, |
|
/// member's name. |
|
m_name: String, |
|
/// is group chat manager. |
|
is_manager: bool, |
|
/// member's joined time. |
|
datetime: i64, |
|
/// member is leave or delete. |
|
is_deleted: bool, |
|
} |
|
|
|
impl Member { |
|
pub fn new( |
|
fid: i64, |
|
m_id: GroupId, |
|
m_addr: PeerAddr, |
|
m_name: String, |
|
is_manager: bool, |
|
datetime: i64, |
|
) -> Self { |
|
Self { |
|
fid, |
|
m_id, |
|
m_addr, |
|
m_name, |
|
is_manager, |
|
datetime, |
|
id: 0, |
|
is_deleted: false, |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.fid, |
|
self.m_id.to_hex(), |
|
self.m_addr.to_hex(), |
|
self.m_name, |
|
self.is_manager, |
|
]) |
|
} |
|
|
|
fn from_values(mut v: Vec<DsValue>, contains_deleted: bool) -> Self { |
|
let is_deleted = if contains_deleted { |
|
v.pop().unwrap().as_bool() |
|
} else { |
|
false |
|
}; |
|
|
|
Self { |
|
is_deleted, |
|
datetime: v.pop().unwrap().as_i64(), |
|
is_manager: v.pop().unwrap().as_bool(), |
|
m_name: v.pop().unwrap().as_string(), |
|
m_addr: PeerAddr::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()), |
|
m_id: GroupId::from_hex(v.pop().unwrap().as_string()).unwrap_or(Default::default()), |
|
fid: v.pop().unwrap().as_i64(), |
|
id: v.pop().unwrap().as_i64(), |
|
} |
|
} |
|
|
|
pub fn all(db: &DStorage, fid: &i64) -> Result<Vec<Member>> { |
|
let matrix = db.query(&format!( |
|
"SELECT id, fid, mid, addr, name, is_manager, datetime FROM members WHERE is_deleted = false AND fid = {}", fid))?; |
|
let mut groups = vec![]; |
|
for values in matrix { |
|
groups.push(Member::from_values(values, false)); |
|
} |
|
Ok(groups) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!("INSERT INTO members (fid, mid, addr, name, is_manager, datetime, is_deleted) VALUES ({}, '{}', '{}', '{}', {}, {}, false)", |
|
self.fid, |
|
self.m_id.to_hex(), |
|
self.m_addr.to_hex(), |
|
self.m_name, |
|
if self.is_manager { 1 } else { 0 }, |
|
self.datetime, |
|
); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
Ok(()) |
|
} |
|
|
|
pub fn get_id(db: &DStorage, fid: &i64, mid: &GroupId) -> Result<i64> { |
|
let mut matrix = db.query(&format!( |
|
"SELECT id FROM members WHERE fid = {} AND mid = '{}'", |
|
fid, |
|
mid.to_hex() |
|
))?; |
|
if matrix.len() > 0 { |
|
Ok(matrix.pop().unwrap().pop().unwrap().as_i64()) // safe unwrap. |
|
} else { |
|
Err(new_io_error("missing member")) |
|
} |
|
} |
|
|
|
pub fn update(db: &DStorage, id: &i64, addr: &PeerAddr, name: &str) -> Result<usize> { |
|
let sql = format!( |
|
"UPDATE members SET addr='{}', name='{}' WHERE id = {}", |
|
addr.to_hex(), |
|
name, |
|
id, |
|
); |
|
db.update(&sql) |
|
} |
|
} |
|
|
|
/// Group Chat Message Model. |
|
pub(crate) struct Message { |
|
/// db auto-increment id. |
|
id: i64, |
|
/// group message consensus height. |
|
height: i64, |
|
/// group's db id. |
|
fid: i64, |
|
/// member's db id. |
|
mid: i64, |
|
/// message is mine. |
|
is_me: bool, |
|
/// message type. |
|
m_type: MessageType, |
|
/// message content. |
|
content: String, |
|
/// message is delivery. |
|
is_delivery: bool, |
|
/// message created time. |
|
datetime: i64, |
|
/// message is deteled |
|
is_deleted: bool, |
|
} |
|
|
|
impl Message { |
|
pub(crate) fn new_with_time( |
|
height: i64, |
|
fid: i64, |
|
mid: i64, |
|
is_me: bool, |
|
m_type: MessageType, |
|
content: String, |
|
datetime: i64, |
|
) -> Message { |
|
Self { |
|
fid, |
|
mid, |
|
m_type, |
|
content, |
|
datetime, |
|
height, |
|
is_me, |
|
is_deleted: false, |
|
is_delivery: true, |
|
id: 0, |
|
} |
|
} |
|
pub(crate) fn new( |
|
height: i64, |
|
fid: i64, |
|
mid: i64, |
|
is_me: bool, |
|
m_type: MessageType, |
|
content: String, |
|
) -> 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. |
|
|
|
Self::new_with_time(height, fid, mid, is_me, m_type, content, 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(), |
|
mid: v.pop().unwrap().as_i64(), |
|
fid: v.pop().unwrap().as_i64(), |
|
height: v.pop().unwrap().as_i64(), |
|
id: v.pop().unwrap().as_i64(), |
|
} |
|
} |
|
|
|
pub fn to_rpc(&self) -> RpcParam { |
|
json!([ |
|
self.id, |
|
self.height, |
|
self.fid, |
|
self.mid, |
|
self.is_me, |
|
self.m_type.to_int(), |
|
self.content, |
|
self.is_delivery, |
|
self.datetime, |
|
]) |
|
} |
|
|
|
pub fn all(db: &DStorage, fid: &i64) -> Result<Vec<Message>> { |
|
let matrix = db.query(&format!("SELECT id, height, fid, mid, is_me, m_type, content, is_delivery, datetime FROM messages WHERE is_deleted = false AND fid = {}", fid))?; |
|
let mut groups = vec![]; |
|
for values in matrix { |
|
groups.push(Message::from_values(values, false)); |
|
} |
|
Ok(groups) |
|
} |
|
|
|
pub fn insert(&mut self, db: &DStorage) -> Result<()> { |
|
let sql = format!("INSERT INTO messages (height, fid, mid, is_me, m_type, content, is_delivery, datetime, is_deleted) VALUES ({}, {}, {}, {}, {}, '{}', {}, {}, false)", |
|
self.height, |
|
self.fid, |
|
self.mid, |
|
if self.is_me { 1 } else { 0 }, |
|
self.m_type.to_int(), |
|
self.content, |
|
if self.is_delivery { 1 } else { 0 }, |
|
self.datetime, |
|
); |
|
let id = db.insert(&sql)?; |
|
self.id = id; |
|
Ok(()) |
|
} |
|
} |
|
|
|
pub(super) fn to_network_message( |
|
_mtype: MessageType, |
|
content: &str, |
|
) -> Result<(NetworkMessage, i64)> { |
|
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. |
|
|
|
Ok((NetworkMessage::String(content.to_owned()), datetime)) |
|
} |
|
|
|
pub(super) fn from_network_message( |
|
height: i64, |
|
gdid: i64, |
|
mid: GroupId, |
|
mgid: &GroupId, |
|
msg: NetworkMessage, |
|
datetime: i64, |
|
base: &PathBuf, |
|
) -> Result<Message> { |
|
let db = group_chat_db(base, mgid)?; |
|
let mdid = Member::get_id(&db, &gdid, &mid)?; |
|
let is_me = &mid == mgid; |
|
|
|
// handle event. |
|
let (m_type, raw) = match msg { |
|
NetworkMessage::String(content) => (MessageType::String, content), |
|
NetworkMessage::Image(bytes) => { |
|
let image_name = write_image_sync(base, mgid, bytes)?; |
|
(MessageType::Image, image_name) |
|
} |
|
NetworkMessage::File(old_name, bytes) => { |
|
let filename = write_file_sync(base, mgid, &old_name, bytes)?; |
|
(MessageType::File, filename) |
|
} |
|
NetworkMessage::Contact(name, rgid, addr, avatar_bytes) => { |
|
write_avatar_sync(base, mgid, &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, mgid, gdid, time, bytes)?; |
|
(MessageType::Record, record_name) |
|
} |
|
NetworkMessage::Phone => { |
|
// TODO |
|
(MessageType::Phone, "".to_owned()) |
|
} |
|
NetworkMessage::Video => { |
|
// TODO |
|
(MessageType::Video, "".to_owned()) |
|
} |
|
NetworkMessage::None => { |
|
return Ok(Message::new( |
|
height, |
|
gdid, |
|
mdid, |
|
is_me, |
|
MessageType::String, |
|
"".to_owned(), |
|
)); |
|
} |
|
}; |
|
|
|
let mut msg = Message::new_with_time(height, gdid, mdid, is_me, m_type, raw, datetime); |
|
msg.insert(&db)?; |
|
GroupChat::update_last_message(&db, gdid, &msg, false, height)?; |
|
Ok(msg) |
|
}
|
|
|