mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 16:58:55 +00:00
Codeforces: asyncify live
This commit is contained in:
parent
1fc6d09910
commit
4a2a4ed688
3 changed files with 57 additions and 62 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -235,15 +235,6 @@ dependencies = [
|
||||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
@ -1933,14 +1924,13 @@ dependencies = [
|
||||||
"Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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-db 0.1.0",
|
||||||
"youmubot-prelude 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 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 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 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-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-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"
|
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||||
|
|
|
@ -7,17 +7,15 @@ edition = "2018"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
tokio = { version = "0.2", features = ["time"] }
|
||||||
reqwest = "0.10.1"
|
reqwest = "0.10.1"
|
||||||
serenity = "0.9.0-rc.0"
|
serenity = "0.9.0-rc.0"
|
||||||
Inflector = "0.11"
|
Inflector = "0.11"
|
||||||
codeforces = "0.2"
|
codeforces = "0.2"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
rayon = "1"
|
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
crossbeam-channel = "0.4"
|
|
||||||
dashmap = "3.11.4"
|
dashmap = "3.11.4"
|
||||||
|
|
||||||
|
|
||||||
youmubot-prelude = { path = "../youmubot-prelude" }
|
youmubot-prelude = { path = "../youmubot-prelude" }
|
||||||
youmubot-db = { path = "../youmubot-db" }
|
youmubot-db = { path = "../youmubot-db" }
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::db::CfSavedUsers;
|
use crate::{db::CfSavedUsers, CFClient};
|
||||||
use codeforces::{Contest, ContestPhase, Problem, ProblemResult, ProblemResultType, RanklistRow};
|
use codeforces::{Contest, ContestPhase, Problem, ProblemResult, ProblemResultType, RanklistRow};
|
||||||
use rayon::prelude::*;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{CommandError, CommandResult},
|
|
||||||
model::{
|
model::{
|
||||||
guild::Member,
|
guild::Member,
|
||||||
id::{ChannelId, GuildId, UserId},
|
id::{ChannelId, GuildId, UserId},
|
||||||
|
@ -21,38 +19,44 @@ struct MemberResult {
|
||||||
/// Watch and commentate a contest.
|
/// Watch and commentate a contest.
|
||||||
///
|
///
|
||||||
/// Does the thing on a channel, block until the contest ends.
|
/// Does the thing on a channel, block until the contest ends.
|
||||||
pub fn watch_contest(
|
pub async fn watch_contest(
|
||||||
ctx: &mut Context,
|
ctx: &Context,
|
||||||
guild: GuildId,
|
guild: GuildId,
|
||||||
channel: ChannelId,
|
channel: ChannelId,
|
||||||
contest_id: u64,
|
contest_id: u64,
|
||||||
) -> CommandResult {
|
) -> Result<()> {
|
||||||
let db = CfSavedUsers::open(&*ctx.data.read()).borrow()?.clone();
|
let data = ctx.data.read().await;
|
||||||
|
let db = CfSavedUsers::open(&*data).borrow()?.clone();
|
||||||
let http = ctx.http.clone();
|
let http = ctx.http.clone();
|
||||||
// Collect an initial member list.
|
// Collect an initial member list.
|
||||||
// This never changes during the scan.
|
// This never changes during the scan.
|
||||||
let mut member_results: HashMap<UserId, MemberResult> = db
|
let mut member_results: HashMap<UserId, MemberResult> = db
|
||||||
.into_par_iter()
|
.into_iter()
|
||||||
.filter_map(|(user_id, cfu)| {
|
.map(|(user_id, cfu)| {
|
||||||
let member = guild.member(http.clone().as_ref(), user_id).ok();
|
let http = http.clone();
|
||||||
match member {
|
async move {
|
||||||
Some(m) => Some((
|
guild.member(http, user_id).await.map(|m| {
|
||||||
|
(
|
||||||
user_id,
|
user_id,
|
||||||
MemberResult {
|
MemberResult {
|
||||||
member: m,
|
member: m,
|
||||||
handle: cfu.handle,
|
handle: cfu.handle,
|
||||||
row: None,
|
row: None,
|
||||||
},
|
},
|
||||||
)),
|
)
|
||||||
None => None,
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect::<stream::FuturesUnordered<_>>()
|
||||||
|
.filter_map(|v| future::ready(v.ok()))
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
|
||||||
let http = ctx.data.get_cloned::<HTTPClient>();
|
let http = data.get::<CFClient>().unwrap();
|
||||||
let (mut contest, _, _) = Contest::standings(&http, contest_id, |f| f.limit(1, 1))?;
|
let (mut contest, _, _) = Contest::standings(&http, contest_id, |f| f.limit(1, 1)).await?;
|
||||||
|
|
||||||
channel.send_message(&ctx, |e| {
|
channel
|
||||||
|
.send_message(&ctx, |e| {
|
||||||
e.content(format!(
|
e.content(format!(
|
||||||
"Youmu is watching contest **{}**, with the following members:\n{}",
|
"Youmu is watching contest **{}**, with the following members:\n{}",
|
||||||
contest.name,
|
contest.name,
|
||||||
|
@ -62,15 +66,17 @@ pub fn watch_contest(
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n"),
|
.join("\n"),
|
||||||
))
|
))
|
||||||
})?;
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
loop {
|
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 {
|
for message in messages {
|
||||||
channel
|
channel
|
||||||
.send_message(&ctx, |e| {
|
.send_message(&ctx, |e| {
|
||||||
e.content(format!("**{}**: {}", contest.name, message))
|
e.content(format!("**{}**: {}", contest.name, message))
|
||||||
})
|
})
|
||||||
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +84,7 @@ pub fn watch_contest(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Sleep for a minute
|
// 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
|
// Announce the final results
|
||||||
|
@ -93,12 +99,14 @@ pub fn watch_contest(
|
||||||
ranks.sort_by(|(_, a), (_, b)| a.rank.cmp(&b.rank));
|
ranks.sort_by(|(_, a), (_, b)| a.rank.cmp(&b.rank));
|
||||||
|
|
||||||
if ranks.is_empty() {
|
if ranks.is_empty() {
|
||||||
channel.send_message(&ctx, |e| {
|
channel
|
||||||
|
.send_message(&ctx, |e| {
|
||||||
e.content(format!(
|
e.content(format!(
|
||||||
"**{}** has ended, but I can't find anyone in this server on the scoreboard...",
|
"**{}** has ended, but I can't find anyone in this server on the scoreboard...",
|
||||||
contest.name
|
contest.name
|
||||||
))
|
))
|
||||||
})?;
|
})
|
||||||
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,23 +123,23 @@ pub fn watch_contest(
|
||||||
row.problem_results.iter().map(|p| format!("{:.0}", p.points)).collect::<Vec<_>>().join("/"),
|
row.problem_results.iter().map(|p| format!("{:.0}", p.points)).collect::<Vec<_>>().join("/"),
|
||||||
row.successful_hack_count,
|
row.successful_hack_count,
|
||||||
row.unsuccessful_hack_count,
|
row.unsuccessful_hack_count,
|
||||||
)).collect::<Vec<_>>().join("\n"))))?;
|
)).collect::<Vec<_>>().join("\n")))).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_changes(
|
async fn scan_changes(
|
||||||
http: <HTTPClient as TypeMapKey>::Value,
|
http: &codeforces::Client,
|
||||||
members: &mut HashMap<UserId, MemberResult>,
|
members: &mut HashMap<UserId, MemberResult>,
|
||||||
contest: &mut Contest,
|
contest: &mut Contest,
|
||||||
) -> Result<Vec<String>, CommandError> {
|
) -> Result<Vec<String>> {
|
||||||
let mut messages: Vec<String> = vec![];
|
let mut messages: Vec<String> = vec![];
|
||||||
let (updated_contest, problems, ranks) = {
|
let (updated_contest, problems, ranks) = {
|
||||||
let handles = members
|
let handles = members
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, h)| h.handle.clone())
|
.map(|(_, h)| h.handle.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
Contest::standings(&http, contest.id, |f| f.handles(handles))?
|
Contest::standings(&http, contest.id, |f| f.handles(handles)).await?
|
||||||
};
|
};
|
||||||
// Change of phase.
|
// Change of phase.
|
||||||
if contest.phase != updated_contest.phase {
|
if contest.phase != updated_contest.phase {
|
||||||
|
|
Loading…
Add table
Reference in a new issue