From 9f749cfdf86d7f9e8787961da5ae749a6d824008 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 12 Aug 2021 09:24:06 +0100 Subject: [PATCH] Invite bot to server / group for #8. --- src/database/entities/channel.rs | 50 +++++++++++++++++++ src/database/guards/reference.rs | 4 ++ src/routes/bots/invite.rs | 65 +++++++++++++++++++++++++ src/routes/bots/mod.rs | 4 +- src/routes/channels/group_add_member.rs | 51 +------------------ src/routes/sync/get_unreads.rs | 2 +- src/util/result.rs | 2 + 7 files changed, 127 insertions(+), 51 deletions(-) create mode 100644 src/routes/bots/invite.rs diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs index 1e1e0a7d..f3029660 100644 --- a/src/database/entities/channel.rs +++ b/src/database/entities/channel.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use crate::database::*; use crate::notifications::events::ClientboundNotification; use crate::util::result::{Error, Result}; +use crate::util::variables::MAX_GROUP_SIZE; use futures::StreamExt; use mongodb::bson::Bson; use mongodb::{ @@ -344,4 +345,53 @@ impl Channel { Ok(()) } + + pub async fn add_to_group(&self, member: String, by_user: String) -> Result<()> { + if let Channel::Group { id, recipients, .. } = &self { + if recipients.len() >= *MAX_GROUP_SIZE { + Err(Error::GroupTooLarge { + max: *MAX_GROUP_SIZE, + })? + } + + if recipients.iter().find(|x| *x == &member).is_some() { + Err(Error::AlreadyInGroup)? + } + + get_collection("channels") + .update_one( + doc! { + "_id": &id + }, + doc! { + "$push": { + "recipients": &member + } + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "update_one", + with: "channel", + })?; + + ClientboundNotification::ChannelGroupJoin { + id: id.clone(), + user: member.clone(), + } + .publish(id.clone()); + + Content::SystemMessage(SystemMessage::UserAdded { + id: member, + by: by_user, + }) + .send_as_system(&self) + .await + .ok(); + Ok(()) + } else { + Err(Error::InvalidOperation) + } + } } diff --git a/src/database/guards/reference.rs b/src/database/guards/reference.rs index 3394ef0b..2982833d 100644 --- a/src/database/guards/reference.rs +++ b/src/database/guards/reference.rs @@ -61,6 +61,10 @@ impl Ref { self.fetch("channel_invites").await } + pub async fn fetch_bot(&self) -> Result { + self.fetch("bots").await + } + pub async fn fetch_member(&self, server: &str) -> Result { let doc = get_collection("server_members") .find_one( diff --git a/src/routes/bots/invite.rs b/src/routes/bots/invite.rs new file mode 100644 index 00000000..0f7399ef --- /dev/null +++ b/src/routes/bots/invite.rs @@ -0,0 +1,65 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use rocket::serde::json::Json; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct ServerId { + server: String +} + +#[derive(Deserialize)] +pub struct GroupId { + group: String +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum Destination { + Server(ServerId), + Group(GroupId) +} + +#[post("//invite", data = "")] +pub async fn invite_bot(user: User, target: Ref, dest: Json) -> Result<()> { + let bot = target.fetch_bot().await?; + + if !bot.public { + if bot.owner != user.id { + return Err(Error::BotIsPrivate); + } + } + + match dest.into_inner() { + Destination::Server(ServerId { server }) => { + let server = Ref::from(server)?.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&server) + .for_server() + .await?; + + if !perm.get_manage_server() { + Err(Error::MissingPermission)? + } + + server.join_member(&bot.id).await?; + Ok(()) + } + Destination::Group(GroupId { group }) => { + let channel = Ref::from(group)?.fetch_channel().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_channel(&channel) + .for_channel() + .await?; + + if !perm.get_invite_others() { + Err(Error::MissingPermission)? + } + + channel.add_to_group(bot.id, user.id).await + } + } +} diff --git a/src/routes/bots/mod.rs b/src/routes/bots/mod.rs index 966ce8a4..1606eff2 100644 --- a/src/routes/bots/mod.rs +++ b/src/routes/bots/mod.rs @@ -1,9 +1,11 @@ use rocket::Route; mod create; +mod invite; pub fn routes() -> Vec { routes![ - create::create_bot + create::create_bot, + invite::invite_bot ] } diff --git a/src/routes/channels/group_add_member.rs b/src/routes/channels/group_add_member.rs index a2361ed7..f2108373 100644 --- a/src/routes/channels/group_add_member.rs +++ b/src/routes/channels/group_add_member.rs @@ -1,6 +1,5 @@ use crate::util::result::{Error, Result}; -use crate::util::variables::MAX_GROUP_SIZE; -use crate::{database::*, notifications::events::ClientboundNotification}; +use crate::database::*; use mongodb::bson::doc; @@ -20,51 +19,5 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { Err(Error::MissingPermission)? } - if let Channel::Group { id, recipients, .. } = &channel { - if recipients.len() >= *MAX_GROUP_SIZE { - Err(Error::GroupTooLarge { - max: *MAX_GROUP_SIZE, - })? - } - - if recipients.iter().find(|x| *x == &member.id).is_some() { - Err(Error::AlreadyInGroup)? - } - - get_collection("channels") - .update_one( - doc! { - "_id": &id - }, - doc! { - "$push": { - "recipients": &member.id - } - }, - None, - ) - .await - .map_err(|_| Error::DatabaseError { - operation: "update_one", - with: "channel", - })?; - - ClientboundNotification::ChannelGroupJoin { - id: id.clone(), - user: member.id.clone(), - } - .publish(id.clone()); - - Content::SystemMessage(SystemMessage::UserAdded { - id: member.id, - by: user.id, - }) - .send_as_system(&channel) - .await - .ok(); - - Ok(()) - } else { - Err(Error::InvalidOperation) - } + channel.add_to_group(member.id, user.id).await } diff --git a/src/routes/sync/get_unreads.rs b/src/routes/sync/get_unreads.rs index b76dfa54..a83149a7 100644 --- a/src/routes/sync/get_unreads.rs +++ b/src/routes/sync/get_unreads.rs @@ -1,5 +1,5 @@ use crate::database::*; -use crate::util::result::Result; +use crate::util::result::{Error, Result}; use mongodb::bson::doc; use rocket::serde::json::Value; diff --git a/src/util/result.rs b/src/util/result.rs index 679cf7a5..1591cf00 100644 --- a/src/util/result.rs +++ b/src/util/result.rs @@ -47,6 +47,7 @@ pub enum Error { // ? Bot related errors. ReachedMaximumBots, IsBot, + BotIsPrivate, // ? General errors. TooManyIds, @@ -104,6 +105,7 @@ impl<'r> Responder<'r, 'static> for Error { Error::ReachedMaximumBots => Status::BadRequest, Error::IsBot => Status::BadRequest, + Error::BotIsPrivate => Status::Forbidden, Error::FailedValidation { .. } => Status::UnprocessableEntity, Error::DatabaseError { .. } => Status::InternalServerError,