From 4a2a4ed688b2a5d95ccee514d7bad7ba7e2b036f Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Mon, 7 Sep 2020 22:48:01 -0400 Subject: [PATCH] Codeforces: asyncify live --- Cargo.lock | 13 +---- youmubot-cf/Cargo.toml | 4 +- youmubot-cf/src/live.rs | 102 ++++++++++++++++++++++------------------ 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 386ded9..9103b19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,15 +235,6 @@ dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-channel" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -1933,14 +1924,13 @@ dependencies = [ "Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "codeforces 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "dashmap 3.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "serenity 0.9.0-rc.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "youmubot-db 0.1.0", "youmubot-prelude 0.1.0", ] @@ -2037,7 +2027,6 @@ dependencies = [ "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" "checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" diff --git a/youmubot-cf/Cargo.toml b/youmubot-cf/Cargo.toml index 178a1dd..cc055a9 100644 --- a/youmubot-cf/Cargo.toml +++ b/youmubot-cf/Cargo.toml @@ -7,17 +7,15 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] serde = { version = "1", features = ["derive"] } +tokio = { version = "0.2", features = ["time"] } reqwest = "0.10.1" serenity = "0.9.0-rc.0" Inflector = "0.11" codeforces = "0.2" regex = "1" lazy_static = "1" -rayon = "1" chrono = { version = "0.4", features = ["serde"] } -crossbeam-channel = "0.4" dashmap = "3.11.4" - youmubot-prelude = { path = "../youmubot-prelude" } youmubot-db = { path = "../youmubot-db" } diff --git a/youmubot-cf/src/live.rs b/youmubot-cf/src/live.rs index e03146b..b2d000f 100644 --- a/youmubot-cf/src/live.rs +++ b/youmubot-cf/src/live.rs @@ -1,8 +1,6 @@ -use crate::db::CfSavedUsers; +use crate::{db::CfSavedUsers, CFClient}; use codeforces::{Contest, ContestPhase, Problem, ProblemResult, ProblemResultType, RanklistRow}; -use rayon::prelude::*; use serenity::{ - framework::standard::{CommandError, CommandResult}, model::{ guild::Member, id::{ChannelId, GuildId, UserId}, @@ -21,56 +19,64 @@ struct MemberResult { /// Watch and commentate a contest. /// /// Does the thing on a channel, block until the contest ends. -pub fn watch_contest( - ctx: &mut Context, +pub async fn watch_contest( + ctx: &Context, guild: GuildId, channel: ChannelId, contest_id: u64, -) -> CommandResult { - let db = CfSavedUsers::open(&*ctx.data.read()).borrow()?.clone(); +) -> Result<()> { + let data = ctx.data.read().await; + let db = CfSavedUsers::open(&*data).borrow()?.clone(); let http = ctx.http.clone(); // Collect an initial member list. // This never changes during the scan. let mut member_results: HashMap = db - .into_par_iter() - .filter_map(|(user_id, cfu)| { - let member = guild.member(http.clone().as_ref(), user_id).ok(); - match member { - Some(m) => Some(( - user_id, - MemberResult { - member: m, - handle: cfu.handle, - row: None, - }, - )), - None => None, + .into_iter() + .map(|(user_id, cfu)| { + let http = http.clone(); + async move { + guild.member(http, user_id).await.map(|m| { + ( + user_id, + MemberResult { + member: m, + handle: cfu.handle, + row: None, + }, + ) + }) } }) - .collect(); + .collect::>() + .filter_map(|v| future::ready(v.ok())) + .collect() + .await; - let http = ctx.data.get_cloned::(); - let (mut contest, _, _) = Contest::standings(&http, contest_id, |f| f.limit(1, 1))?; + let http = data.get::().unwrap(); + let (mut contest, _, _) = Contest::standings(&http, contest_id, |f| f.limit(1, 1)).await?; - channel.send_message(&ctx, |e| { - e.content(format!( - "Youmu is watching contest **{}**, with the following members:\n{}", - contest.name, - member_results - .iter() - .map(|(_, m)| format!("- {} as **{}**", m.member.distinct(), m.handle)) - .collect::>() - .join("\n"), - )) - })?; + channel + .send_message(&ctx, |e| { + e.content(format!( + "Youmu is watching contest **{}**, with the following members:\n{}", + contest.name, + member_results + .iter() + .map(|(_, m)| format!("- {} as **{}**", m.member.distinct(), m.handle)) + .collect::>() + .join("\n"), + )) + }) + .await?; loop { - if let Ok(messages) = scan_changes(http.clone(), &mut member_results, &mut contest) { + if let Ok(messages) = scan_changes(&*http, &mut member_results, &mut contest).await { for message in messages { channel .send_message(&ctx, |e| { e.content(format!("**{}**: {}", contest.name, message)) }) + .await .ok(); } } @@ -78,7 +84,7 @@ pub fn watch_contest( break; } // Sleep for a minute - std::thread::sleep(std::time::Duration::from_secs(60)); + tokio::time::delay_for(std::time::Duration::from_secs(60)).await; } // Announce the final results @@ -93,12 +99,14 @@ pub fn watch_contest( ranks.sort_by(|(_, a), (_, b)| a.rank.cmp(&b.rank)); if ranks.is_empty() { - channel.send_message(&ctx, |e| { - e.content(format!( - "**{}** has ended, but I can't find anyone in this server on the scoreboard...", - contest.name - )) - })?; + channel + .send_message(&ctx, |e| { + e.content(format!( + "**{}** has ended, but I can't find anyone in this server on the scoreboard...", + contest.name + )) + }) + .await?; return Ok(()); } @@ -115,23 +123,23 @@ pub fn watch_contest( row.problem_results.iter().map(|p| format!("{:.0}", p.points)).collect::>().join("/"), row.successful_hack_count, row.unsuccessful_hack_count, - )).collect::>().join("\n"))))?; + )).collect::>().join("\n")))).await?; Ok(()) } -fn scan_changes( - http: ::Value, +async fn scan_changes( + http: &codeforces::Client, members: &mut HashMap, contest: &mut Contest, -) -> Result, CommandError> { +) -> Result> { let mut messages: Vec = vec![]; let (updated_contest, problems, ranks) = { let handles = members .iter() .map(|(_, h)| h.handle.clone()) .collect::>(); - Contest::standings(&http, contest.id, |f| f.handles(handles))? + Contest::standings(&http, contest.id, |f| f.handles(handles)).await? }; // Change of phase. if contest.phase != updated_contest.phase {