use crate::{commands::utils, Context, Error}; use cached::proc_macro::cached; use chrono::{DateTime, Utc}; use log::{warn}; use reqwest::header::AUTHORIZATION; use serde::Deserialize; #[derive(Deserialize, Clone)] struct MSReq { containers: Vec, #[serde(rename = "eventTitle")] event_title: String, } #[derive(Deserialize, Clone)] struct MSEvent { id: String, // #[serde(rename = "longDescription")] // description: String, // country: String, metadata: MSMetadata, } impl MSEvent { fn get_title(&self) -> String { let title = &self.metadata.attributes.series.replace("FORMULA", "F"); format!("**{}: {}**", title, self.metadata.brief) // format!("", sport=self.content.format.sport, title=self.content.title, synopsis=self.content.synopsis, start=self.times.start.timestamp(), end=self.times.end.timestamp(), desc=self.content.description, id=self.system.product_key) } fn get_value(&self, high_tier: bool) -> String { let link = if high_tier { format!("[{id}](https://morningstreams.com/hightier/f1/session/{id})\n", id=self.id) } else { "".to_string() }; format!("{link}Start: \nEnd:  ", link=link, start=self.metadata.attributes.start.timestamp(), end=self.metadata.attributes.end.timestamp()) } } #[derive(Deserialize, Clone)] struct MSMetadata { // id: String, // system: System, #[serde(rename="emfAttributes")] attributes: EmfAttributes, #[serde(rename="titleBrief")] brief: String, // #[serde(rename="Series")] // series: String, } #[derive(Deserialize, Clone)] struct EmfAttributes { #[serde(with = "ms_date")] #[serde(rename="sessionStartDate")] start: DateTime, #[serde(with = "ms_date")] #[serde(rename="sessionEndDate")] end: DateTime, #[serde(rename="Series")] series: String, } mod ms_date { use chrono::{DateTime, Utc, NaiveDateTime}; use serde::{self, Deserialize, Deserializer}; pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let n = i64::deserialize(deserializer)?/1000; // let s = String::deserialize(deserializer)?; Ok(DateTime::from_utc(NaiveDateTime::from_timestamp(n, 0), Utc)) } } #[cached(time = 3600)] async fn get_schedule() -> Option { let token = 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/f1/next-event" )) .header(AUTHORIZATION, token) .send() .await; let result: Option = match req { Err(e) => { warn!("Error getting Viaplay schedule {}", e); None } Ok(req) if req.status().as_u16() == 404 => { warn!("404 on getting VP events"); None } Ok(req) if req.status().as_u16() == 200 => { let data = req.json::().await; match data { Ok(d) => Some(d), Err(e) => { warn!("Error getting Viaplay schedule {}", e); None } } } Ok(req) => { warn!( "Unhandled status when parsing viaplay request {}", req.status() ); None } }; result } #[poise::command(slash_command)] pub async fn schedule( ctx: Context<'_>, ) -> Result<(), Error> { let events: Option = get_schedule().await; let ht: bool = utils::high_tier(ctx).await; match events { None => {ctx.say("Error on fetching events :(").await?;}, Some(evs) => { ctx.send(|b| {b.embed(|e| { e.title(format!("F1 schedule: {}", evs.event_title)); for event in evs.containers { e.field(event.get_title(), event.get_value(ht), true); }; e })}).await?; } }; Ok(()) }