use super::channel::ChannelType; use super::Response; use crate::database::guild::{fetch_member as get_member, get_invite, Guild, MemberKey}; use crate::database::{ self, channel::fetch_channel, guild::serialise_guilds_with_channels, channel::Channel, Permission, PermissionCalculator, user::User }; use crate::notifications::{ self, events::{guilds::*, Notification}, }; use crate::util::gen_token; use mongodb::bson::{doc, Bson}; use mongodb::options::{FindOneOptions, FindOptions}; use rocket::request::Form; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; // ! FIXME: GET RID OF THIS macro_rules! with_permissions { ($user: expr, $target: expr) => {{ let permissions = PermissionCalculator::new($user.clone()) .guild($target.clone()) .fetch_data(); let value = permissions.as_permission(); if !value.get_access() { return None; } (value, permissions.member.unwrap()) }}; } /// fetch your guilds #[get("/@me")] pub fn my_guilds(user: User) -> Response { if let Ok(gids) = user.find_guilds() { if let Ok(data) = serialise_guilds_with_channels(&gids) { Response::Success(json!(data)) } else { Response::InternalServerError(json!({ "error": "Failed to fetch guilds." })) } } else { Response::InternalServerError(json!({ "error": "Failed to fetch memberships." })) } } /// fetch a guild #[get("/<target>")] pub fn guild(user: User, target: Guild) -> Option<Response> { with_permissions!(user, target); if let Ok(result) = target.seralise_with_channels() { Some(Response::Success(result)) } else { Some(Response::InternalServerError(json!({ "error": "Failed to fetch channels!" }))) } } /// delete or leave a guild #[delete("/<target>")] pub fn remove_guild(user: User, target: Guild) -> Option<Response> { with_permissions!(user, target); if user.id == target.owner { let channels = database::get_collection("channels"); if let Ok(result) = channels.find( doc! { "type": 2, "guild": &target.id }, FindOptions::builder().projection(doc! { "_id": 1 }).build(), ) { let mut values = vec![]; for item in result { if let Ok(doc) = item { values.push(Bson::String(doc.get_str("_id").unwrap().to_string())); } } if database::get_collection("messages") .delete_many( doc! { "channel": { "$in": values } }, None, ) .is_ok() { if channels .delete_many( doc! { "type": 2, "guild": &target.id, }, None, ) .is_ok() { if database::get_collection("members") .delete_many( doc! { "_id.guild": &target.id, }, None, ) .is_ok() { if database::get_collection("guilds") .delete_one( doc! { "_id": &target.id }, None, ) .is_ok() { notifications::send_message_threaded( None, target.id.clone(), Notification::guild_delete(Delete { id: target.id.clone(), }), ); Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to delete guild." }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Failed to delete guild members." }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Failed to delete guild channels." }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Failed to delete guild messages." }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Could not fetch channels." }), )) } } else { if database::get_collection("members") .delete_one( doc! { "_id.guild": &target.id, "_id.user": &user.id, }, None, ) .is_ok() { notifications::send_message_threaded( None, target.id.clone(), Notification::guild_user_leave(UserLeave { id: target.id.clone(), user: user.id.clone(), banned: false, }), ); Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to remove you from the guild." }), )) } } } #[derive(Serialize, Deserialize)] pub struct CreateChannel { nonce: String, name: String, description: Option<String>, } /// create a new channel #[post("/<target>/channels", data = "<info>")] pub fn create_channel(user: User, target: Guild, info: Json<CreateChannel>) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); if !permissions.get_manage_channels() { return Some(Response::LackingPermission(Permission::ManageChannels)); } let nonce: String = info.nonce.chars().take(32).collect(); let name: String = info.name.chars().take(32).collect(); let description: String = info .description .clone() .unwrap_or(String::new()) .chars() .take(255) .collect(); if let Ok(result) = database::get_collection("channels").find_one(doc! { "nonce": &nonce }, None) { if result.is_some() { return Some(Response::BadRequest( json!({ "error": "Channel already created." }), )); } let id = Ulid::new().to_string(); if database::get_collection("channels") .insert_one( doc! { "_id": &id, "nonce": &nonce, "type": 2, "guild": &target.id, "name": &name, "description": &description, }, None, ) .is_ok() { if database::get_collection("guilds") .update_one( doc! { "_id": &target.id }, doc! { "$addToSet": { "channels": &id } }, None ) .is_ok() { notifications::send_message_threaded( None, target.id.clone(), Notification::guild_channel_create(ChannelCreate { id: target.id.clone(), channel: id.clone(), name: name.clone(), description: description.clone(), }), ); Some(Response::Success(json!({ "id": &id }))) } else { Some(Response::InternalServerError( json!({ "error": "Couldn't save channel list." }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Couldn't create channel." }), )) } } else { Some(Response::BadRequest( json!({ "error": "Failed to check if channel was made." }), )) } } #[derive(Serialize, Deserialize)] pub struct InviteOptions { // ? TODO: add options } /// create a new invite #[post("/<target>/channels/<channel>/invite", data = "<_options>")] pub fn create_invite( user: User, target: Guild, channel: Channel, _options: Json<InviteOptions>, ) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); if !permissions.get_create_invite() { return Some(Response::LackingPermission(Permission::CreateInvite)); } let code = gen_token(7); if database::get_collection("guilds") .update_one( doc! { "_id": target.id }, doc! { "$push": { "invites": { "code": &code, "creator": user.id, "channel": channel.id, } } }, None, ) .is_ok() { Some(Response::Success(json!({ "code": code }))) } else { Some(Response::BadRequest( json!({ "error": "Failed to create invite." }), )) } } /// remove an invite #[delete("/<target>/invites/<code>")] pub fn remove_invite(user: User, target: Guild, code: String) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); if let Some((guild_id, _, invite)) = get_invite(&code, None) { if invite.creator != user.id { if !permissions.get_manage_server() { return Some(Response::LackingPermission(Permission::ManageServer)); } } if database::get_collection("guilds") .update_one( doc! { "_id": &guild_id, }, doc! { "$pull": { "invites": { "code": &code } } }, None, ) .is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::BadRequest( json!({ "error": "Failed to delete invite." }), )) } } else { Some(Response::NotFound( json!({ "error": "Failed to fetch invite or code is invalid." }), )) } } /// fetch all guild invites #[get("/<target>/invites")] pub fn fetch_invites(user: User, target: Guild) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); if !permissions.get_manage_server() { return Some(Response::LackingPermission(Permission::ManageServer)); } Some(Response::Success(json!(target.invites))) } /// view an invite before joining #[get("/join/<code>", rank = 1)] pub fn fetch_invite(user: User, code: String) -> Response { if let Some((guild_id, name, invite)) = get_invite(&code, user.id) { match fetch_channel(&invite.channel) { Ok(result) => { if let Some(channel) = result { Response::Success(json!({ "guild": { "id": guild_id, "name": name, }, "channel": { "id": channel.id, "name": channel.name, } })) } else { Response::NotFound(json!({ "error": "Channel does not exist." })) } } Err(err) => Response::InternalServerError(json!({ "error": err })), } } else { Response::NotFound(json!({ "error": "Failed to fetch invite or code is invalid." })) } } /// join a guild using an invite #[post("/join/<code>", rank = 1)] pub fn use_invite(user: User, code: String) -> Response { if let Some((guild_id, _, invite)) = get_invite(&code, Some(user.id.clone())) { if let Ok(result) = database::get_collection("members").find_one( doc! { "_id.guild": &guild_id, "_id.user": &user.id }, FindOneOptions::builder() .projection(doc! { "_id": 1 }) .build(), ) { if result.is_none() { if database::get_collection("members") .insert_one( doc! { "_id": { "guild": &guild_id, "user": &user.id } }, None, ) .is_ok() { notifications::send_message_threaded( None, guild_id.clone(), Notification::guild_user_join(UserJoin { id: guild_id.clone(), user: user.id.clone(), }), ); Response::Success(json!({ "guild": &guild_id, "channel": &invite.channel, })) } else { Response::InternalServerError( json!({ "error": "Failed to add you to the guild." }), ) } } else { Response::BadRequest(json!({ "error": "Already in the guild." })) } } else { Response::InternalServerError( json!({ "error": "Failed to check if you're in the guild." }), ) } } else { Response::NotFound(json!({ "error": "Failed to fetch invite or code is invalid." })) } } #[derive(Serialize, Deserialize)] pub struct CreateGuild { name: String, description: Option<String>, nonce: String, } /// create a new guild #[post("/create", data = "<info>")] pub fn create_guild(user: User, info: Json<CreateGuild>) -> Response { if !user.email_verification.verified { return Response::Unauthorized(json!({ "error": "Email not verified!" })); } let name: String = info.name.chars().take(32).collect(); let description: String = info .description .clone() .unwrap_or("No description.".to_string()) .chars() .take(255) .collect(); let nonce: String = info.nonce.chars().take(32).collect(); let channels = database::get_collection("channels"); let col = database::get_collection("guilds"); if col .find_one(doc! { "nonce": nonce.clone() }, None) .unwrap() .is_some() { return Response::BadRequest(json!({ "error": "Guild already created!" })); } let id = Ulid::new().to_string(); let channel_id = Ulid::new().to_string(); if channels .insert_one( doc! { "_id": channel_id.clone(), "type": ChannelType::GUILDCHANNEL as u32, "name": "general", "description": "", "guild": id.clone(), }, None, ) .is_err() { return Response::InternalServerError( json!({ "error": "Failed to create guild channel." }), ); } if database::get_collection("members") .insert_one( doc! { "_id": { "guild": &id, "user": &user.id } }, None, ) .is_err() { return Response::InternalServerError( json!({ "error": "Failed to add you to members list." }), ); } if col .insert_one( doc! { "_id": &id, "nonce": nonce, "name": name, "description": description, "owner": &user.id, "invites": [], "bans": [], "default_permissions": 51, }, None, ) .is_ok() { Response::Success(json!({ "id": id })) } else { channels .delete_one(doc! { "_id": channel_id }, None) .expect("Failed to delete the channel we just made."); Response::InternalServerError(json!({ "error": "Failed to create guild." })) } } /// fetch a guild's member #[get("/<target>/members")] pub fn fetch_members(user: User, target: Guild) -> Option<Response> { with_permissions!(user, target); if let Ok(result) = database::get_collection("members").find(doc! { "_id.guild": target.id }, None) { let mut users = vec![]; for item in result { if let Ok(doc) = item { users.push(json!({ "id": doc.get_document("_id").unwrap().get_str("user").unwrap(), "nickname": doc.get_str("nickname").ok(), })); } } Some(Response::Success(json!(users))) } else { Some(Response::InternalServerError( json!({ "error": "Failed to fetch members." }), )) } } /// fetch a guild member #[get("/<target>/members/<other>")] pub fn fetch_member(user: User, target: Guild, other: String) -> Option<Response> { with_permissions!(user, target); if let Ok(result) = get_member(MemberKey(target.id, other)) { if let Some(member) = result { Some(Response::Success(json!({ "id": member.id.user, "nickname": member.nickname, }))) } else { Some(Response::NotFound( json!({ "error": "Member does not exist!" }), )) } } else { Some(Response::InternalServerError( json!({ "error": "Failed to fetch member." }), )) } } /// kick a guild member #[delete("/<target>/members/<other>")] pub fn kick_member(user: User, target: Guild, other: String) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); if user.id == other { return Some(Response::BadRequest( json!({ "error": "Cannot kick yourself." }), )); } if !permissions.get_kick_members() { return Some(Response::LackingPermission(Permission::KickMembers)); } if let Ok(result) = get_member(MemberKey( target.id.clone(), other.clone() )) { if result.is_none() { return Some(Response::BadRequest( json!({ "error": "User not part of guild." }), )); } } else { return Some(Response::InternalServerError(json!({ "error": "Failed to fetch member." }))) } if database::get_collection("members") .delete_one( doc! { "_id.guild": &target.id, "_id.user": &other, }, None, ) .is_ok() { notifications::send_message_threaded( None, target.id.clone(), Notification::guild_user_leave(UserLeave { id: target.id.clone(), user: other.clone(), banned: false, }), ); Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to kick member." }), )) } } #[derive(Serialize, Deserialize, FromForm)] pub struct BanOptions { reason: Option<String>, } /// ban a guild member #[put("/<target>/members/<other>/ban?<options..>")] pub fn ban_member( user: User, target: Guild, other: String, options: Form<BanOptions>, ) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); let reason: String = options .reason .clone() .unwrap_or("No reason specified.".to_string()) .chars() .take(64) .collect(); if user.id == other { return Some(Response::BadRequest( json!({ "error": "Cannot ban yourself." }), )); } if !permissions.get_ban_members() { return Some(Response::LackingPermission(Permission::BanMembers)); } if let Ok(result) = get_member(MemberKey( target.id.clone(), other.clone() )) { if result.is_none() { return Some(Response::BadRequest( json!({ "error": "User not part of guild." }), )); } } else { return Some(Response::InternalServerError(json!({ "error": "Failed to fetch member." }))) } if database::get_collection("guilds") .update_one( doc! { "_id": &target.id }, doc! { "$push": { "bans": { "id": &other, "reason": reason, } } }, None, ) .is_err() { return Some(Response::BadRequest( json!({ "error": "Failed to add ban to guild." }), )); } if database::get_collection("members") .delete_one( doc! { "_id.guild": &target.id, "_id.user": &other, }, None, ) .is_ok() { notifications::send_message_threaded( None, target.id.clone(), Notification::guild_user_leave(UserLeave { id: target.id.clone(), user: other.clone(), banned: true, }), ); Some(Response::Result(super::Status::Ok)) } else { Some(Response::InternalServerError( json!({ "error": "Failed to kick member after adding to ban list." }), )) } } /// unban a guild member #[delete("/<target>/members/<other>/ban")] pub fn unban_member(user: User, target: Guild, other: String) -> Option<Response> { let (permissions, _) = with_permissions!(user, target); if user.id == other { return Some(Response::BadRequest( json!({ "error": "Cannot unban yourself (not checking if you're banned)." }), )); } if !permissions.get_ban_members() { return Some(Response::LackingPermission(Permission::BanMembers)); } if target.bans.iter().any(|v| v.id == other) { return Some(Response::BadRequest(json!({ "error": "User not banned." }))); } if database::get_collection("guilds") .update_one( doc! { "_id": &target.id }, doc! { "$pull": { "bans": { "$elemMatch": { "id": &other } } } }, None, ) .is_ok() { Some(Response::Result(super::Status::Ok)) } else { Some(Response::BadRequest( json!({ "error": "Failed to remove ban." }), )) } } #[options("/<_target>")] pub fn guild_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/channels")] pub fn create_channel_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/channels/<_channel>/invite")] pub fn create_invite_preflight(_target: String, _channel: String) -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/invites/<_code>")] pub fn remove_invite_preflight(_target: String, _code: String) -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/invites")] pub fn fetch_invites_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) } #[options("/join/<_code>", rank = 1)] pub fn invite_preflight(_code: String) -> Response { Response::Result(super::Status::Ok) } #[options("/create")] pub fn create_guild_preflight() -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/members")] pub fn fetch_members_preflight(_target: String) -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/members/<_other>")] pub fn fetch_member_preflight(_target: String, _other: String) -> Response { Response::Result(super::Status::Ok) } #[options("/<_target>/members/<_other>/ban")] pub fn ban_member_preflight(_target: String, _other: String) -> Response { Response::Result(super::Status::Ok) }