diff --git a/Cargo.lock b/Cargo.lock index a6e24ed..33fe5f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2992,6 +2992,7 @@ dependencies = [ "dashmap", "lazy_static", "osuparse", + "rand", "regex", "reqwest", "rosu-pp", diff --git a/youmubot-osu/Cargo.toml b/youmubot-osu/Cargo.toml index 650fefb..02db6f6 100644 --- a/youmubot-osu/Cargo.toml +++ b/youmubot-osu/Cargo.toml @@ -19,6 +19,7 @@ rosu-pp = "0.9.1" serde = { version = "1.0.137", features = ["derive"] } serenity = "0.11.2" zip = "0.6.2" +rand = "0.8" youmubot-db = { path = "../youmubot-db" } youmubot-db-sql = { path = "../youmubot-db-sql" } diff --git a/youmubot-osu/src/discord/mod.rs b/youmubot-osu/src/discord/mod.rs index fe1ef44..8c7aeaf 100644 --- a/youmubot-osu/src/discord/mod.rs +++ b/youmubot-osu/src/discord/mod.rs @@ -6,6 +6,7 @@ use crate::{ request::{BeatmapRequestKind, UserID}, Client as OsuHttpClient, }; +use rand::seq::IteratorRandom; use serenity::{ collector::{CollectReaction, ReactionAction}, framework::standard::{ @@ -26,11 +27,9 @@ pub(crate) mod display; pub(crate) mod embeds; mod hook; pub(crate) mod oppai_cache; -mod register_user; mod server_rank; -use db::OsuUser; -use db::{OsuLastBeatmap, OsuSavedUsers, OsuUserBests}; +use db::{OsuLastBeatmap, OsuSavedUsers, OsuUser, OsuUserBests}; use embeds::{beatmap_embed, score_embed, user_embed}; use hook::SHORT_LINK_REGEX; pub use hook::{dot_osu_hook, hook}; @@ -176,85 +175,105 @@ pub async fn save(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult let data = ctx.data.read().await; let osu = data.get::().unwrap(); - async fn check(client: &OsuHttpClient, u: &User) -> Result { - let check_beatmap_id = register_user::user_register_beatmap_id(&u); + let user = args.single::()?; + let u = match osu.user(UserID::Auto(user), |f| f).await? { + Some(u) => u, + None => { + msg.reply(&ctx, "user not found...").await?; + return Ok(()); + } + }; + async fn find_score(client: &OsuHttpClient, u: &User) -> Result> { + for mode in &[Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania] { + let scores = client + .user_best(UserID::ID(u.id), |f| f.mode(*mode)) + .await?; + match scores.into_iter().choose(&mut rand::thread_rng()) { + Some(v) => return Ok(Some((v, *mode))), + None => (), + }; + } + Ok(None) + } + let (score, mode) = match find_score(&osu, &u).await? { + Some(v) => v, + None => { + msg.reply( + &ctx, + "No plays found in this account! Play something first...!", + ) + .await?; + return Ok(()); + } + }; + + async fn check(client: &OsuHttpClient, u: &User, map_id: u64) -> Result { Ok(client .user_recent(UserID::ID(u.id), |f| f.mode(Mode::Std).limit(1)) .await? .into_iter() .take(1) - .any(|s| s.beatmap_id == check_beatmap_id)) + .any(|s| s.beatmap_id == map_id)) } - let user = args.single::()?; - let user: Option = osu.user(UserID::Auto(user), |f| f).await?; - match user { - Some(u) => { - if !check(&osu, &u).await? { - let check_beatmap_id = register_user::user_register_beatmap_id(&u); - let reply = msg.reply(&ctx, format!("To set your osu username, please make your most recent play be the following map: `/b/{}` in **osu! standard** mode! It does **not** have to be a pass, and **NF** can be used! React to this message with 👌 within 5 minutes when you're done!", check_beatmap_id)); - let beatmap = osu - .beatmaps( - crate::request::BeatmapRequestKind::Beatmap(check_beatmap_id), - |f| f, - ) - .await? - .into_iter() - .next() - .unwrap(); - let info = data - .get::() - .unwrap() - .get_beatmap(beatmap.beatmap_id) - .await? - .get_possible_pp_with(Mode::Std, Mods::NOMOD)?; - let mut reply = reply.await?; - reply - .edit(&ctx, |f| { - f.embed(|e| beatmap_embed(&beatmap, Mode::Std, Mods::NOMOD, info, e)) - }) - .await?; - let reaction = reply.react(&ctx, '👌').await?; - let completed = loop { - let emoji = reaction.emoji.clone(); - let user_reaction = CollectReaction::new(&ctx) - .message_id(reply.id.0) - .author_id(msg.author.id.0) - .filter(move |r| r.emoji == emoji) - .timeout(std::time::Duration::from_secs(300)) - .collect_limit(1) - .await; - if let Some(ur) = user_reaction { - if check(&osu, &u).await? { - break true; - } - if let ReactionAction::Added(ur) = &*ur { - ur.delete(&ctx).await?; - } - } else { - break false; - } - }; - if !completed { - reaction.delete(&ctx).await?; - return Ok(()); - } + let reply = msg.reply(&ctx, format!("To set your osu username, please make your most recent play be the following map: `/b/{}` in **{}** mode! It does **not** have to be a pass, and **NF** can be used! React to this message with 👌 within 5 minutes when you're done!", score.beatmap_id, mode.as_str_new_site())); + let beatmap = osu + .beatmaps( + crate::request::BeatmapRequestKind::Beatmap(score.beatmap_id), + |f| f.mode(mode, true), + ) + .await? + .into_iter() + .next() + .unwrap(); + let info = data + .get::() + .unwrap() + .get_beatmap(beatmap.beatmap_id) + .await? + .get_possible_pp_with(mode, Mods::NOMOD)?; + let mut reply = reply.await?; + reply + .edit(&ctx, |f| { + f.embed(|e| beatmap_embed(&beatmap, mode, Mods::NOMOD, info, e)) + }) + .await?; + let reaction = reply.react(&ctx, '👌').await?; + let completed = loop { + let emoji = reaction.emoji.clone(); + let user_reaction = CollectReaction::new(&ctx) + .message_id(reply.id.0) + .author_id(msg.author.id.0) + .filter(move |r| r.emoji == emoji) + .timeout(std::time::Duration::from_secs(300)) + .collect_limit(1) + .await; + if let Some(ur) = user_reaction { + if check(&osu, &u, score.beatmap_id).await? { + break true; } - let username = u.username.clone(); - add_user(msg.author.id, u, &data).await?; - msg.reply( - &ctx, - MessageBuilder::new() - .push("user has been set to ") - .push_mono_safe(username) - .build(), - ) - .await?; - } - None => { - msg.reply(&ctx, "user not found...").await?; + if let ReactionAction::Added(ur) = &*ur { + ur.delete(&ctx).await?; + } + } else { + break false; } + }; + if !completed { + reaction.delete(&ctx).await?; + return Ok(()); } + + let username = u.username.clone(); + add_user(msg.author.id, u, &data).await?; + msg.reply( + &ctx, + MessageBuilder::new() + .push("user has been set to ") + .push_mono_safe(username) + .build(), + ) + .await?; Ok(()) } diff --git a/youmubot-osu/src/discord/register_user.rs b/youmubot-osu/src/discord/register_user.rs deleted file mode 100644 index 6f655f1..0000000 --- a/youmubot-osu/src/discord/register_user.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::models::User; - -const BEATMAP_IDS: [u64; 100] = [ - 2469345, 2084862, 2486881, 2330357, 2546607, 1655981, 1626537, 888015, 1062394, 1319547, - 1852572, 1944926, 2129143, 1057509, 2022718, 1097543, 1736329, 1056207, 930249, 1936782, - 1919312, 1570203, 2201460, 1495498, 965549, 2428358, 2118444, 1849433, 820619, 999944, 1571309, - 1055147, 1619555, 338682, 1438917, 954692, 824891, 2026320, 764014, 2237466, 2058788, 1969946, - 1892257, 1473301, 2336704, 774965, 657509, 1031604, 898576, 714001, 1872396, 831705, 1917082, - 978326, 795232, 1814494, 713867, 2077126, 1612329, 1314214, 1849273, 1829925, 1640362, 801158, - 431957, 1054501, 1627148, 816600, 1857519, 1080094, 1642274, 1232440, 1843653, 953586, 2044362, - 1489536, 951053, 1069111, 2154507, 1007699, 1099936, 1077323, 1874119, 909032, 760466, 1911308, - 1820921, 1231520, 954254, 425779, 1586059, 2198684, 1040044, 799913, 994933, 969681, 888016, - 1100327, 1063410, 2078961, -]; - -pub fn user_register_beatmap_id(u: &User) -> u64 { - let now = chrono::Utc::now(); - BEATMAP_IDS[(u.id + (now.timestamp() / 3600) as u64) as usize % BEATMAP_IDS.len()] -}