diff --git a/youmubot-osu/src/discord/announcer.rs b/youmubot-osu/src/discord/announcer.rs index d631b3c..d20bf4c 100644 --- a/youmubot-osu/src/discord/announcer.rs +++ b/youmubot-osu/src/discord/announcer.rs @@ -10,6 +10,7 @@ use rayon::prelude::*; use serenity::{ framework::standard::{CommandError as Error, CommandResult}, http::CacheHttp, + model::id::{ChannelId, UserId}, CacheAndHttp, }; use std::sync::Arc; @@ -23,53 +24,69 @@ pub fn updates(c: Arc, d: AppData, channels: MemberToChannels) -> let osu = d.get_cloned::(); // For each user... let mut data = OsuSavedUsers::open(&*d.read()).borrow()?.clone(); - 'user_loop: for (user_id, osu_user) in data.iter_mut() { - let mut pp_values = vec![]; // Store the pp values here... - for mode in &[Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania] { - let scores = scan_user(&osu, osu_user, *mode)?; - let user = match osu.user(UserID::ID(osu_user.id), |f| f.mode(*mode)) { - Ok(Some(u)) => u, - _ => continue 'user_loop, - }; - pp_values.push(user.pp); - if scores.is_empty() && !osu_user.pp.is_empty() { - // Nothing to update: no new scores and pp is there. + for (user_id, osu_user) in data.iter_mut() { + let channels = channels.channels_of(c.clone(), *user_id); + if channels.is_empty() { + continue; // We don't wanna update an user without any active server + } + osu_user.pp = match (&[Mode::Std, Mode::Taiko, Mode::Catch, Mode::Mania]) + .par_iter() + .map(|m| handle_user_mode(c.clone(), &osu, &osu_user, *user_id, &channels[..], *m)) + .collect::>() + { + Ok(v) => v, + Err(e) => { + eprintln!("osu: Cannot update {}: {}", osu_user.id, e.0); continue; } - scores - .into_par_iter() - .filter_map(|(rank, score)| { - let beatmap = osu - .beatmaps(BeatmapRequestKind::Beatmap(score.beatmap_id), |f| f) - .map(|v| BeatmapWithMode(v.into_iter().next().unwrap(), *mode)); - let channels = channels.channels_of(c.clone(), *user_id); - match beatmap { - Ok(v) => Some((rank, score, v, channels)), - Err(e) => { - dbg!(e); - None - } - } - }) - .for_each(|(rank, score, beatmap, channels)| { - for channel in channels { - if let Err(e) = channel.send_message(c.http(), |c| { - c.content(format!("New top record from {}!", user_id.mention())) - .embed(|e| score_embed(&score, &beatmap, &user, Some(rank), e)) - }) { - dbg!(e); - } - } - }); - } + }; osu_user.last_update = chrono::Utc::now(); - osu_user.pp = pp_values; } // Update users *OsuSavedUsers::open(&*d.read()).borrow_mut()? = data; Ok(()) } +/// Handles an user/mode scan, announces all possible new scores, return the new pp value. +fn handle_user_mode( + c: Arc, + osu: &Osu, + osu_user: &OsuUser, + user_id: UserId, + channels: &[ChannelId], + mode: Mode, +) -> Result, Error> { + let scores = scan_user(osu, osu_user, mode)?; + let user = osu + .user(UserID::ID(osu_user.id), |f| f.mode(mode))? + .ok_or(Error::from("user not found"))?; + scores + .into_par_iter() + .filter_map(|(rank, score)| { + let beatmap = osu + .beatmaps(BeatmapRequestKind::Beatmap(score.beatmap_id), |f| f) + .map(|v| BeatmapWithMode(v.into_iter().next().unwrap(), mode)); + match beatmap { + Ok(v) => Some((rank, score, v)), + Err(e) => { + dbg!(e); + None + } + } + }) + .for_each(|(rank, score, beatmap)| { + for channel in (&channels).iter() { + if let Err(e) = channel.send_message(c.http(), |c| { + c.content(format!("New top record from {}!", user_id.mention())) + .embed(|e| score_embed(&score, &beatmap, &user, Some(rank), e)) + }) { + dbg!(e); + } + } + }); + Ok(user.pp) +} + fn scan_user(osu: &Osu, u: &OsuUser, mode: Mode) -> Result, Error> { let scores = osu.user_best(UserID::ID(u.id), |f| f.mode(mode).limit(25))?; let scores = scores diff --git a/youmubot-osu/src/models/parse.rs b/youmubot-osu/src/models/parse.rs index ba6651a..0d17431 100644 --- a/youmubot-osu/src/models/parse.rs +++ b/youmubot-osu/src/models/parse.rs @@ -75,23 +75,26 @@ impl TryFrom for User { username: raw.username, joined: parse_date(&raw.join_date)?, country: raw.country, - count_300: parse_from_str(&raw.count300)?, - count_100: parse_from_str(&raw.count100)?, - count_50: parse_from_str(&raw.count50)?, - play_count: parse_from_str(&raw.playcount)?, - played_time: parse_duration(&raw.total_seconds_played)?, - ranked_score: parse_from_str(&raw.ranked_score)?, - total_score: parse_from_str(&raw.total_score)?, - count_ss: parse_from_str(&raw.count_rank_ss)?, - count_ssh: parse_from_str(&raw.count_rank_ssh)?, - count_s: parse_from_str(&raw.count_rank_s)?, - count_sh: parse_from_str(&raw.count_rank_sh)?, - count_a: parse_from_str(&raw.count_rank_a)?, - rank: parse_from_str(&raw.pp_rank)?, - country_rank: parse_from_str(&raw.pp_country_rank)?, - level: parse_from_str(&raw.level)?, - pp: Some(parse_from_str(&raw.pp_raw)?).filter(|v| *v != 0.0), - accuracy: parse_from_str(&raw.accuracy)?, + count_300: raw.count300.map(parse_from_str).unwrap_or(Ok(0))?, + count_100: raw.count100.map(parse_from_str).unwrap_or(Ok(0))?, + count_50: raw.count50.map(parse_from_str).unwrap_or(Ok(0))?, + play_count: raw.playcount.map(parse_from_str).unwrap_or(Ok(0))?, + played_time: raw + .total_seconds_played + .map(parse_duration) + .unwrap_or(Ok(Duration::from_secs(0)))?, + ranked_score: raw.ranked_score.map(parse_from_str).unwrap_or(Ok(0))?, + total_score: raw.total_score.map(parse_from_str).unwrap_or(Ok(0))?, + count_ss: raw.count_rank_ss.map(parse_from_str).unwrap_or(Ok(0))?, + count_ssh: raw.count_rank_ssh.map(parse_from_str).unwrap_or(Ok(0))?, + count_s: raw.count_rank_s.map(parse_from_str).unwrap_or(Ok(0))?, + count_sh: raw.count_rank_sh.map(parse_from_str).unwrap_or(Ok(0))?, + count_a: raw.count_rank_a.map(parse_from_str).unwrap_or(Ok(0))?, + rank: raw.pp_rank.map(parse_from_str).unwrap_or(Ok(0))?, + country_rank: raw.pp_country_rank.map(parse_from_str).unwrap_or(Ok(0))?, + level: raw.level.map(parse_from_str).unwrap_or(Ok(0.0))?, + pp: Some(raw.pp_raw.map(parse_from_str).unwrap_or(Ok(0.0))?).filter(|v| *v != 0.0), + accuracy: raw.accuracy.map(parse_from_str).unwrap_or(Ok(0.0))?, events: { let mut v = Vec::new(); for e in raw.events.into_iter() { diff --git a/youmubot-osu/src/models/raw.rs b/youmubot-osu/src/models/raw.rs index 70cf94e..641518f 100644 --- a/youmubot-osu/src/models/raw.rs +++ b/youmubot-osu/src/models/raw.rs @@ -46,24 +46,24 @@ pub(crate) struct User { pub user_id: String, pub username: String, pub join_date: String, - pub count300: String, - pub count100: String, - pub count50: String, - pub playcount: String, - pub ranked_score: String, - pub total_score: String, - pub pp_rank: String, - pub level: String, - pub pp_raw: String, - pub accuracy: String, - pub count_rank_ss: String, - pub count_rank_ssh: String, - pub count_rank_s: String, - pub count_rank_sh: String, - pub count_rank_a: String, pub country: String, - pub total_seconds_played: String, - pub pp_country_rank: String, + pub count300: Option, + pub count100: Option, + pub count50: Option, + pub playcount: Option, + pub ranked_score: Option, + pub total_score: Option, + pub pp_rank: Option, + pub level: Option, + pub pp_raw: Option, + pub accuracy: Option, + pub count_rank_ss: Option, + pub count_rank_ssh: Option, + pub count_rank_s: Option, + pub count_rank_sh: Option, + pub count_rank_a: Option, + pub total_seconds_played: Option, + pub pp_country_rank: Option, pub events: Vec, }