revolt_database/models/bots/
model.rs1use revolt_result::Result;
2use ulid::Ulid;
3
4use crate::{events::client::EventV1, BotInformation, Database, PartialUser, User};
5
6auto_derived_partial!(
7 pub struct Bot {
9 #[serde(rename = "_id")]
13 pub id: String,
14 pub owner: String,
16 pub token: String,
18 pub public: bool,
21
22 #[serde(skip_serializing_if = "crate::if_false", default)]
24 pub analytics: bool,
25 #[serde(skip_serializing_if = "crate::if_false", default)]
27 pub discoverable: bool,
28 #[serde(skip_serializing_if = "String::is_empty", default)]
30 pub interactions_url: String,
31 #[serde(skip_serializing_if = "String::is_empty", default)]
33 pub terms_of_service_url: String,
34 #[serde(skip_serializing_if = "String::is_empty", default)]
36 pub privacy_policy_url: String,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub flags: Option<i32>,
41 },
42 "PartialBot"
43);
44
45auto_derived!(
46 pub enum FieldsBot {
48 Token,
49 InteractionsURL,
50 }
51);
52
53#[allow(clippy::derivable_impls)]
54impl Default for Bot {
55 fn default() -> Self {
56 Self {
57 id: Default::default(),
58 owner: Default::default(),
59 token: Default::default(),
60 public: Default::default(),
61 analytics: Default::default(),
62 discoverable: Default::default(),
63 interactions_url: Default::default(),
64 terms_of_service_url: Default::default(),
65 privacy_policy_url: Default::default(),
66 flags: Default::default(),
67 }
68 }
69}
70
71#[allow(clippy::disallowed_methods)]
72impl Bot {
73 pub async fn create<D>(
75 db: &Database,
76 username: String,
77 owner: &User,
78 data: D,
79 ) -> Result<(Bot, User)>
80 where
81 D: Into<Option<PartialBot>>,
82 {
83 if owner.bot.is_some() {
84 return Err(create_error!(IsBot));
85 }
86
87 if db.get_number_of_bots_by_user(&owner.id).await? >= owner.limits().await.bots {
88 return Err(create_error!(ReachedMaximumBots));
89 }
90
91 let id = Ulid::new().to_string();
92
93 let user = User::create(
94 db,
95 username,
96 Some(id.to_string()),
97 Some(PartialUser {
98 bot: Some(BotInformation {
99 owner: owner.id.to_string(),
100 }),
101 ..Default::default()
102 }),
103 )
104 .await?;
105
106 let mut bot = Bot {
107 id,
108 owner: owner.id.to_string(),
109 token: nanoid::nanoid!(64),
110 ..Default::default()
111 };
112
113 if let Some(data) = data.into() {
114 bot.apply_options(data);
115 }
116
117 db.insert_bot(&bot).await?;
118 Ok((bot, user))
119 }
120
121 pub fn remove_field(&mut self, field: &FieldsBot) {
123 match field {
124 FieldsBot::Token => self.token = nanoid::nanoid!(64),
125 FieldsBot::InteractionsURL => {
126 self.interactions_url = String::new();
127 }
128 }
129 }
130
131 pub async fn update(
133 &mut self,
134 db: &Database,
135 mut partial: PartialBot,
136 remove: Vec<FieldsBot>,
137 ) -> Result<()> {
138 if remove.contains(&FieldsBot::Token) {
139 partial.token = Some(nanoid::nanoid!(64));
140 }
141
142 for field in &remove {
143 self.remove_field(field);
144 }
145
146 db.update_bot(&self.id, &partial, remove).await?;
147
148 if partial.token.is_some() {
149 EventV1::Logout.private(self.id.clone()).await;
150 }
151
152 self.apply_options(partial);
153 Ok(())
154 }
155
156 pub async fn delete(&self, db: &Database) -> Result<()> {
158 db.fetch_user(&self.id).await?.mark_deleted(db).await?;
159 db.delete_bot(&self.id).await
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::{Bot, FieldsBot, PartialBot, User};
166
167 #[async_std::test]
168 async fn crud() {
169 database_test!(|db| async move {
170 let owner = User::create(&db, "Owner".to_string(), None, None)
171 .await
172 .unwrap();
173
174 let (bot, _) = Bot::create(
175 &db,
176 "Bot Name".to_string(),
177 &owner,
178 PartialBot {
179 token: Some("my token".to_string()),
180 interactions_url: Some("some url".to_string()),
181 ..Default::default()
182 },
183 )
184 .await
185 .unwrap();
186
187 assert!(!bot.interactions_url.is_empty());
188
189 let mut updated_bot = bot.clone();
190 updated_bot
191 .update(
192 &db,
193 PartialBot {
194 public: Some(true),
195 ..Default::default()
196 },
197 vec![FieldsBot::Token, FieldsBot::InteractionsURL],
198 )
199 .await
200 .unwrap();
201
202 let fetched_bot1 = db.fetch_bot(&bot.id).await.unwrap();
203 let fetched_bot2 = db.fetch_bot_by_token(&fetched_bot1.token).await.unwrap();
204 let fetched_bots = db.fetch_bots_by_user(&owner.id).await.unwrap();
205
206 assert!(!bot.public);
207 assert!(fetched_bot1.public);
208 assert!(!bot.interactions_url.is_empty());
209 assert!(fetched_bot1.interactions_url.is_empty());
210 assert_ne!(bot.token, fetched_bot1.token);
211 assert_eq!(updated_bot, fetched_bot1);
212 assert_eq!(fetched_bot1, fetched_bot2);
213 assert_eq!(fetched_bot1, fetched_bots[0]);
214 assert_eq!(1, db.get_number_of_bots_by_user(&owner.id).await.unwrap());
215
216 bot.delete(&db).await.unwrap();
217 assert!(db.fetch_bot(&bot.id).await.is_err());
218 assert_eq!(0, db.get_number_of_bots_by_user(&owner.id).await.unwrap());
219 assert_eq!(db.fetch_user(&bot.id).await.unwrap().flags, Some(2))
220 });
221 }
222}