set some command to ephemeral Add roles command Do some refactoring and minor changes to commands
291 lines
9.2 KiB
Rust
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(())
|
|
}
|