Add NFL links
This commit is contained in:
parent
92507d10e6
commit
8ac7702d8c
|
@ -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
229
src/commands/links/nfl.rs
Normal 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(())
|
||||
}
|
Loading…
Reference in a new issue