ol_rusty/src/commands/links/f1.rs
Tom 5caf820dc0 Update poise version (with refactor)
set some command to ephemeral
Add roles command
Do some refactoring and minor changes to commands
2023-04-07 19:06:14 +02:00

291 lines
9.2 KiB
Rust

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())
.rev()
.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, ephemeral)]
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)
.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, |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(())
}