From 3a14c401cd38e8973d16844f0d5ce9be9e054989 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Mon, 10 Feb 2020 15:18:22 -0500 Subject: [PATCH] Implement Codeforces announcer --- youmubot-cf/src/announcer.rs | 96 ++++++++++++++++++++++++++++++++++++ youmubot-cf/src/embed.rs | 12 ++++- youmubot-cf/src/lib.rs | 6 ++- youmubot/src/main.rs | 2 +- 4 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 youmubot-cf/src/announcer.rs diff --git a/youmubot-cf/src/announcer.rs b/youmubot-cf/src/announcer.rs new file mode 100644 index 0000000..d1ce197 --- /dev/null +++ b/youmubot-cf/src/announcer.rs @@ -0,0 +1,96 @@ +use crate::db::{CfSavedUsers, CfUser}; +use announcer::MemberToChannels; +use chrono::{DateTime, Utc}; +use codeforces::{RatingChange, User}; +use serenity::{ + framework::standard::{CommandError, CommandResult}, + http::CacheHttp, + model::id::{ChannelId, UserId}, + CacheAndHttp, +}; +use std::sync::Arc; +use youmubot_prelude::*; + +type Reqwest = ::Value; + +/// Updates the rating and rating changes of the users. +pub fn updates( + http: Arc, + data: AppData, + channels: MemberToChannels, +) -> CommandResult { + let mut users = CfSavedUsers::open(&*data.read()).borrow()?.clone(); + let reqwest = data.get_cloned::(); + + for (user_id, cfu) in users.iter_mut() { + if let Err(e) = update_user(http.clone(), &channels, &reqwest, *user_id, cfu) { + dbg!((*user_id, e)); + } + } + + *CfSavedUsers::open(&*data.read()).borrow_mut()? = users; + Ok(()) +} + +fn update_user( + http: Arc, + channels: &MemberToChannels, + reqwest: &Reqwest, + user_id: UserId, + cfu: &mut CfUser, +) -> CommandResult { + let info = User::info(reqwest, &[cfu.handle.as_str()])? + .into_iter() + .next() + .ok_or(CommandError::from("Not found"))?; + + let rating_changes = { + let mut v = info.rating_changes(reqwest)?; + v.reverse(); + v + }; + + let mut channels_list: Option> = None; + let last_update = std::mem::replace(&mut cfu.last_update, Utc::now()); + // Update the rating + cfu.rating = info.rating; + + let mut send_message = |rc: RatingChange| -> CommandResult { + let (contest, _, _) = + codeforces::Contest::standings(reqwest, rc.contest_id, |f| f.limit(1, 1))?; + let channels = + channels_list.get_or_insert_with(|| channels.channels_of(http.clone(), user_id)); + for channel in channels { + if let Err(e) = channel.send_message(http.http(), |e| { + e.content(format!("Rating change for {}!", user_id.mention())) + .embed(|c| { + crate::embed::rating_change_embed( + &rc, + &info, + &contest, + &user_id.mention(), + c, + ) + }) + }) { + dbg!(e); + } + } + Ok(()) + }; + + // Check for any good announcements to make + for rc in rating_changes { + let date: DateTime = DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp(rc.rating_update_time_seconds as i64, 0), + Utc, + ); + if &date > &last_update { + if let Err(v) = send_message(rc) { + dbg!(v); + } + } + } + + Ok(()) +} diff --git a/youmubot-cf/src/embed.rs b/youmubot-cf/src/embed.rs index be81961..2531029 100644 --- a/youmubot-cf/src/embed.rs +++ b/youmubot-cf/src/embed.rs @@ -64,7 +64,7 @@ pub fn rating_change_embed<'a>( ) -> &'a mut CreateEmbed { let delta = (rating_change.new_rating as i64) - (rating_change.old_rating as i64); let color = if delta < 0 { 0xff0000 } else { 0x00ff00 }; - let message = if delta < 0 { + let message = if delta > 0 { MessageBuilder::new() .push(tag) .push(" competed in ") @@ -89,11 +89,19 @@ pub fn rating_change_embed<'a>( }; e.author(|a| { - a.icon_url(&user.avatar) + a.icon_url(format!("http:{}", &user.avatar)) .url(user.profile_url()) .name(&user.handle) }) .color(color) .description(message) .field("Contest Link", contest.url(), true) + .field( + "Rating Change", + format!( + "from **{}** to **{}**", + rating_change.old_rating, rating_change.new_rating + ), + false, + ) } diff --git a/youmubot-cf/src/lib.rs b/youmubot-cf/src/lib.rs index 3f2fc96..3abb68e 100644 --- a/youmubot-cf/src/lib.rs +++ b/youmubot-cf/src/lib.rs @@ -8,6 +8,7 @@ use serenity::{ }; use youmubot_prelude::*; +mod announcer; mod db; mod embed; mod hook; @@ -20,9 +21,10 @@ use db::CfSavedUsers; pub use hook::codeforces_info_hook; /// Sets up the CF databases. -pub fn setup(path: &std::path::Path, data: &mut ShareMap) { +pub fn setup(path: &std::path::Path, data: &mut ShareMap, announcers: &mut AnnouncerHandler) { 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"); + announcers.add("codeforces", announcer::updates); } #[group] diff --git a/youmubot/src/main.rs b/youmubot/src/main.rs index 88b813b..307b490 100644 --- a/youmubot/src/main.rs +++ b/youmubot/src/main.rs @@ -87,7 +87,7 @@ fn main() { .expect("osu! is initialized"); // codeforces #[cfg(feature = "codeforces")] - youmubot_cf::setup(&db_path, &mut data); + youmubot_cf::setup(&db_path, &mut data, &mut announcers); } #[cfg(feature = "core")]