diff --git a/clippy.toml b/clippy.toml index 00ef1dce..d4739dfa 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,19 +3,19 @@ disallowed-methods = [ "revolt_database::models::bots::model::Bot::remove_field", # Prefer to use Object::create() - "revolt_database::models::bots::ops::AbstractBots::*", "revolt_database::models::bots::ops::AbstractBots::insert_bot", - "revolt_database::models::bots::ops::AbstractChannelInvites::insert_invite", - "revolt_database::models::bots::ops::AbstractChannelUnreads::acknowledge_message", - "revolt_database::models::bots::ops::AbstractChannels::insert_channel", - "revolt_database::models::bots::ops::AbstractEmojis::insert_emoji", - "revolt_database::models::bots::ops::AbstractMessages::insert_message", - "revolt_database::models::bots::ops::AbstractRatelimitEvents::insert_ratelimit_event", - "revolt_database::models::bots::ops::AbstractRatelimitEvents::insert_ratelimit_event", - "revolt_database::models::bots::ops::AbstractServerBans::insert_ban", - "revolt_database::models::bots::ops::AbstractServerMembers::insert_member", - "revolt_database::models::bots::ops::AbstractServers::insert_server", - "revolt_database::models::bots::ops::AbstractUsers::insert_user", + "revolt_database::models::channel_invites::ops::AbstractChannelInvites::insert_invite", + "revolt_database::models::channel_unreads::ops::AbstractChannelUnreads::acknowledge_message", + "revolt_database::models::channel_webhooks::ops::AbstractWebhooks::insert_webhook", + "revolt_database::models::channels::ops::AbstractChannels::insert_channel", + "revolt_database::models::emojis::ops::AbstractEmojis::insert_emoji", + "revolt_database::models::files::ops::AbstractAttachments::insert_attachment", + "revolt_database::models::messages::ops::AbstractMessages::insert_message", + "revolt_database::models::ratelimit_events::ops::AbstractRatelimitEvents::insert_ratelimit_event", + "revolt_database::models::server_bans::ops::AbstractServerBans::insert_ban", + "revolt_database::models::server_members::ops::AbstractServerMembers::insert_member", + "revolt_database::models::servers::ops::AbstractServers::insert_server", + "revolt_database::models::users::ops::AbstractUsers::insert_user", # Prefer to use Object::update(&self) "revolt_database::models::bots::ops::AbstractBots::update_bot", diff --git a/crates/core/database/src/models/bots/model.rs b/crates/core/database/src/models/bots/model.rs index 23cd9861..702f8d8b 100644 --- a/crates/core/database/src/models/bots/model.rs +++ b/crates/core/database/src/models/bots/model.rs @@ -1,6 +1,7 @@ use revolt_result::Result; +use ulid::Ulid; -use crate::Database; +use crate::{BotInformation, Database, PartialUser, User}; auto_derived_partial!( /// Bot @@ -51,6 +52,51 @@ auto_derived!( #[allow(clippy::disallowed_methods)] impl Bot { + /// Create a new bot + pub async fn create(db: &Database, username: String, owner: &User, data: D) -> Result + where + D: Into>, + { + if owner.bot.is_some() { + return Err(create_error!(IsBot)); + } + + // TODO: config + let max_bot_count = 5; + if db.get_number_of_bots_by_user(&owner.id).await? >= max_bot_count { + return Err(create_error!(ReachedMaximumBots)); + } + + let id = Ulid::new().to_string(); + + User::create( + db, + username, + Some(id.to_string()), + Some(PartialUser { + bot: Some(BotInformation { + owner: id.to_string(), + }), + ..Default::default() + }), + ) + .await?; + + let mut bot = Bot { + id, + owner: owner.id.to_string(), + token: nanoid::nanoid!(64), + ..Default::default() + }; + + if let Some(data) = data.into() { + bot.apply_options(data); + } + + db.insert_bot(&bot).await?; + Ok(bot) + } + /// Remove a field from this object pub fn remove_field(&mut self, field: &FieldsBot) { match field { @@ -96,27 +142,24 @@ mod tests { #[async_std::test] async fn crud() { database_test!(|db| async move { - let bot_id = "bot"; - let user_id = "user"; - let token = "my_token"; + let owner = User::create(&db, "Owner".to_string(), None, None) + .await + .unwrap(); - let user = User { - id: bot_id.to_string(), - username: "Bot Name".to_string(), - ..Default::default() - }; + let bot = Bot::create( + &db, + "Bot Name".to_string(), + &owner, + PartialBot { + token: Some("my token".to_string()), + interactions_url: Some("some url".to_string()), + ..Default::default() + }, + ) + .await + .unwrap(); - db.insert_user(&user).await.unwrap(); - - let bot = Bot { - id: bot_id.to_string(), - owner: user_id.to_string(), - token: token.to_string(), - interactions_url: "some url".to_string(), - ..Default::default() - }; - - db.insert_bot(&bot).await.unwrap(); + assert!(!bot.interactions_url.is_empty()); let mut updated_bot = bot.clone(); updated_bot @@ -131,9 +174,9 @@ mod tests { .await .unwrap(); - let fetched_bot1 = db.fetch_bot(bot_id).await.unwrap(); + let fetched_bot1 = db.fetch_bot(&bot.id).await.unwrap(); let fetched_bot2 = db.fetch_bot_by_token(&fetched_bot1.token).await.unwrap(); - let fetched_bots = db.fetch_bots_by_user(user_id).await.unwrap(); + let fetched_bots = db.fetch_bots_by_user(&owner.id).await.unwrap(); assert!(!bot.public); assert!(fetched_bot1.public); @@ -143,12 +186,12 @@ mod tests { assert_eq!(updated_bot, fetched_bot1); assert_eq!(fetched_bot1, fetched_bot2); assert_eq!(fetched_bot1, fetched_bots[0]); - assert_eq!(1, db.get_number_of_bots_by_user(user_id).await.unwrap()); + assert_eq!(1, db.get_number_of_bots_by_user(&owner.id).await.unwrap()); bot.delete(&db).await.unwrap(); - assert!(db.fetch_bot(bot_id).await.is_err()); - assert_eq!(0, db.get_number_of_bots_by_user(user_id).await.unwrap()); - assert_eq!(db.fetch_user(bot_id).await.unwrap().flags, Some(2)) + assert!(db.fetch_bot(&bot.id).await.is_err()); + assert_eq!(0, db.get_number_of_bots_by_user(&owner.id).await.unwrap()); + assert_eq!(db.fetch_user(&bot.id).await.unwrap().flags, Some(2)) }); } } diff --git a/crates/core/database/src/models/users/model.rs b/crates/core/database/src/models/users/model.rs index 4287fd44..cd823a02 100644 --- a/crates/core/database/src/models/users/model.rs +++ b/crates/core/database/src/models/users/model.rs @@ -133,7 +133,35 @@ pub static DISCRIMINATOR_SEARCH_SPACE: Lazy> = Lazy::new(|| { set.into_iter().collect() }); +#[allow(clippy::disallowed_methods)] impl User { + /// Create a new user + pub async fn create( + db: &Database, + username: String, + account_id: I, + data: D, + ) -> Result + where + I: Into>, + D: Into>, + { + let username = User::validate_username(username)?; + let mut user = User { + id: account_id.into().unwrap_or_else(|| Ulid::new().to_string()), + discriminator: User::find_discriminator(db, &username, None).await?, + username, + ..Default::default() + }; + + if let Some(data) = data.into() { + user.apply_options(data); + } + + db.insert_user(&user).await?; + Ok(user) + } + /// Sanitise and validate a username can be used pub fn validate_username(username: String) -> Result { // Copy the username for validation @@ -207,28 +235,6 @@ impl User { event_type: crate::RatelimitEventType::DiscriminatorChange, }) .await?; - - /* let rvdb: revolt_database::Database = db.clone().into(); - if rvdb - .has_ratelimited( - &target_id, - RatelimitEventType::DiscriminatorChange, - Duration::from_secs(60 * 60 * 24), - 1, - ) - .await - .map_err(Error::from_core)? - { - return Err(Error::DiscriminatorChangeRatelimited); - } - - rvdb.insert_ratelimit_event(&revolt_database::RatelimitEvent { - id: ulid::Ulid::new().to_string(), - target_id, - event_type: RatelimitEventType::DiscriminatorChange, - }) - .await - .map_err(Error::from_core)?; */ } } diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index f329e345..dddbaded 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -605,6 +605,33 @@ impl crate::User { id: self.id, } } + + pub async fn into_self(self) -> User { + User { + username: self.username, + discriminator: self.discriminator, + display_name: self.display_name, + avatar: self.avatar.map(|file| file.into()), + relations: self + .relations + .map(|relationships| { + relationships + .into_iter() + .map(|relationship| relationship.into()) + .collect() + }) + .unwrap_or_default(), + badges: self.badges.unwrap_or_default() as u32, + status: None, + profile: None, + flags: self.flags.unwrap_or_default() as u32, + privileged: self.privileged, + bot: self.bot.map(|bot| bot.into()), + relationship: RelationshipStatus::User, + online: revolt_presence::is_online(&self.id).await, + id: self.id, + } + } } impl From for RelationshipStatus { diff --git a/crates/delta/src/routes/bots/create.rs b/crates/delta/src/routes/bots/create.rs index 3462b9cd..ab87bee8 100644 --- a/crates/delta/src/routes/bots/create.rs +++ b/crates/delta/src/routes/bots/create.rs @@ -1,11 +1,8 @@ -use nanoid::nanoid; - -use revolt_database::{Bot, BotInformation, Database, User}; +use revolt_database::{Bot, Database, User}; use revolt_models::v0; use revolt_result::{create_error, Result}; use rocket::serde::json::Json; use rocket::State; -use ulid::Ulid; use validator::Validate; /// # Create Bot @@ -18,10 +15,6 @@ pub async fn create_bot( user: User, info: Json, ) -> Result> { - if user.bot.is_some() { - return Err(create_error!(IsBot)); - } - let info = info.into_inner(); info.validate().map_err(|error| { create_error!(FailedValidation { @@ -29,32 +22,6 @@ pub async fn create_bot( }) })?; - // TODO: config - let max_bot_count = 5; - if db.get_number_of_bots_by_user(&user.id).await? >= max_bot_count { - return Err(create_error!(ReachedMaximumBots)); - } - - let id = Ulid::new().to_string(); - let username = User::validate_username(info.name)?; - let bot_user = User { - id: id.clone(), - discriminator: User::find_discriminator(db, &username, None).await?, - username, - bot: Some(BotInformation { - owner: user.id.clone(), - }), - ..Default::default() - }; - - let bot = Bot { - id, - owner: user.id, - token: nanoid!(64), - ..Default::default() - }; - - db.insert_user(&bot_user).await?; - db.insert_bot(&bot).await?; + let bot = Bot::create(db, info.name, &user, None).await?; Ok(Json(bot.into())) } diff --git a/crates/delta/src/routes/onboard/complete.rs b/crates/delta/src/routes/onboard/complete.rs index 90f7136f..150fdf7d 100644 --- a/crates/delta/src/routes/onboard/complete.rs +++ b/crates/delta/src/routes/onboard/complete.rs @@ -1,7 +1,8 @@ use crate::util::regex::RE_USERNAME; -use revolt_quark::{ - authifier::models::Session, models::User, Database, EmptyResponse, Error, Result, -}; +use revolt_database::{Database, User}; +use revolt_models::v0; +use revolt_quark::authifier::models::Session; +use revolt_result::{create_error, Result}; use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; @@ -25,22 +26,22 @@ pub async fn req( session: Session, user: Option, data: Json, -) -> Result { +) -> Result> { if user.is_some() { - return Err(Error::AlreadyOnboarded); + return Err(create_error!(AlreadyOnboarded)); } let data = data.into_inner(); - data.validate() - .map_err(|error| Error::FailedValidation { error })?; + data.validate().map_err(|error| { + create_error!(FailedValidation { + error: error.to_string() + }) + })?; - let username = User::validate_username(data.username)?; - let user = User { - id: session.user_id, - discriminator: User::find_discriminator(db, &username, None).await?, - username, - ..Default::default() - }; - - db.insert_user(&user).await.map(|_| EmptyResponse) + Ok(Json( + User::create(db, data.username, session.user_id, None) + .await? + .into_self() + .await, + )) }