Update to codeforces package with sound ratelimiting

This commit is contained in:
Natsu Kagami 2021-02-18 03:42:49 +09:00
parent 68142c6236
commit e36c385f15
Signed by: nki
GPG key ID: 7306B3D3C3AD6E51
5 changed files with 26 additions and 38 deletions

5
Cargo.lock generated
View file

@ -154,10 +154,11 @@ dependencies = [
[[package]] [[package]]
name = "codeforces" name = "codeforces"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df54b4689d51d58162c8b76a642ba44d6128b68f29c3fbc23815e6ea4bfef15" checksum = "f0167d57615475a20056a4ce224d143cf603f7159e3102ac5e6a3a1d9ea1c71a"
dependencies = [ dependencies = [
"flume",
"futures-util", "futures-util",
"reqwest", "reqwest",
"serde", "serde",

View file

@ -74,13 +74,13 @@ async fn update_user(
user_id: UserId, user_id: UserId,
cfu: &mut CfUser, cfu: &mut CfUser,
) -> Result<()> { ) -> Result<()> {
let info = User::info(&*client.borrow().await?, &[cfu.handle.as_str()]) let info = User::info(&*client, &[cfu.handle.as_str()])
.await? .await?
.into_iter() .into_iter()
.next() .next()
.ok_or(Error::msg("Not found"))?; .ok_or(Error::msg("Not found"))?;
let rating_changes = info.rating_changes(&*client.borrow().await?).await?; let rating_changes = info.rating_changes(&*client).await?;
let channels_list = channels.channels_of(&http, user_id).await; let channels_list = channels.channels_of(&http, user_id).await;
cfu.last_update = Utc::now(); cfu.last_update = Utc::now();
@ -119,10 +119,8 @@ async fn update_user(
return Ok(()); return Ok(());
} }
let (contest, _, _) = let (contest, _, _) =
codeforces::Contest::standings(&*client.borrow().await?, rc.contest_id, |f| { codeforces::Contest::standings(&*client, rc.contest_id, |f| f.limit(1, 1))
f.limit(1, 1) .await?;
})
.await?;
channels channels
.iter() .iter()
.map(|channel| { .map(|channel| {

View file

@ -42,7 +42,7 @@ impl TypeMapKey for ContestCache {
impl ContestCache { impl ContestCache {
/// Creates a new, empty cache. /// Creates a new, empty cache.
pub(crate) async fn new(http: Client) -> Result<Self> { pub(crate) async fn new(http: Client) -> Result<Self> {
let contests_list = Contest::list(&*http.borrow().await?, true).await?; let contests_list = Contest::list(&*http, true).await?;
Ok(Self { Ok(Self {
contests: HashMap::new(), contests: HashMap::new(),
all_list: RwLock::new((contests_list, Instant::now())), all_list: RwLock::new((contests_list, Instant::now())),
@ -64,17 +64,14 @@ impl ContestCache {
&self, &self,
contest_id: u64, contest_id: u64,
) -> Result<(Contest, Option<Vec<Problem>>)> { ) -> Result<(Contest, Option<Vec<Problem>>)> {
let (c, p) = let (c, p) = match Contest::standings(&*self.http, contest_id, |f| f.limit(1, 1)).await {
match Contest::standings(&*self.http.borrow().await?, contest_id, |f| f.limit(1, 1)) Ok((c, p, _)) => (c, Some(p)),
.await Err(codeforces::Error::Codeforces(s)) if s.ends_with("has not started") => {
{ let c = self.get_from_list(contest_id).await?;
Ok((c, p, _)) => (c, Some(p)), (c, None)
Err(codeforces::Error::Codeforces(s)) if s.ends_with("has not started") => { }
let c = self.get_from_list(contest_id).await?; Err(v) => return Err(Error::from(v)),
(c, None) };
}
Err(v) => return Err(Error::from(v)),
};
self.contests.insert(contest_id, (c, p)); self.contests.insert(contest_id, (c, p));
Ok(self.contests.get(&contest_id).unwrap().clone()) Ok(self.contests.get(&contest_id).unwrap().clone())
} }
@ -83,10 +80,8 @@ impl ContestCache {
let last_updated = self.all_list.read().await.1.clone(); let last_updated = self.all_list.read().await.1.clone();
if Instant::now() - last_updated > std::time::Duration::from_secs(60 * 60) { if Instant::now() - last_updated > std::time::Duration::from_secs(60 * 60) {
// We update at most once an hour. // We update at most once an hour.
*self.all_list.write().await = ( *self.all_list.write().await =
Contest::list(&*self.http.borrow().await?, true).await?, (Contest::list(&*self.http, true).await?, Instant::now());
Instant::now(),
);
} }
self.all_list self.all_list
.read() .read()

View file

@ -22,7 +22,7 @@ mod live;
struct CFClient; struct CFClient;
impl TypeMapKey for CFClient { impl TypeMapKey for CFClient {
type Value = Arc<ratelimit::Ratelimit<codeforces::Client>>; type Value = Arc<codeforces::Client>;
} }
use db::{CfSavedUsers, CfUser}; use db::{CfSavedUsers, CfUser};
@ -33,11 +33,7 @@ pub use hook::InfoHook;
pub async fn setup(path: &std::path::Path, data: &mut TypeMap, announcers: &mut AnnouncerHandler) { pub async fn setup(path: &std::path::Path, data: &mut TypeMap, announcers: &mut AnnouncerHandler) {
CfSavedUsers::insert_into(data, path.join("cf_saved_users.yaml")) CfSavedUsers::insert_into(data, path.join("cf_saved_users.yaml"))
.expect("Must be able to set up DB"); .expect("Must be able to set up DB");
let client = Arc::new(ratelimit::Ratelimit::new( let client = Arc::new(codeforces::Client::new());
codeforces::Client::new(),
4,
std::time::Duration::from_secs(1),
));
data.insert::<hook::ContestCache>(hook::ContestCache::new(client.clone()).await.unwrap()); data.insert::<hook::ContestCache>(hook::ContestCache::new(client.clone()).await.unwrap());
data.insert::<CFClient>(client); data.insert::<CFClient>(client);
announcers.add("codeforces", announcer::Announcer); announcers.add("codeforces", announcer::Announcer);
@ -78,7 +74,7 @@ pub async fn profile(ctx: &Context, m: &Message, mut args: Args) -> CommandResul
} }
}; };
let account = codeforces::User::info(&*http.borrow().await?, &[&handle[..]]) let account = codeforces::User::info(&*http, &[&handle[..]])
.await? .await?
.into_iter() .into_iter()
.next(); .next();
@ -110,7 +106,7 @@ pub async fn save(ctx: &Context, m: &Message, mut args: Args) -> CommandResult {
let handle = args.single::<String>()?; let handle = args.single::<String>()?;
let http = data.get::<CFClient>().unwrap(); let http = data.get::<CFClient>().unwrap();
let account = codeforces::User::info(&*http.borrow().await?, &[&handle[..]]) let account = codeforces::User::info(&*http, &[&handle[..]])
.await? .await?
.into_iter() .into_iter()
.next(); .next();
@ -122,7 +118,7 @@ pub async fn save(ctx: &Context, m: &Message, mut args: Args) -> CommandResult {
} }
Some(acc) => { Some(acc) => {
// Collect rating changes data. // Collect rating changes data.
let rating_changes = acc.rating_changes(&*http.borrow().await?).await?; let rating_changes = acc.rating_changes(&*http).await?;
let mut db = CfSavedUsers::open(&*data); let mut db = CfSavedUsers::open(&*data);
m.reply( m.reply(
&ctx, &ctx,
@ -272,7 +268,7 @@ pub async fn contestranks(ctx: &Context, m: &Message, mut args: Args) -> Command
.collect::<HashMap<_, _>>() .collect::<HashMap<_, _>>()
.await; .await;
let http = data.get::<CFClient>().unwrap(); let http = data.get::<CFClient>().unwrap();
let (contest, problems, ranks) = Contest::standings(&*http.borrow().await?, contest_id, |f| { let (contest, problems, ranks) = Contest::standings(&*http, contest_id, |f| {
f.handles(members.iter().map(|(k, _)| k.clone()).collect()) f.handles(members.iter().map(|(k, _)| k.clone()).collect())
}) })
.await?; .await?;

View file

@ -57,7 +57,7 @@ pub async fn watch_contest(
let http = data.get::<CFClient>().unwrap(); let http = data.get::<CFClient>().unwrap();
let (mut contest, problems, _) = let (mut contest, problems, _) =
Contest::standings(&*http.borrow().await?, contest_id, |f| f.limit(1, 1)).await?; Contest::standings(&*http, contest_id, |f| f.limit(1, 1)).await?;
msg.edit(&ctx, |e| { msg.edit(&ctx, |e| {
e.content(format!( e.content(format!(
@ -79,9 +79,7 @@ pub async fn watch_contest(
msg.pin(ctx).await.ok(); msg.pin(ctx).await.ok();
loop { loop {
if let Ok(messages) = if let Ok(messages) = scan_changes(&*http, &mut member_results, &mut contest).await {
scan_changes(&*http.borrow().await?, &mut member_results, &mut contest).await
{
for message in messages { for message in messages {
channel channel
.send_message(&ctx, |e| { .send_message(&ctx, |e| {