From b0f70fb2a12e78026c3b27eca14762d9b8ca96a3 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Sun, 9 Feb 2020 15:02:13 -0500 Subject: [PATCH] Implement server ranks --- youmubot-cf/src/lib.rs | 102 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/youmubot-cf/src/lib.rs b/youmubot-cf/src/lib.rs index 949692b..3f2fc96 100644 --- a/youmubot-cf/src/lib.rs +++ b/youmubot-cf/src/lib.rs @@ -1,9 +1,10 @@ use serenity::{ framework::standard::{ macros::{command, group}, - Args, CommandResult, + Args, CommandError as Error, CommandResult, }, model::channel::Message, + utils::MessageBuilder, }; use youmubot_prelude::*; @@ -27,7 +28,7 @@ pub fn setup(path: &std::path::Path, data: &mut ShareMap) { #[group] #[prefix = "cf"] #[description = "Codeforces-related commands"] -#[commands(profile, save)] +#[commands(profile, save, ranks)] #[default_command(profile)] pub struct Codeforces; @@ -105,3 +106,100 @@ pub fn save(ctx: &mut Context, m: &Message, mut args: Args) -> CommandResult { Ok(()) } + +#[command] +#[description = "See the leaderboard of all people in the server."] +#[only_in(guilds)] +#[num_args(0)] +pub fn ranks(ctx: &mut Context, m: &Message) -> CommandResult { + let everyone = { + let db = CfSavedUsers::open(&*ctx.data.read()); + let db = db.borrow()?; + db.iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>() + }; + let guild = m.guild_id.expect("Guild-only command"); + let mut ranks = everyone + .into_iter() + .filter_map(|(id, cf_user)| guild.member(&ctx, id).ok().map(|mem| (mem, cf_user))) + .collect::>(); + ranks.sort_by(|(_, a), (_, b)| b.rating.unwrap_or(-1).cmp(&a.rating.unwrap_or(-1))); + + if ranks.is_empty() { + m.reply(&ctx, "No saved users in this server.")?; + return Ok(()); + } + + const ITEMS_PER_PAGE: usize = 10; + let total_pages = (ranks.len() + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE; + let last_updated = ranks.iter().map(|(_, cfu)| cfu.last_update).min().unwrap(); + + ctx.data.get_cloned::().paginate_fn( + ctx.clone(), + m.channel_id, + |page, e| { + let page = page as usize; + let start = ITEMS_PER_PAGE * page; + let end = ranks.len().min(start + ITEMS_PER_PAGE); + if start >= end { + return (e, Err(Error::from("No more pages"))); + } + let ranks = &ranks[start..end]; + + let handle_width = ranks.iter().map(|(_, cfu)| cfu.handle.len()).max().unwrap(); + let username_width = ranks + .iter() + .map(|(mem, _)| mem.distinct().len()) + .max() + .unwrap(); + + let mut m = MessageBuilder::new(); + m.push_line("```"); + + // Table header + m.push_line(format!( + "Rank | Rating | {:hw$} | {:uw$}", + "Handle", + "Username", + hw = handle_width, + uw = username_width + )); + m.push_line(format!( + "----------------{:->hw$}---{:->uw$}", + "", + "", + hw = handle_width, + uw = username_width + )); + + for (id, (mem, cfu)) in ranks.iter().enumerate() { + let id = id + start + 1; + m.push_line(format!( + "{:>4} | {:>6} | {:hw$} | {:uw$}", + format!("#{}", id), + cfu.rating + .map(|v| v.to_string()) + .unwrap_or("----".to_owned()), + cfu.handle, + mem.distinct(), + hw = handle_width, + uw = username_width + )); + } + + m.push_line("```"); + m.push(format!( + "Page **{}/{}**. Last updated **{}**", + page + 1, + total_pages, + last_updated.to_rfc2822() + )); + + (e.content(m.build()), Ok(())) + }, + std::time::Duration::from_secs(60), + )?; + + Ok(()) +}