mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-10 04:30:29 +00:00
Add ranks command
This commit is contained in:
parent
3f9db46032
commit
5d9fda682e
2 changed files with 95 additions and 37 deletions
|
@ -19,7 +19,8 @@ use serenity::all::User;
|
|||
"save",
|
||||
"forcesave",
|
||||
"beatmap",
|
||||
"check"
|
||||
"check",
|
||||
"ranks"
|
||||
)
|
||||
)]
|
||||
pub async fn osu<U: HasOsuEnv>(_ctx: CmdContext<'_, U>) -> Result<()> {
|
||||
|
@ -522,6 +523,33 @@ async fn check<U: HasOsuEnv>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Display the rankings of members in the server.
|
||||
#[poise::command(slash_command, guild_only)]
|
||||
async fn ranks<U: HasOsuEnv>(
|
||||
ctx: CmdContext<'_, U>,
|
||||
#[description = "Sort users by"] sort: Option<server_rank::RankQuery>,
|
||||
#[description = "Reverse the ordering"] reverse: Option<bool>,
|
||||
#[description = "The gamemode for the rankings"] mode: Option<Mode>,
|
||||
) -> Result<()> {
|
||||
let env = ctx.data().osu_env();
|
||||
let guild = ctx.partial_guild().await.unwrap();
|
||||
ctx.defer().await?;
|
||||
server_rank::do_server_ranks(
|
||||
ctx.clone().serenity_context(),
|
||||
env,
|
||||
&guild,
|
||||
mode,
|
||||
sort,
|
||||
reverse.unwrap_or(false),
|
||||
|s| async move {
|
||||
let m = ctx.reply(s).await?;
|
||||
Ok(m.into_message().await?)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arg_from_username_or_discord(
|
||||
username: Option<String>,
|
||||
discord_name: Option<User>,
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::{
|
|||
borrow::Cow,
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, HashMap},
|
||||
future::Future,
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
@ -9,7 +10,7 @@ use std::{
|
|||
use chrono::DateTime;
|
||||
use pagination::paginate_with_first_message;
|
||||
use serenity::{
|
||||
all::{GuildId, Member},
|
||||
all::{GuildId, Member, PartialGuild},
|
||||
builder::EditMessage,
|
||||
framework::standard::{macros::command, Args, CommandResult},
|
||||
model::channel::Message,
|
||||
|
@ -32,15 +33,16 @@ use crate::{
|
|||
|
||||
use super::{ModeArg, OsuEnv};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
enum RankQuery {
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, poise::ChoiceParameter)]
|
||||
pub(crate) enum RankQuery {
|
||||
#[default]
|
||||
PP,
|
||||
#[name = "Total PP"]
|
||||
TotalPP,
|
||||
#[name = "Weighted Map Length"]
|
||||
MapLength,
|
||||
MapAge {
|
||||
newest_first: bool,
|
||||
},
|
||||
#[name = "Map Age"]
|
||||
MapAge,
|
||||
}
|
||||
|
||||
impl RankQuery {
|
||||
|
@ -49,13 +51,13 @@ impl RankQuery {
|
|||
RankQuery::PP => "pp",
|
||||
RankQuery::TotalPP => "Total pp",
|
||||
RankQuery::MapLength => "Map length",
|
||||
RankQuery::MapAge { newest_first: _ } => "Map age",
|
||||
RankQuery::MapAge => "Map age",
|
||||
}
|
||||
}
|
||||
fn pass_pp_limit(&self, mode: Mode, ou: &OsuUser) -> bool {
|
||||
match self {
|
||||
RankQuery::PP | RankQuery::TotalPP => true,
|
||||
RankQuery::MapAge { newest_first: _ } | RankQuery::MapLength => {
|
||||
RankQuery::MapAge | RankQuery::MapLength => {
|
||||
ou.modes.get(&mode).is_some_and(|v| v.pp >= 500.0)
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +83,7 @@ impl RankQuery {
|
|||
format!("{}m{:05.2}s", minutes, seconds).into()
|
||||
})
|
||||
.unwrap_or_else(|| "-".into()),
|
||||
RankQuery::MapAge { newest_first: _ } => ou
|
||||
RankQuery::MapAge => ou
|
||||
.modes
|
||||
.get(&mode)
|
||||
.and_then(|v| DateTime::from_timestamp(v.map_age, 0))
|
||||
|
@ -99,10 +101,7 @@ impl FromStr for RankQuery {
|
|||
"pp" => Ok(RankQuery::PP),
|
||||
"total" | "total-pp" => Ok(RankQuery::TotalPP),
|
||||
"map-length" => Ok(RankQuery::MapLength),
|
||||
"age" | "map-age" => Ok(RankQuery::MapAge { newest_first: true }),
|
||||
"old" | "age-old" | "map-age-old" => Ok(RankQuery::MapAge {
|
||||
newest_first: false,
|
||||
}),
|
||||
"age" | "map-age" => Ok(RankQuery::MapAge),
|
||||
_ => Err(format!("not a query: {}", s)),
|
||||
}
|
||||
}
|
||||
|
@ -115,10 +114,38 @@ impl FromStr for RankQuery {
|
|||
#[only_in(guilds)]
|
||||
pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandResult {
|
||||
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
||||
let mode = args.find::<ModeArg>().map(|v| v.0).unwrap_or(Mode::Std);
|
||||
let query = args.find::<RankQuery>().unwrap_or_default();
|
||||
let guild = m.guild_id.expect("Guild-only command");
|
||||
let mode = args.find::<ModeArg>().map(|v| v.0).ok();
|
||||
let query = args.find::<RankQuery>().ok();
|
||||
let guild = m
|
||||
.guild_id
|
||||
.expect("Guild-only command")
|
||||
.to_partial_guild(&ctx)
|
||||
.await?;
|
||||
|
||||
let ctxc = ctx.clone();
|
||||
do_server_ranks(ctx, &env, &guild, mode, query, false, |msg| async {
|
||||
let m = m.reply(&ctxc, msg).await?;
|
||||
Ok(m) as Result<_>
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn do_server_ranks<T>(
|
||||
ctx: &Context,
|
||||
env: &OsuEnv,
|
||||
guild: &PartialGuild,
|
||||
mode: Option<Mode>,
|
||||
query: Option<RankQuery>,
|
||||
reverse: bool,
|
||||
mk_initial_message: impl FnOnce(String) -> T,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Future<Output = Result<Message>>,
|
||||
{
|
||||
let mode = mode.unwrap_or(Mode::Std);
|
||||
let query = query.unwrap_or(RankQuery::PP);
|
||||
let mut users = env
|
||||
.saved_users
|
||||
.all()
|
||||
|
@ -130,7 +157,7 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR
|
|||
let mut users = env
|
||||
.prelude
|
||||
.members
|
||||
.query_members(&ctx, guild)
|
||||
.query_members(&ctx, guild.id)
|
||||
.await?
|
||||
.iter()
|
||||
.filter_map(|m| users.remove(&m.user.id).map(|ou| (m.clone(), ou)))
|
||||
|
@ -173,35 +200,41 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR
|
|||
.unwrap()
|
||||
.reverse()
|
||||
}),
|
||||
RankQuery::MapAge { newest_first } => Box::new(move |(_, a), (_, b)| {
|
||||
let r = a
|
||||
.modes
|
||||
RankQuery::MapAge => Box::new(move |(_, a), (_, b)| {
|
||||
a.modes
|
||||
.get(&mode)
|
||||
.map(|v| v.map_age)
|
||||
.partial_cmp(&b.modes.get(&mode).map(|v| v.map_age))
|
||||
.unwrap();
|
||||
if newest_first {
|
||||
r.reverse()
|
||||
} else {
|
||||
r
|
||||
}
|
||||
.unwrap()
|
||||
}),
|
||||
};
|
||||
users.sort_unstable_by(sort_fn);
|
||||
if reverse {
|
||||
users.reverse();
|
||||
}
|
||||
|
||||
if users.is_empty() {
|
||||
m.reply(&ctx, "No saved users in the current server...")
|
||||
.await?;
|
||||
mk_initial_message("No saved users in the current server...".to_owned()).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let header = format!(
|
||||
"Rankings for **{}**, ordered by _{}{}_",
|
||||
guild.name,
|
||||
query.col_name(),
|
||||
if reverse { " (reversed)" } else { "" },
|
||||
);
|
||||
|
||||
let msg = mk_initial_message(header.clone()).await?;
|
||||
|
||||
const ITEMS_PER_PAGE: usize = 10;
|
||||
let users = Arc::new(users);
|
||||
let last_update = last_update.unwrap();
|
||||
let total_len = users.len();
|
||||
let total_pages = (total_len + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
|
||||
paginate_reply(
|
||||
paginate_with_first_message(
|
||||
paginate_from_fn(move |page: u8, ctx: &Context, m: &mut Message| {
|
||||
let header = header.clone();
|
||||
use Align::*;
|
||||
let users = users.clone();
|
||||
Box::pin(async move {
|
||||
|
@ -212,7 +245,7 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR
|
|||
}
|
||||
let users = &users[start..end];
|
||||
let table = match query {
|
||||
RankQuery::MapAge { newest_first: _ } | RankQuery::MapLength => {
|
||||
RankQuery::MapAge | RankQuery::MapLength => {
|
||||
let headers = ["#", query.col_name(), "pp", "Username", "Member"];
|
||||
const ALIGNS: [Align; 5] = [Right, Right, Right, Left, Left];
|
||||
|
||||
|
@ -244,11 +277,7 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR
|
|||
format!("{}", 1 + i + start),
|
||||
RankQuery::PP.extract_row(mode, ou).to_string(),
|
||||
RankQuery::MapLength.extract_row(mode, ou).to_string(),
|
||||
(RankQuery::MapAge {
|
||||
newest_first: false,
|
||||
})
|
||||
.extract_row(mode, ou)
|
||||
.to_string(),
|
||||
RankQuery::MapAge.extract_row(mode, ou).to_string(),
|
||||
ou.username.to_string(),
|
||||
mem.distinct(),
|
||||
]
|
||||
|
@ -276,6 +305,7 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR
|
|||
}
|
||||
};
|
||||
let content = MessageBuilder::new()
|
||||
.push_line(header)
|
||||
.push_line(table)
|
||||
.push_line(format!(
|
||||
"Page **{}**/**{}**. Last updated: {}",
|
||||
|
@ -290,7 +320,7 @@ pub async fn server_rank(ctx: &Context, m: &Message, mut args: Args) -> CommandR
|
|||
})
|
||||
.with_page_count(total_pages),
|
||||
ctx,
|
||||
m,
|
||||
msg,
|
||||
std::time::Duration::from_secs(60),
|
||||
)
|
||||
.await?;
|
||||
|
|
Loading…
Add table
Reference in a new issue