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<MSEvent>, #[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: <t:{start}:R>\nEnd: <t:{end}:R>", 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<Utc>, #[serde(with = "ms_date")] #[serde(rename = "sessionEndDate")] end: DateTime<Utc>, #[serde(rename = "Series")] series: String, } mod ms_date { use chrono::{DateTime, NaiveDateTime, Utc}; use serde::{self, Deserialize, Deserializer}; pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, 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<MSReq> { 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<MSReq> = match req { Err(e) => { warn!("Error getting normal schedule {}", e); None } Ok(req) if req.status().as_u16() == 404 => { warn!("404 on getting schedule"); None } Ok(req) if req.status().as_u16() == 200 => { let data = req.json::<MSReq>().await; match data { Ok(d) => Some(d), Err(e) => { warn!("Error getting normal schedule {}", e); None } } } Ok(req) => { warn!( "Unhandled status when parsing schedule request {}", req.status() ); None } }; result } #[poise::command(slash_command)] pub async fn schedule(ctx: Context<'_>) -> Result<(), Error> { let events: Option<MSReq> = 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(()) }