use super::Response; use crate::database::{ self, get_relationship, get_relationship_internal, message::Message, Permission, PermissionCalculator, Relationship, }; use crate::guards::auth::UserRef; use crate::guards::channel::ChannelRef; use crate::util::vec_to_set; use bson::{doc, from_bson, Bson, Bson::UtcDatetime}; use chrono::prelude::*; use mongodb::options::FindOptions; use num_enum::TryFromPrimitive; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; const MAXGROUPSIZE: usize = 50; #[derive(Debug, TryFromPrimitive)] #[repr(usize)] pub enum ChannelType { DM = 0, GROUPDM = 1, GUILDCHANNEL = 2, } macro_rules! with_permissions { ($user: expr, $target: expr) => {{ let permissions = PermissionCalculator::new($user.clone()) .channel($target.clone()) .as_permission(); if !permissions.get_access() { return None; } permissions }}; } #[derive(Serialize, Deserialize)] pub struct CreateGroup { name: String, nonce: String, users: Vec<String>, } /// create a new group #[post("/create", data = "<info>")] pub fn create_group(user: UserRef, info: Json<CreateGroup>) -> Response { let name: String = info.name.chars().take(32).collect(); let nonce: String = info.nonce.chars().take(32).collect(); let mut set = vec_to_set(&info.users); set.insert(user.id.clone()); if set.len() > MAXGROUPSIZE { return Response::BadRequest(json!({ "error": "Maximum group size is 50." })); } let col = database::get_collection("channels"); if let Some(_) = col.find_one(doc! { "nonce": nonce.clone() }, None).unwrap() { return Response::BadRequest(json!({ "error": "Group already created!" })); } let mut query = vec![]; for item in &set { if item == &user.id { continue; } query.push(Bson::String(item.clone())); } if let Ok(result) = database::get_collection("users").find( doc! { "_id": { "$in": &query } }, FindOptions::builder().limit(query.len() as i64).build(), ) { if result.count() != query.len() { return Response::BadRequest(json!({ "error": "Specified non-existant user(s)." })); } let relationships = user.fetch_relationships(); for item in set { if item == user.id { continue; } if get_relationship_internal(&user.id, &item, &relationships) != Relationship::Friend { return Response::BadRequest(json!({ "error": "Not friends with user(s)." })); } } query.push(Bson::String(user.id.clone())); let id = Ulid::new().to_string(); if col .insert_one( doc! { "_id": id.clone(), "nonce": nonce, "type": ChannelType::GROUPDM as u32, "recipients": &query, "name": name, "owner": &user.id, }, None, ) .is_ok() { Response::Success(json!({ "id": id })) } else { Response::InternalServerError(json!({ "error": "Failed to create guild channel." })) } } else { Response::InternalServerError(json!({ "error": "Failed to validate users." })) } } /// fetch channel information #[get("/<target>")] pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> { with_permissions!(user, target); match target.channel_type { 0 => Some(Response::Success(json!({ "id": target.id, "type": target.channel_type, "recipients": target.recipients, }))), 1 => { if let Some(info) = target.fetch_data(doc! { "name": 1, "description": 1, "owner": 1, }) { Some(Response::Success(json!({ "id": target.id, "type": target.channel_type, "recipients": target.recipients, "name": info.get_str("name").unwrap(), "owner": info.get_str("owner").unwrap(), "description": info.get_str("description").unwrap_or(""), }))) } else { None } } 2 => { if let Some(info) = target.fetch_data(doc! { "name": 1, "description": 1, }) { Some(Response::Success(json!({ "id": target.id, "type": target.channel_type, "guild": target.guild, "name": info.get_str("name").unwrap(), "description": info.get_str("description").unwrap_or(""), }))) } else { None } } _ => unreachable!(), } } /// [groups] add user to channel #[put("/<target>/recipients/<member>")] pub fn add_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<Response> { if target.channel_type != 1 { return Some(Response::BadRequest(json!({ "error": "Not a group DM." }))); } with_permissions!(user, target); let recp = target.recipients.unwrap(); if recp.len() == 50 { return Some(Response::BadRequest( json!({ "error": "Maximum group size is 50." }), )); } let set = vec_to_set(&recp); if set.get(&member.id).is_some() { return Some(Response::BadRequest( json!({ "error": "User already in group!" }), )); } match get_relationship(&user, &member) { Relationship::Friend => { if database::get_collection("channels") .update_one( doc! { "_id": &target.id }, doc! { "$push": { "recipients": &member.id } }, None, ) .is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to add user to group." }), )) } } _ => Some(Response::BadRequest( json!({ "error": "Not friends with user." }), )), } } /// [groups] remove user from channel #[delete("/<target>/recipients/<member>")] pub fn remove_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<Response> { if target.channel_type != 1 { return Some(Response::BadRequest(json!({ "error": "Not a group DM." }))); } if &user.id == &member.id { return Some(Response::BadRequest( json!({ "error": "Cannot kick yourself, leave the channel instead." }), )); } let permissions = with_permissions!(user, target); if !permissions.get_kick_members() { return Some(Response::LackingPermission(Permission::KickMembers)); } let set = vec_to_set(&target.recipients.unwrap()); if set.get(&member.id).is_none() { return Some(Response::BadRequest( json!({ "error": "User not in group!" }), )); } if database::get_collection("channels") .update_one( doc! { "_id": &target.id }, doc! { "$pull": { "recipients": &member.id } }, None, ) .is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to add user to group." }), )) } } /// delete channel /// or leave group DM /// or close DM conversation #[delete("/<target>")] pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_manage_channels() { return Some(Response::LackingPermission(Permission::ManageChannels)); } let col = database::get_collection("channels"); let target_id = target.id.clone(); let try_delete = || { let messages = database::get_collection("messages"); if messages .delete_many(doc! { "channel": &target_id }, None) .is_ok() { if col.delete_one(doc! { "_id": &target_id }, None).is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to delete group." }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Failed to delete messages." }), )) } }; match target.channel_type { 0 => { if col .update_one( doc! { "_id": &target_id }, doc! { "$set": { "active": false } }, None, ) .is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to close channel." }), )) } } 1 => { let mut recipients = vec_to_set(&target.recipients.expect("Missing recipients on Group DM.")); let owner = target.owner.expect("Missing owner on Group DM."); if recipients.len() == 1 { try_delete() } else { recipients.remove(&user.id); let new_owner = if owner == user.id { recipients.iter().next().unwrap() } else { &owner }; if col .update_one( doc! { "_id": target_id }, doc! { "$set": { "owner": new_owner, }, "$pull": { "recipients": &user.id, } }, None, ) .is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to remove you from the group." }), )) } } } 2 => try_delete(), _ => Some(Response::InternalServerError( json!({ "error": "Unknown error has occurred." }), )), } } /// fetch channel messages #[get("/<target>/messages")] pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_read_messages() { return Some(Response::LackingPermission(Permission::ReadMessages)); } let col = database::get_collection("messages"); let result = col.find(doc! { "channel": target.id }, None).unwrap(); let mut messages = Vec::new(); for item in result { let message: Message = from_bson(bson::Bson::Document(item.unwrap())).expect("Failed to unwrap message."); messages.push(json!({ "id": message.id, "author": message.author, "content": message.content, "edited": if let Some(t) = message.edited { Some(t.timestamp()) } else { None } })); } Some(Response::Success(json!(messages))) } #[derive(Serialize, Deserialize)] pub struct SendMessage { content: String, nonce: String, } /// send a message to a channel #[post("/<target>/messages", data = "<message>")] pub fn send_message( user: UserRef, target: ChannelRef, message: Json<SendMessage>, ) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_send_messages() { return Some(Response::LackingPermission(Permission::SendMessages)); } let content: String = message.content.chars().take(2000).collect(); let nonce: String = message.nonce.chars().take(32).collect(); let col = database::get_collection("messages"); if col .find_one(doc! { "nonce": nonce.clone() }, None) .unwrap() .is_some() { return Some(Response::BadRequest( json!({ "error": "Message already sent!" }), )); } let id = Ulid::new().to_string(); Some( if col .insert_one( doc! { "_id": id.clone(), "nonce": nonce, "channel": target.id.clone(), "author": user.id, "content": content, }, None, ) .is_ok() { if target.channel_type == ChannelType::DM as u8 { let col = database::get_collection("channels"); col.update_one( doc! { "_id": &target.id }, doc! { "$set": { "active": true } }, None, ) .unwrap(); } /*websocket::queue_message( get_recipients(&target), json!({ "type": "message", "data": { "id": id.clone(), "nonce": nonce, "channel": target.id, "author": user.id, "content": content, }, }) .to_string(), );*/ Response::Success(json!({ "id": id })) } else { Response::InternalServerError(json!({ "error": "Failed database query." })) }, ) } /// get a message #[get("/<target>/messages/<message>")] pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_read_messages() { return Some(Response::LackingPermission(Permission::ReadMessages)); } let prev = // ! CHECK IF USER HAS PERMISSION TO VIEW EDITS OF MESSAGES if let Some(previous) = message.previous_content { let mut entries = vec![]; for entry in previous { entries.push(json!({ "content": entry.content, "time": entry.time.timestamp(), })); } Some(entries) } else { None }; Some(Response::Success(json!({ "id": message.id, "author": message.author, "content": message.content, "edited": if let Some(t) = message.edited { Some(t.timestamp()) } else { None }, "previous_content": prev, }))) } #[derive(Serialize, Deserialize)] pub struct EditMessage { content: String, } /// edit a message #[patch("/<target>/messages/<message>", data = "<edit>")] pub fn edit_message( user: UserRef, target: ChannelRef, message: Message, edit: Json<EditMessage>, ) -> Option<Response> { with_permissions!(user, target); if message.author != user.id { return Some(Response::Unauthorized( json!({ "error": "You did not send this message." }), )); } let col = database::get_collection("messages"); let time = if let Some(edited) = message.edited { edited.0 } else { Ulid::from_string(&message.id).unwrap().datetime() }; let edited = Utc::now(); match col.update_one( doc! { "_id": message.id.clone() }, doc! { "$set": { "content": edit.content.clone(), "edited": UtcDatetime(edited) }, "$push": { "previous_content": { "content": message.content, "time": time, } }, }, None, ) { Ok(_) => { /*websocket::queue_message( get_recipients(&target), json!({ "type": "message_update", "data": { "id": message.id, "channel": target.id, "content": edit.content.clone(), "edited": edited.timestamp() }, }) .to_string(), );*/ Some(Response::Result(super::Status::Ok)) } Err(_) => Some(Response::InternalServerError( json!({ "error": "Failed to update message." }), )), } } /// delete a message #[delete("/<target>/messages/<message>")] pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_manage_messages() { if message.author != user.id { return Some(Response::LackingPermission(Permission::ManageMessages)); } } let col = database::get_collection("messages"); match col.delete_one(doc! { "_id": &message.id }, None) { Ok(_) => { /*websocket::queue_message( get_recipients(&target), json!({ "type": "message_delete", "data": { "id": message.id, "channel": target.id }, }) .to_string(), );*/ Some(Response::Result(super::Status::Ok)) } Err(_) => Some(Response::InternalServerError( json!({ "error": "Failed to delete message." }), )), } }