mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-16 07:18:54 +00:00
Implement contestranks
This commit is contained in:
parent
4f08eb0caf
commit
a1d453cfff
1 changed files with 134 additions and 1 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use codeforces::Contest;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
|
@ -6,6 +7,7 @@ use serenity::{
|
||||||
model::channel::Message,
|
model::channel::Message,
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
};
|
};
|
||||||
|
use std::{collections::HashMap, time::Duration};
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
mod announcer;
|
mod announcer;
|
||||||
|
@ -31,7 +33,7 @@ pub fn setup(path: &std::path::Path, data: &mut ShareMap, announcers: &mut Annou
|
||||||
#[group]
|
#[group]
|
||||||
#[prefix = "cf"]
|
#[prefix = "cf"]
|
||||||
#[description = "Codeforces-related commands"]
|
#[description = "Codeforces-related commands"]
|
||||||
#[commands(profile, save, ranks, watch)]
|
#[commands(profile, save, ranks, watch, contestranks)]
|
||||||
#[default_command(profile)]
|
#[default_command(profile)]
|
||||||
pub struct Codeforces;
|
pub struct Codeforces;
|
||||||
|
|
||||||
|
@ -209,6 +211,137 @@ pub fn ranks(ctx: &mut Context, m: &Message) -> CommandResult {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
#[description = "See the server ranks on a certain contest"]
|
||||||
|
#[usage = "[the contest id]"]
|
||||||
|
#[num_args(1)]
|
||||||
|
#[only_in(guilds)]
|
||||||
|
pub fn contestranks(ctx: &mut Context, m: &Message, mut args: Args) -> CommandResult {
|
||||||
|
let contest_id: u64 = args.single()?;
|
||||||
|
let guild = m.guild_id.unwrap(); // Guild-only command
|
||||||
|
let members = CfSavedUsers::open(&*ctx.data.read()).borrow()?.clone();
|
||||||
|
let members = members
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(user_id, cf_user)| {
|
||||||
|
guild
|
||||||
|
.member(&ctx, user_id)
|
||||||
|
.ok()
|
||||||
|
.map(|v| (cf_user.handle, v))
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
let http = ctx.data.get_cloned::<HTTPClient>();
|
||||||
|
let (contest, problems, ranks) = Contest::standings(&http, contest_id, |f| {
|
||||||
|
f.handles(members.iter().map(|(k, _)| k.clone()).collect())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Table me
|
||||||
|
let ranks = ranks
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| {
|
||||||
|
v.party
|
||||||
|
.members
|
||||||
|
.iter()
|
||||||
|
.filter_map(|m| members.get(&m.handle).map(|mem| (mem, m.handle.clone(), v)))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if ranks.is_empty() {
|
||||||
|
m.reply(&ctx, "No one in this server participated in the contest...")?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE: usize = 10;
|
||||||
|
let total_pages = (ranks.len() + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
|
||||||
|
|
||||||
|
ctx.data.get_cloned::<ReactionWatcher>().paginate_fn(
|
||||||
|
ctx.clone(),
|
||||||
|
m.channel_id,
|
||||||
|
move |page, e| {
|
||||||
|
let page = page as usize;
|
||||||
|
let start = page * ITEMS_PER_PAGE;
|
||||||
|
let end = total_pages.min(start + ITEMS_PER_PAGE);
|
||||||
|
if start >= end {
|
||||||
|
return (e, Err(Error::from("no more pages to show")));
|
||||||
|
}
|
||||||
|
let ranks = &ranks[start..end];
|
||||||
|
let hw = ranks
|
||||||
|
.iter()
|
||||||
|
.map(|(mem, handle, _)| format!("{} ({})", handle, mem.distinct()).len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.max(6);
|
||||||
|
let hackw = ranks
|
||||||
|
.iter()
|
||||||
|
.map(|(_, _, row)| {
|
||||||
|
format!(
|
||||||
|
"{}/{}",
|
||||||
|
row.successful_hack_count, row.unsuccessful_hack_count
|
||||||
|
)
|
||||||
|
.len()
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.max(5);
|
||||||
|
|
||||||
|
let mut table = MessageBuilder::new();
|
||||||
|
let mut header = MessageBuilder::new();
|
||||||
|
// Header
|
||||||
|
header.push(format!(
|
||||||
|
" Rank | {:hw$} | Total | {:hackw$}",
|
||||||
|
"Handle",
|
||||||
|
"Hacks",
|
||||||
|
hw = hw,
|
||||||
|
hackw = hackw
|
||||||
|
));
|
||||||
|
for p in &problems {
|
||||||
|
header.push(format!(" | {:4}", p.index));
|
||||||
|
}
|
||||||
|
let header = header.build();
|
||||||
|
table
|
||||||
|
.push_line(&header)
|
||||||
|
.push_line(format!("{:-<w$}", "", w = header.len()));
|
||||||
|
|
||||||
|
// Body
|
||||||
|
for (mem, handle, row) in ranks {
|
||||||
|
table.push(format!(
|
||||||
|
"{:>5} | {:<hw$} | {:>5.0} | {:<hackw$}",
|
||||||
|
row.rank,
|
||||||
|
format!("{} ({})", handle, mem.distinct()),
|
||||||
|
row.points,
|
||||||
|
format!(
|
||||||
|
"{}/{}",
|
||||||
|
row.successful_hack_count, row.unsuccessful_hack_count
|
||||||
|
),
|
||||||
|
hw = hw,
|
||||||
|
hackw = hackw
|
||||||
|
));
|
||||||
|
for p in &row.problem_results {
|
||||||
|
table.push(" | ");
|
||||||
|
if p.points > 0.0 {
|
||||||
|
table.push(format!("{:^4.0}", p.points));
|
||||||
|
} else if let Some(_) = p.best_submission_time_seconds {
|
||||||
|
table.push(format!("{:^4}", "?"));
|
||||||
|
} else if p.rejected_attempt_count > 0 {
|
||||||
|
table.push(format!("{:^4}", format!("-{}", p.rejected_attempt_count)));
|
||||||
|
} else {
|
||||||
|
table.push(format!("{:^4}", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut m = MessageBuilder::new();
|
||||||
|
m.push_bold_safe(&contest.name)
|
||||||
|
.push(" ")
|
||||||
|
.push_line(contest.url())
|
||||||
|
.push_codeblock(table.build(), None)
|
||||||
|
.push_line(format!("Page **{}/{}**", page + 1, total_pages));
|
||||||
|
(e.content(m.build()), Ok(()))
|
||||||
|
},
|
||||||
|
Duration::from_secs(60),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
#[description = "Watch a contest and announce any change on the members of the server assigned to the contest."]
|
#[description = "Watch a contest and announce any change on the members of the server assigned to the contest."]
|
||||||
#[usage = "[the contest id]"]
|
#[usage = "[the contest id]"]
|
||||||
|
|
Loading…
Add table
Reference in a new issue