Add NFL links

This commit is contained in:
Tom 2022-09-11 22:41:01 +02:00
parent 92507d10e6
commit 8ac7702d8c
2 changed files with 231 additions and 1 deletions

View file

@ -2,6 +2,7 @@ use crate::{Context, Error};
mod eurosport;
mod f1;
mod nfl;
mod viaplay;
mod wrc;
@ -59,7 +60,7 @@ pub enum Timeframe {
#[poise::command(
slash_command,
subcommands("viaplay::viaplay", "eurosport::eurosport", "wrc::wrc", "f1::f1")
subcommands("viaplay::viaplay", "eurosport::eurosport", "wrc::wrc", "f1::f1", "nfl::nfl")
)]
pub async fn links(ctx: Context<'_>) -> Result<(), Error> {
ctx.say("Hello there!").await?;

229
src/commands/links/nfl.rs Normal file
View file

@ -0,0 +1,229 @@
use cached::proc_macro::cached;
use chrono::{DateTime, Duration, Utc};
use log::{warn, info};
use reqwest::header::AUTHORIZATION;
use serde::Deserialize;
use crate::{commands::utils, Context, Error};
use super::Timeframe;
mod str_to_u8 {
use serde::{self, Deserialize, Deserializer};
pub fn deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(s.parse::<u8>().unwrap())
}
}
mod nfl_date {
use chrono::{DateTime, TimeZone, Utc};
use serde::{self, Deserialize, Deserializer};
const FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S.%fZ";
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Utc.datetime_from_str(&s, FORMAT)
.map_err(serde::de::Error::custom)
}
}
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
struct NFLContext {
// current_season: String,
// current_season_type: String,
#[serde(with = "str_to_u8")]
current_week: u8,
}
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
struct NFLEvent {
home_nick_name: String,
visitor_nick_name: String,
// game_id: String,
#[serde(with = "nfl_date")]
game_date_time_utc: DateTime<Utc>,
video: NFLVideo,
}
#[derive(Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
struct NFLVideo {
title: String,
// video_status: String,
video_id: String,
// schedule_date: String
}
impl NFLEvent {
fn filter(&self, filter: &str) -> bool {
if filter.is_empty() {
return true;
};
if self.video.title.contains(filter) {
return true;
}
false
}
fn comp(&self, when: &Option<Timeframe>) -> bool {
let now = Utc::now();
match when {
Some(Timeframe::Everything) => true,
Some(Timeframe::Current) => {
self.game_date_time_utc <= now && (self.game_date_time_utc + Duration::minutes(240)) >= now
}
Some(Timeframe::Future) => (self.game_date_time_utc + Duration::minutes(240)) >= now,
Some(Timeframe::Past) => self.game_date_time_utc <= now,
_ => (self.game_date_time_utc + Duration::minutes(240)) >= now,
}
}
fn to_string(&self) -> String {
format!(
"```fix\n{home}-{away} ||{title}```<t:{time}:R> https://tom.al/ms/nfl/{id}",
title = self.video.title,
id = self.video.video_id,
time = self.game_date_time_utc.timestamp(),
home= self.home_nick_name,
away = self.visitor_nick_name,
)
}
}
#[cached(time = 3600)]
async fn get_week() -> Option<NFLContext> {
let token = super::super::super::SETTINGS
.read()
.unwrap()
.get_table("morningstreams")
.unwrap()
.get("token")
.expect("Config error, please set the morningstreams[token] value")
.clone()
.into_string()
.expect("Config error, please make sure morningstreams[token] is a string");
let client = reqwest::Client::new();
let req = client
.get("https://api.morningstreams.com/api/hightier/nfl/current-context")
.header(AUTHORIZATION, token)
.send()
.await;
match req {
Err(e) => {
warn!("Error getting NFL context {}", e);
None
}
Ok(req) => match req.json::<NFLContext>().await {
Ok(data) => Some(data),
Err(e) => {
warn!("Error parsing NFL context {}", e);
None
}
},
}
}
#[cached(time = 3600)]
async fn get_schedule(week: u8) -> Option<Vec<NFLEvent>> {
let token = super::super::super::SETTINGS
.read()
.unwrap()
.get_table("morningstreams")
.unwrap()
.get("token")
.expect("Config error, please set the morningstreams[token] value")
.clone()
.into_string()
.expect("Config error, please make sure morningstreams[token] is a string");
let client = reqwest::Client::new();
let req = client
.get(format!(
"https://api.morningstreams.com/api/hightier/nfl/schedule/{}",
week
))
.header(AUTHORIZATION, token)
.send()
.await;
let result: Option<Vec<NFLEvent>> = match req {
Err(e) => {
warn!("Error getting NFL schedule {}", e);
None
}
Ok(req) if req.status().as_u16() == 404 => {
warn!("404 on getting NFL events");
None
}
Ok(req) if req.status().as_u16() == 200 => {
let data = req.json::<Vec<NFLEvent>>().await;
match data {
Ok(d) => Some(d),
Err(e) => {
warn!("Error getting NFL schedule {}", e);
None
}
}
}
Ok(req) => {
warn!("Unhandled status when parsing NFL request {}", req.status());
None
}
};
result
}
//NFL events listing
#[poise::command(slash_command)]
pub async fn nfl(
ctx: Context<'_>,
#[description = "Filter sessions for when they are/were happening, defaults to future"]
timeframe: Option<super::Timeframe>,
#[description = "Content to filter on"] filter: Option<String>,
#[description = "Which game week? (Defaults to current)"]
week: Option<u8>,
) -> Result<(), Error> {
let get_week: u8 = match week{
Some(w) => w,
None => {
match get_week().await {
None => { ctx.say("Error getting current week data, try setting one manually").await?; return Ok(())},
Some(w) => w.current_week,
}
}
};
let events: Option<Vec<NFLEvent>> = get_schedule(get_week).await;
match events {
None => {
ctx.say("Unable to get the events, try a different game week or try again later (it's cached so wait a bit...)")
.await?;
}
Some(evs) => {
info!("Found {} events from NFL", evs.len());
let filtered: Vec<String> = evs
.into_iter()
.filter(|e| e.comp(&timeframe))
.filter(|e| match &filter {
None => true,
Some(f) => e.filter(f.as_str()),
})
.map(|e| e.to_string())
.collect();
let pages = utils::paginator(filtered, 1900, "\n".to_string());
utils::paginate_string(ctx, pages).await?;
}
};
Ok(())
}