diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 84a4cdc4..ff79a6cb 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -307,7 +307,7 @@ impl State { Ok(EventV1::Ready { users: if fields.users { Some(users) } else { None }, servers: if fields.servers { - Some(servers.into_iter().map(Into::into).collect()) + Some(join_all(servers.into_iter().map(|server| server.into(db))).await) } else { None }, diff --git a/crates/core/database/src/models/server_members/model.rs b/crates/core/database/src/models/server_members/model.rs index 158874e6..f452564b 100644 --- a/crates/core/database/src/models/server_members/model.rs +++ b/crates/core/database/src/models/server_members/model.rs @@ -176,7 +176,7 @@ impl Member { EventV1::ServerCreate { id: server.id.clone(), - server: server.clone().into(), + server: server.clone().into(db).await, channels: channels .clone() .into_iter() @@ -317,9 +317,15 @@ impl Member { }) { match intention { - RemovalIntention::Leave => SystemMessage::UserLeft { id: self.id.user.clone() }, - RemovalIntention::Kick => SystemMessage::UserKicked { id: self.id.user.clone() }, - RemovalIntention::Ban => SystemMessage::UserBanned { id: self.id.user.clone() }, + RemovalIntention::Leave => SystemMessage::UserLeft { + id: self.id.user.clone(), + }, + RemovalIntention::Kick => SystemMessage::UserKicked { + id: self.id.user.clone(), + }, + RemovalIntention::Ban => SystemMessage::UserBanned { + id: self.id.user.clone(), + }, } .into_message(id.to_string()) // TODO: support notifications here in the future? diff --git a/crates/core/database/src/models/servers/model.rs b/crates/core/database/src/models/servers/model.rs index 59b15000..65d37929 100644 --- a/crates/core/database/src/models/servers/model.rs +++ b/crates/core/database/src/models/servers/model.rs @@ -1,5 +1,10 @@ use std::collections::{HashMap, HashSet}; +use redis_kiss::{ + get_connection, + redis::{SetExpiry, SetOptions}, + AsyncCommands, +}; use revolt_models::v0::{self, DataCreateServerChannel}; use revolt_permissions::{OverrideField, DEFAULT_PERMISSION_SERVER}; use revolt_result::Result; @@ -322,6 +327,30 @@ impl Server { Ok(()) } + + /// Gets a approximate count of the members in this server + /// + /// this value is cached for one hour + pub async fn get_approximate_member_count(&self, db: &Database) -> usize { + let Ok(mut redis) = get_connection().await else { + return 0; + }; + let key = format!("member_count:{}", &self.id); + + if let Some(count) = redis.get::<_, Option>(&key).await.ok().flatten() { + count + } else { + let count = db.fetch_member_count(&self.id).await.unwrap_or(0); + let _ = redis + .set_options::<_, _, ()>( + &key, + count, + SetOptions::default().with_expiration(SetExpiry::EX(60 * 60)), + ) + .await; + count + } + } } impl Role { diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index acb0b4c5..65e90b73 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -751,30 +751,29 @@ impl From for RemovalIntention { } } -impl From for Server { - fn from(value: crate::Server) -> Self { +impl crate::Server { + pub async fn into(self, db: &Database) -> Server { + let approximate_member_count = self.get_approximate_member_count(db).await; + Server { - id: value.id, - owner: value.owner, - name: value.name, - description: value.description, - channels: value.channels, - categories: value + id: self.id, + owner: self.owner, + name: self.name, + description: self.description, + channels: self.channels, + categories: self .categories .map(|categories| categories.into_iter().map(|v| v.into()).collect()), - system_messages: value.system_messages.map(|v| v.into()), - roles: value - .roles - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect(), - default_permissions: value.default_permissions, - icon: value.icon.map(|f| f.into()), - banner: value.banner.map(|f| f.into()), - flags: value.flags.unwrap_or_default() as u32, - nsfw: value.nsfw, - analytics: value.analytics, - discoverable: value.discoverable, + system_messages: self.system_messages.map(|v| v.into()), + roles: self.roles.into_iter().map(|(k, v)| (k, v.into())).collect(), + default_permissions: self.default_permissions, + icon: self.icon.map(|f| f.into()), + banner: self.banner.map(|f| f.into()), + flags: self.flags.unwrap_or_default() as u32, + nsfw: self.nsfw, + analytics: self.analytics, + discoverable: self.discoverable, + approximate_member_count, } } } @@ -829,6 +828,7 @@ impl From for PartialServer { nsfw: value.nsfw, analytics: value.analytics, discoverable: value.discoverable, + approximate_member_count: None, } } } diff --git a/crates/core/models/src/v0/servers.rs b/crates/core/models/src/v0/servers.rs index 2aab765e..10d2eb1d 100644 --- a/crates/core/models/src/v0/servers.rs +++ b/crates/core/models/src/v0/servers.rs @@ -78,6 +78,9 @@ auto_derived_partial!( serde(skip_serializing_if = "crate::if_false", default) )] pub discoverable: bool, + + /// Approximate amount of members in the server + pub approximate_member_count: usize, }, "PartialServer" ); diff --git a/crates/delta/src/routes/invites/invite_fetch.rs b/crates/delta/src/routes/invites/invite_fetch.rs index 7c1b7550..b45b33ab 100644 --- a/crates/delta/src/routes/invites/invite_fetch.rs +++ b/crates/delta/src/routes/invites/invite_fetch.rs @@ -28,7 +28,7 @@ pub async fn fetch(db: &State, target: Reference<'_>) -> Result