diff --git a/youmubot-osu/src/discord/server_rank.rs b/youmubot-osu/src/discord/server_rank.rs index 2af0c9a..de503b3 100644 --- a/youmubot-osu/src/discord/server_rank.rs +++ b/youmubot-osu/src/discord/server_rank.rs @@ -46,7 +46,7 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR .single::() .unwrap_or(ModeOrTotal::Mode(Mode::Std)); let guild = m.guild_id.expect("Guild-only command"); - // let member_cache = data.get::().unwrap(); + let member_cache = data.get::().unwrap(); let osu_users = data .get::() .unwrap() @@ -55,30 +55,22 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR .into_iter() .map(|v| (v.user_id, v)) .collect::>(); - let users = guild - .members_iter(ctx) - .filter_map(|m| { - future::ready( - m.ok() - .and_then(|m| osu_users.get(&m.user.id).map(|ou| (m, ou))), - ) - }) + let users = member_cache + .query_members(&ctx, guild) + .await? + .iter() + .filter_map(|m| osu_users.get(&m.user.id).map(|ou| (m, ou))) .filter_map(|(member, osu_user)| { - future::ready(|| -> Option<_> { - let pp = match mode { - ModeOrTotal::Total - if osu_user.pp.iter().any(|v| v.is_some_and(|v| v > 0.0)) => - { - Some(osu_user.pp.iter().map(|v| v.unwrap_or(0.0)).sum()) - } - ModeOrTotal::Mode(m) => osu_user.pp.get(m as usize).and_then(|v| *v), - _ => None, - }?; - Some((pp, member.user.name, osu_user)) - }()) + let pp = match mode { + ModeOrTotal::Total if osu_user.pp.iter().any(|v| v.is_some_and(|v| v > 0.0)) => { + Some(osu_user.pp.iter().map(|v| v.unwrap_or(0.0)).sum()) + } + ModeOrTotal::Mode(m) => osu_user.pp.get(m as usize).and_then(|v| *v), + _ => None, + }?; + Some((pp, member.user.name.clone(), osu_user)) }) - .collect::>() - .await; + .collect::>(); let last_update = users.iter().map(|(_, _, a)| a.last_update).min(); let mut users = users .into_iter() @@ -197,6 +189,7 @@ pub async fn show_leaderboard(ctx: &Context, m: &Message, mut args: Args) -> Com let style = args.single::().unwrap_or_default(); let data = ctx.data.read().await; + let member_cache = data.get::().unwrap(); let (bm, _) = match super::load_beatmap(ctx, m).await { Some((bm, mods_def)) => { @@ -230,17 +223,11 @@ pub async fn show_leaderboard(ctx: &Context, m: &Message, mut args: Args) -> Com .into_iter() .map(|v| (v.user_id, v)) .collect::>(); - let mut scores = guild - .members_iter(&ctx) - .filter_map(|mem| { - future::ready( - mem.ok() - .and_then(|m| osu_users.get(&m.user.id).map(|ou| (m.distinct(), ou.id))), - ) - }) - .collect::>() - .await - .into_iter() + let mut scores = member_cache + .query_members(&ctx, guild) + .await? + .iter() + .filter_map(|m| osu_users.get(&m.user.id).map(|ou| (m.distinct(), ou.id))) .map(|(mem, osu_id)| { osu.scores(bm.0.beatmap_id, move |f| { f.user(UserID::ID(osu_id)).mode(bm.1) diff --git a/youmubot-prelude/src/member_cache.rs b/youmubot-prelude/src/member_cache.rs index 467a0ef..dc13acb 100644 --- a/youmubot-prelude/src/member_cache.rs +++ b/youmubot-prelude/src/member_cache.rs @@ -5,19 +5,15 @@ use serenity::model::{ id::{GuildId, UserId}, }; use serenity::{http::CacheHttp, prelude::*}; -use std::collections::HashMap as Map; use std::sync::Arc; -use crate::OkPrint; - const VALID_CACHE_SECONDS: i64 = 15 * 60; // 15 minutes /// MemberCache resolves `does User belong to Guild` requests, and store them in a cache. #[derive(Debug, Default)] pub struct MemberCache { per_user: DashMap<(UserId, GuildId), Expiring>>, - per_guild: DashMap>>, - guild_counts: DashMap>, + per_guild: DashMap>>, } #[derive(Debug)] @@ -50,62 +46,6 @@ impl MemberCache { cache_http: impl CacheHttp, user_id: UserId, guild_id: GuildId, - ) -> Option { - let members_count = match self.guild_counts.get(&guild_id) { - Some(v) => v.clone(), - None => { - let res = guild_id - .to_partial_guild_with_counts(cache_http.http()) - .await - .ok() - .and_then(|v| v.approximate_member_count); - self.guild_counts.insert(guild_id, res); - res - } - }; - match members_count { - Some(g) if g <= 1000 => self.query_per_guild(cache_http, user_id, guild_id).await, - _ => self.query_per_user(cache_http, user_id, guild_id).await, - } - } - - async fn query_per_guild( - &self, - cache_http: impl CacheHttp, - user_id: UserId, - guild_id: GuildId, - ) -> Option { - let now = Utc::now(); - // Check cache - if let Some(r) = self.per_guild.get(&guild_id) { - if r.timeout > now { - return r.get(&user_id).cloned(); - } - } - // query - let members = guild_id - .members(cache_http.http(), None, None) - .await - .pls_ok()? - .into_iter() - .map(|m| (m.user.id, m)) - .collect::>(); - let result = members.get(&user_id).cloned(); - self.per_guild.insert( - guild_id, - Expiring::new( - members, - now + chrono::Duration::seconds(VALID_CACHE_SECONDS), - ), - ); - result - } - - async fn query_per_user( - &self, - cache_http: impl CacheHttp, - user_id: UserId, - guild_id: GuildId, ) -> Option { let now = Utc::now(); // Check cache @@ -125,4 +65,31 @@ impl MemberCache { ); t } + + pub async fn query_members( + &self, + cache_http: impl CacheHttp, + guild_id: GuildId, + ) -> crate::Result> { + let now = Utc::now(); + // Check cache + if let Some(r) = self.per_guild.get(&guild_id) { + if r.timeout > now { + return Ok(r.value.clone()); + } + } + // query + let members: Arc<[Member]> = guild_id + .members(cache_http.http(), None, None) + .await? + .into(); + self.per_guild.insert( + guild_id, + Expiring::new( + members.clone(), + now + chrono::Duration::seconds(VALID_CACHE_SECONDS), + ), + ); + Ok(members) + } }