Update links stuff
This commit is contained in:
parent
4aa918a966
commit
ed9330667b
|
@ -1,8 +1,10 @@
|
|||
use crate::{commands::utils, Context, Error};
|
||||
|
||||
pub mod eurosport;
|
||||
pub mod viaplay;
|
||||
pub mod wrc;
|
||||
|
||||
#[derive(Debug, poise::SlashChoiceParameter)]
|
||||
#[derive(Debug, poise::ChoiceParameter)]
|
||||
pub enum Timeframe {
|
||||
#[name = "Currently happening"]
|
||||
Current,
|
||||
|
@ -14,33 +16,52 @@ pub enum Timeframe {
|
|||
Everything,
|
||||
}
|
||||
|
||||
#[derive(Debug, poise::SlashChoiceParameter)]
|
||||
pub enum Source {
|
||||
#[name = "Get links for the Eurosport player"]
|
||||
Eurosport,
|
||||
#[name = "Get links for the apocalympics Eurosport player"]
|
||||
Olympics,
|
||||
}
|
||||
// #[derive(Debug, poise::ChoiceParameter)]
|
||||
// pub enum Source {
|
||||
// #[name = "Get links for the Eurosport player"]
|
||||
// Eurosport,
|
||||
// // #[name = "Get links for the apocalympics Eurosport player"]
|
||||
// // Olympics,
|
||||
// #[name = "Get WRC links for the WRC player"]
|
||||
// WRC,
|
||||
// #[name = "Get Viaplay links for the Viaplay player"]
|
||||
// Viaplay,
|
||||
// // #[name = "F1 content for the weekend"]
|
||||
// // F1,
|
||||
// }
|
||||
|
||||
/// Get links for high tier commands.
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn links(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Where to git the juicy links from?"] source: Source,
|
||||
#[description = "Filter sessions for when they are/were happening, defaults to future"]
|
||||
timeframe: Option<Timeframe>,
|
||||
#[description = "Content to filter on"] filter: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
if !utils::high_tier(ctx).await {
|
||||
ctx.say("This command can only be used in high tier channels for security")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
// #[poise::command(slash_command)]
|
||||
// pub async fn links2(
|
||||
// ctx: Context<'_>,
|
||||
// #[description = "Where to git the juicy links from?"] source: Source,
|
||||
// #[description = "Filter sessions for when they are/were happening, defaults to future"]
|
||||
// timeframe: Option<Timeframe>,
|
||||
// #[description = "Content to filter on"] filter: Option<String>,
|
||||
// ) -> Result<(), Error> {
|
||||
// if !utils::high_tier(ctx).await {
|
||||
// ctx.say("This command can only be used in high tier channels for security")
|
||||
// .await?;
|
||||
// return Ok(());
|
||||
// }
|
||||
|
||||
match source {
|
||||
Source::Eurosport => eurosport::proc_eurosport(ctx, timeframe, filter).await,
|
||||
Source::Olympics => eurosport::proc_olympics(ctx, timeframe, filter).await,
|
||||
}
|
||||
// match source {
|
||||
// Source::Eurosport => eurosport::proc_eurosport(ctx, timeframe, filter).await,
|
||||
// // Source::Olympics => eurosport::proc_olympics(ctx, timeframe, filter).await,
|
||||
// Source::WRC => wrc::wrc(ctx).await,
|
||||
// Source::Viaplay => viaplay::viaplay(ctx, timeframe, filter).await,
|
||||
// // Source::F1 => f1::proc_f1(ctx, timeframe, filter).await,
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// // Ok(())
|
||||
// }
|
||||
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
subcommands("viaplay::viaplay", "eurosport::eurosport", "wrc::wrc")
|
||||
)]
|
||||
pub async fn links(ctx: Context<'_>) -> Result<(), Error> {
|
||||
ctx.say("Hello there!").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
281
src/commands/links/viaplay.rs
Normal file
281
src/commands/links/viaplay.rs
Normal file
|
@ -0,0 +1,281 @@
|
|||
use std::{collections::HashSet, fmt};
|
||||
|
||||
use crate::{commands::utils, Context, Error};
|
||||
use cached::proc_macro::cached;
|
||||
use chrono::{DateTime, Utc};
|
||||
use futures::{Stream, StreamExt};
|
||||
use log::{info, warn};
|
||||
use reqwest::header::AUTHORIZATION;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::Timeframe;
|
||||
|
||||
// const translations: HashMap<String, String> = HashMap::from([
|
||||
// ("Valioliiga", "Premier league"),
|
||||
// ("Gjensidige Kvindeliga", "Mutual Women's League (Norway)"),
|
||||
// ("Tanskan 1. divisioona", "Danish 1st Division"),
|
||||
// ("Bundesliiga", "Bundesliga"),
|
||||
// ("2. Bundesliiga", "2. Bundesliga"),
|
||||
// ]);
|
||||
|
||||
// #[derive(Deserialize, Clone)]
|
||||
// struct ViaplaySchedule {
|
||||
// events: Vec<ViaplayEvent>,
|
||||
// }
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct ViaplayEvent {
|
||||
content: Content,
|
||||
#[serde(rename = "epg")]
|
||||
times: EPG,
|
||||
system: System,
|
||||
}
|
||||
|
||||
impl ViaplayEvent {
|
||||
fn filter(&self, filter: &str) -> bool {
|
||||
if filter.is_empty() {
|
||||
return true;
|
||||
};
|
||||
if self.content.format.sport.contains(filter) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn comp(&self, when: &Option<Timeframe>) -> bool {
|
||||
let now = Utc::now();
|
||||
match when {
|
||||
Some(Timeframe::Everything) => true,
|
||||
Some(Timeframe::Current) => self.times.start <= now && self.times.end >= now,
|
||||
Some(Timeframe::Future) => self.times.end >= now,
|
||||
Some(Timeframe::Past) => self.times.end <= now,
|
||||
_ => self.times.end >= now,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
format!("```md\n[{sport}]({title}) {synopsis}```(<t:{start}:R>-<t:{end}:R>) {desc}\nhttps://tom.al/ms/vp/{id}", 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 check_sport(&self, sport: &Option<String>) -> bool {
|
||||
match sport {
|
||||
None => true,
|
||||
Some(s) => self.content.format.sport.contains(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ViaplayEvent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Use `self.number` to refer to each positional data point.
|
||||
// write!(f, "```md\n[{sport}]({title}) {synopsis}```(<t:{start}:R>-<t:{end}:R>) {desc}\nhttps://tom.al/ms/vp/{id}", 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)
|
||||
write!(f, "{}", self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Content {
|
||||
format: Format,
|
||||
title: String,
|
||||
#[serde(rename = "originalTitle")]
|
||||
#[serde(default)]
|
||||
description: String,
|
||||
#[serde(default)]
|
||||
synopsis: String,
|
||||
}
|
||||
|
||||
fn default_sport() -> String {
|
||||
"Unknown".to_string()
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Format {
|
||||
#[serde(rename = "title")]
|
||||
#[serde(default = "default_sport")]
|
||||
#[serde(with = "viaplay_sport")]
|
||||
sport: String,
|
||||
}
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct EPG {
|
||||
#[serde(with = "viaplay_date")]
|
||||
start: DateTime<Utc>,
|
||||
#[serde(with = "viaplay_date")]
|
||||
end: DateTime<Utc>,
|
||||
}
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct System {
|
||||
#[serde(rename = "productKey")]
|
||||
product_key: String,
|
||||
}
|
||||
|
||||
mod viaplay_date {
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
||||
const FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S.%fZ";
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Utc.datetime_from_str(&s, FORMAT)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
mod viaplay_sport {
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
match s.as_str() {
|
||||
"Valioliiga" => Ok("Premier league".to_string()),
|
||||
"Gjensidige Kvindeliga" => Ok("Mutual Women's League (Norway)".to_string()),
|
||||
"Tanskan 1. divisioona" => Ok("Danish 1st Division".to_string()),
|
||||
"Bundesliiga" => Ok("Bundesliga".to_string()),
|
||||
"2. Bundesliiga" => Ok("2. Bundesliga".to_string()),
|
||||
"Hevosurheilu" => Ok("Equestrian sport".to_string()),
|
||||
_ => Ok(s.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cached(time = 3600)]
|
||||
async fn get_schedule() -> Option<Vec<ViaplayEvent>> {
|
||||
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/viaplay/schedule"
|
||||
))
|
||||
.header(AUTHORIZATION, token)
|
||||
.send()
|
||||
.await;
|
||||
|
||||
let result: Option<Vec<ViaplayEvent>> = 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::<Vec<ViaplayEvent>>().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
|
||||
}
|
||||
|
||||
#[cached(time = 3600)]
|
||||
pub async fn get_sports() -> Vec<String> {
|
||||
// let events = get_schedule();
|
||||
if let Some(events) = get_schedule().await {
|
||||
let mut result: HashSet<String> = HashSet::new();
|
||||
for event in events {
|
||||
result.insert(event.content.format.sport);
|
||||
}
|
||||
return Vec::from_iter(result);
|
||||
} else {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
|
||||
// #[allow(dead_code)]
|
||||
// async fn autocomplete_sport(_ctx: Context<'_>, partial: String) -> impl Stream<Item = String> {
|
||||
// // futures::stream::iter(get_sports().iter())
|
||||
// // .filter(move |name| futures::future::ready(name.contains(&partial)))
|
||||
// // .map(|name| name.to_string())
|
||||
// futures::stream::iter(get_sports().await)
|
||||
// .filter(move |name| futures::future::ready(name.contains(&partial)))
|
||||
// }
|
||||
|
||||
async fn autocomplete_sport<'a>(
|
||||
_ctx: Context<'_>,
|
||||
partial: &'a str,
|
||||
) -> impl Stream<Item = String> + 'a {
|
||||
futures::stream::iter(get_sports().await)
|
||||
.filter(move |name| futures::future::ready(name.contains(&partial)))
|
||||
.map(|name| name.to_string())
|
||||
}
|
||||
|
||||
//Viaplay events listing
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn viaplay(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Filter sessions for when they are/were happening, defaults to future"]
|
||||
timeframe: Option<super::Timeframe>,
|
||||
#[description = "Content to filter on"] filter: Option<String>,
|
||||
#[description = "Filter for which sport to list"]
|
||||
#[autocomplete = "autocomplete_sport"]
|
||||
sport: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let events: Option<Vec<ViaplayEvent>> = get_schedule().await;
|
||||
match events {
|
||||
None => {
|
||||
ctx.say("Unable to get the events, try again later (it's cached so wait a bit...)")
|
||||
.await?;
|
||||
}
|
||||
Some(evs) => {
|
||||
info!("Found {} events from viaplay", evs.len());
|
||||
let filtered: Vec<String> = evs
|
||||
.into_iter()
|
||||
.filter(|e| e.comp(&timeframe))
|
||||
.filter(|e| e.check_sport(&sport))
|
||||
.filter(|e| match &filter {
|
||||
None => true,
|
||||
Some(f) => e.filter(f.as_str()),
|
||||
})
|
||||
.map(|e| e.to_string())
|
||||
.collect();
|
||||
let pages = utils::paginator(filtered, 1900, "\n".to_string());
|
||||
utils::paginate_string(ctx, pages).await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// /// Another subcommand of `parent`
|
||||
// #[poise::command(slash_command)]
|
||||
// pub async fn child3(
|
||||
// ctx: Context<'_>,
|
||||
// #[description = "Where to git the juicy links from?"]
|
||||
// _source: super::Source,
|
||||
// #[description = "Filter sessions for when they are/were happening, defaults to future"]
|
||||
// _timeframe: Option<super::Timeframe>,
|
||||
// #[description = "Content to filter on"] filter: Option<String>,
|
||||
// #[description = "Filter for which sport to list (only Viaplay)"]
|
||||
// #[autocomplete = "autocomplete_sport"]
|
||||
// sport: Option<String>,
|
||||
// ) -> Result<(), Error> {
|
||||
// ctx.say("You invoked the second child command!").await?;
|
||||
// Ok(())
|
||||
// }
|
118
src/commands/links/wrc.rs
Normal file
118
src/commands/links/wrc.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use crate::{commands::utils, Context, Error};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
mod wrc_date {
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
||||
const FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S%:z";
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Utc.datetime_from_str(&s, FORMAT)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WRC {
|
||||
// #[serde(rename = "earliestPlayableStart")]
|
||||
// #[serde(with = "es_date_time")]
|
||||
// earliest_playable_start: DateTime<Utc>,
|
||||
// id: u32,
|
||||
name: String,
|
||||
#[serde(rename = "eventDays")]
|
||||
days: Vec<WRCDays>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WRCDays {
|
||||
// id: u32,
|
||||
#[serde(rename = "eventDay")]
|
||||
event_day: String,
|
||||
#[serde(rename = "spottChannel")]
|
||||
spott_channel: WRCChannel,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WRCChannel {
|
||||
// id: u32,
|
||||
// #[serde(rename = "displayName")]
|
||||
// name: String,
|
||||
assets: Vec<WRCAssets>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WRCAssets {
|
||||
// id: u32,
|
||||
#[serde(with = "wrc_date")]
|
||||
start: DateTime<Utc>,
|
||||
#[serde(with = "wrc_date")]
|
||||
end: DateTime<Utc>,
|
||||
// duration: u32,
|
||||
content: WRCContent,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WRCContent {
|
||||
title: String,
|
||||
id: u32,
|
||||
#[serde(default)]
|
||||
description: Option<String>,
|
||||
}
|
||||
|
||||
async fn get_schedule() -> Result<Option<WRC>, Error> {
|
||||
let client = reqwest::Client::new();
|
||||
let req = client
|
||||
.get("https://api.wrc.com/sdb/rallyevent/active/")
|
||||
.send()
|
||||
.await?;
|
||||
if req.status().as_u16() == 404 {
|
||||
info!("Error 404 on getting wrc schedule");
|
||||
Ok(None)
|
||||
} else if req.status().as_u16() == 200 {
|
||||
let data: WRC = req.json().await?;
|
||||
Ok(Some(data))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
// WRC sessions
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn wrc(
|
||||
ctx: Context<'_>,
|
||||
// #[description = "Filter sessions for when they are/were happening, defaults to future"]
|
||||
// timeframe: Option<super::Timeframe>,
|
||||
// #[description = "Content to filter on"] filter: Option<String>,
|
||||
) -> Result<(), Error> {
|
||||
let wrc = get_schedule().await?;
|
||||
match wrc {
|
||||
None => {
|
||||
ctx.say("No WRC sessions found").await?;
|
||||
Ok(())
|
||||
}
|
||||
Some(wrc_data) => {
|
||||
let mut events: Vec<String> = vec![];
|
||||
for day in wrc_data.days {
|
||||
for session in day.spott_channel.assets {
|
||||
let desc = if let Some(d) = session.content.description {
|
||||
d
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
events.push(format!("{}: <t:{}:R>- <t:{}:R>: [{}](https://morningstreams.com/wrcplayer.html?id={}) {} {}", day.event_day, session.start.timestamp(), session.end.timestamp(), session.content.id, session.content.id, session.content.title, desc));
|
||||
}
|
||||
}
|
||||
let pages = utils::paginator(events, 2400, "\n".to_owned());
|
||||
utils::paginate_string_embed(ctx, format!("WRC {}", wrc_data.name), pages).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue