Remove some pubs since not needed and add F1 content to the links command

This commit is contained in:
Tom 2022-08-18 20:51:13 +02:00
parent 91e195ddbf
commit 022a1d8991
3 changed files with 296 additions and 7 deletions

289
src/commands/links/f1.rs Normal file
View file

@ -0,0 +1,289 @@
use crate::{Context, Error};
use cached::proc_macro::cached;
use log::warn;
use poise::serenity_prelude as serenity;
use poise::serenity_prelude::CreateSelectMenuOption;
use reqwest::header::AUTHORIZATION;
use serde::Deserialize;
async fn autocomplete_season<'a>(
_ctx: Context<'_>,
partial: &'a str,
) -> impl Iterator<Item = String> + 'a {
(1981..2023)
.map(|n: u32| n.to_string())
.filter(move |e| e.starts_with(&partial))
.map(|e| e.to_string())
}
#[derive(Deserialize, Clone)]
struct MSReq {
containers: Vec<MSEvent>,
}
#[derive(Deserialize, Clone)]
struct MSEvent {
id: 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) -> String {
format!(
"[{id}](https://morningstreams.com/hightier/f1/session/{id})\nDuration: {duration}",
id = self.id,
duration = self.metadata.duration
)
}
}
#[derive(Deserialize, Clone)]
struct MSMetadata {
title: String,
#[serde(rename = "emfAttributes")]
attributes: EmfAttributes,
#[serde(rename = "titleBrief")]
brief: String,
#[serde(rename = "uiDuration")]
duration: String,
}
#[derive(Deserialize, Clone)]
struct EmfAttributes {
#[serde(rename = "Series")]
series: String,
}
#[derive(Deserialize, Clone)]
struct Season {
#[serde(rename = "containers")]
events: Vec<EventData>,
}
#[derive(Deserialize, Clone)]
struct EventData {
metadata: SeasonMetadata,
}
#[derive(Deserialize, Clone)]
struct SeasonMetadata {
#[serde(rename = "emfAttributes")]
attributes: SeasonAttributes,
}
#[derive(Deserialize, Clone)]
struct SeasonAttributes {
#[serde(rename = "MeetingKey")]
id: String,
#[serde(rename = "Meeting_Official_Name")]
name: String,
#[serde(rename = "Meeting_Name")]
short_name: String,
}
#[cached(time = 18000)]
async fn get_event(session: String) -> Option<MSReq> {
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/f1/event/{}",
session
))
.header(AUTHORIZATION, token)
.send()
.await;
let result: Option<MSReq> = match req {
Err(e) => {
warn!("Error getting F1TV schedule {}", e);
None
}
Ok(req) if req.status().as_u16() == 404 => {
warn!("404 on getting F1TV events");
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 F1TV schedule {}", e);
None
}
}
}
Ok(req) => {
warn!(
"Unhandled status when parsing F1TV request {}",
req.status()
);
None
}
};
result
}
#[cached(time = 18000)]
async fn get_sessions(season: String) -> Option<Season> {
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/f1/season/{}",
season
))
.header(AUTHORIZATION, token)
.send()
.await;
let result: Option<Season> = match req {
Err(e) => {
warn!("Error getting F1TV schedule {}", e);
None
}
Ok(req) if req.status().as_u16() == 404 => {
warn!("404 on getting F1TV events");
None
}
Ok(req) if req.status().as_u16() == 200 => {
let data = req.json::<Season>().await;
match data {
Ok(d) => Some(d),
Err(e) => {
warn!("Error getting F1TV schedule {}", e);
None
}
}
}
Ok(req) => {
warn!(
"Unhandled status when parsing F1TV request {} text: {}",
req.status(),
req.text().await.expect(
"Whoops not getting the text out of this one, F1TV status error unknown"
)
);
None
}
};
result
}
// F1TV links throughout the seasons
#[poise::command(slash_command)]
pub async fn f1(
ctx: Context<'_>,
#[description = "Which season to pull from?"]
#[autocomplete = "autocomplete_season"]
season: String,
) -> Result<(), Error> {
let sessions: Option<Season> = get_sessions(season).await;
match sessions {
None => {
ctx.say("Unable to find data for selected season").await?;
}
Some(ses) if ses.events.len() == 0 => {
ctx.say("Found 0 events for this season :(").await?;
}
Some(sessions) => {
let content_id = ctx.id();
let options: Vec<_> = sessions
.events
.iter()
.map(|e| {
CreateSelectMenuOption::default()
.label(&e.metadata.attributes.short_name)
.description(&e.metadata.attributes.name)
.value(&e.metadata.attributes.id)
.to_owned()
})
.collect();
ctx.send(|m| {
m.content("Please select a session").components(|c| {
c.create_action_row(|ar| {
ar.create_select_menu(|menu| {
menu.custom_id(content_id);
menu.options(|opt| opt.set_options(options))
})
})
})
})
.await?;
while let Some(mci) = serenity::CollectComponentInteraction::new(ctx.discord())
.channel_id(ctx.channel_id())
.filter(move |mci| mci.data.custom_id == content_id.to_string())
.await
{
println!("Got new interaction event");
let session = mci.data.values[0].parse::<String>().unwrap();
let ses = get_event(session).await;
println!("Now responding to interaction");
match mci
.create_interaction_response(ctx.discord(), |ir| {
ir.kind(serenity::InteractionResponseType::UpdateMessage)
.interaction_response_data(|m| match ses {
None => m.content("Unable to get these events :("),
Some(evs) if evs.containers.len() == 0 => {
m.content("Somehow no sessions found for this event :(")
}
Some(evs) => {
println!("editing message");
m.content(evs.containers[0].metadata.title.to_string());
m.embed(|e| {
e.title(format!(
"F1 schedule: {}",
evs.containers[0].metadata.title
));
for event in evs.containers {
e.field(event.get_title(), event.get_value(), true);
}
e
})
}
})
})
.await
{
Err(e) => {
println!("Error on interaction response {}", e);
}
// Ok(o) => {println!("No error on interaction response, here's what we got back: {}", o);},
_ => {
();
}
};
println!("Reached end of while loop iteration, going back?");
}
println!("Exited while loop for F1TV links");
}
}
Ok(())
}

View file

@ -1,8 +1,9 @@
use crate::{commands::utils, Context, Error};
use crate::{Context, Error};
pub mod eurosport;
pub mod viaplay;
pub mod wrc;
mod eurosport;
mod f1;
mod viaplay;
mod wrc;
#[derive(Debug, poise::ChoiceParameter)]
pub enum Timeframe {
@ -57,9 +58,8 @@ pub enum Timeframe {
// }
#[poise::command(
prefix_command,
slash_command,
subcommands("viaplay::viaplay", "eurosport::eurosport", "wrc::wrc")
subcommands("viaplay::viaplay", "eurosport::eurosport", "wrc::wrc", "f1::f1")
)]
pub async fn links(ctx: Context<'_>) -> Result<(), Error> {
ctx.say("Hello there!").await?;

View file

@ -2,7 +2,7 @@ use crate::{Context, Error};
use poise::serenity_prelude as serenity;
pub mod invites;
pub mod links;
mod links;
// pub mod planning;
pub mod schedule;
pub mod utils;