chore: refine user and bot creation methods

This commit is contained in:
Paul Makles
2023-08-05 12:06:43 +01:00
parent c9011ac692
commit 9f3c1036d0
6 changed files with 155 additions and 111 deletions

View File

@@ -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",

View File

@@ -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<D>(db: &Database, username: String, owner: &User, data: D) -> Result<Bot>
where
D: Into<Option<PartialBot>>,
{
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))
});
}
}

View File

@@ -133,7 +133,35 @@ pub static DISCRIMINATOR_SEARCH_SPACE: Lazy<HashSet<String>> = Lazy::new(|| {
set.into_iter().collect()
});
#[allow(clippy::disallowed_methods)]
impl User {
/// Create a new user
pub async fn create<I, D>(
db: &Database,
username: String,
account_id: I,
data: D,
) -> Result<User>
where
I: Into<Option<String>>,
D: Into<Option<PartialUser>>,
{
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<String> {
// 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)?; */
}
}

View File

@@ -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<crate::RelationshipStatus> for RelationshipStatus {

View File

@@ -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<v0::DataCreateBot>,
) -> Result<Json<v0::Bot>> {
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()))
}

View File

@@ -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<User>,
data: Json<DataOnboard>,
) -> Result<EmptyResponse> {
) -> Result<Json<v0::User>> {
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,
))
}