use super::utils; use crate::{Context, Error}; use futures::{Stream, StreamExt}; #[allow(dead_code)] async fn autocomplete_series(_ctx: Context<'_>, partial: String) -> impl Stream { futures::stream::iter(&[ "Formula 1", "Formula", "MotoGP", "Moto", "IndyCar", "World Rally Championship", ]) .filter(move |name| futures::future::ready(name.starts_with(&partial))) .map(|name| name.to_string()) } #[allow(dead_code)] async fn autocomplete_session(_ctx: Context<'_>, partial: String) -> impl Stream { futures::stream::iter(&[ "Race", "Qualifying", "Free practice 3", "Free practice 2", "Free practice 1", "Sprint race", ]) .filter(move |name| futures::future::ready(name.starts_with(&partial))) .map(|name| name.to_string()) } /// Get a list of supported racing series #[poise::command{slash_command}] pub async fn series( ctx: Context<'_>, #[description = "Filter for series (These are suggestions)"] #[autocomplete = "autocomplete_series"] serie: Option, ) -> Result<(), Error> { let filter: String = match serie { None => "%".to_string(), Some(s) if s.is_empty() => "%".to_string(), Some(s) => { format!("%{}%", s) } }; let query = sqlx::query!("SELECT name FROM Series WHERE name like ?", filter) .fetch_many(&ctx.data().database); let serie_list = query .filter_map(|result| async move { result.ok()?.right() }) .map(|record| record.name) .collect() .await; let pages = utils::paginator(serie_list, 1000, ", ".to_string()); print!("{:?}", pages); utils::paginate_string(ctx, pages).await?; Ok(()) } #[derive(Debug, poise::SlashChoiceParameter)] pub enum Timeframe { #[name = "Currently happening"] Current, #[name = "Currently happening or starting in the future"] Future, #[name = "Currently happening or already ended"] Past, #[name = "Everything"] Everything, } #[derive(sqlx::FromRow)] struct Session { name: String, start: i64, duration_minutes: i64, #[sqlx(rename = "series_name")] series: String, #[sqlx(rename = "location_name")] location: Option, } impl Session { fn to_string(&self) -> String { match &self.location { None => format!( " - | **{}** `{}`", self.start, (self.start + 60 * self.duration_minutes), self.series, self.name ), Some(l) => format!( " - | **{}** `{}` @*{}*", self.start, (self.start + 60 * self.duration_minutes), self.series, self.name, l ), } } } /// Get a list of racing sessions #[poise::command{slash_command}] pub async fn sessions( ctx: Context<'_>, #[description = "Filter for the session"] filter: Option, #[description = "Filter sessions for when they are/were happening, defaults to future"] timeframe: Option, ) -> Result<(), Error> { let filter_f: String = match filter { None => "%".to_string(), Some(s) if s.is_empty() => "%".to_string(), Some(s) => { format!("%{}%", s) } }; let mut base_query: String = "SELECT name, start, duration_minutes, series_name, location_name FROM sessions WHERE (series_name LIKE ? or location_name like ? or name like ?)".to_string(); let time_query = match timeframe { Some(Timeframe::Everything) => " ORDER BY abs(julianday() - julianday(start, 'unixepoch')) ASC", Some(Timeframe::Current) => " AND (DATETIME(start, 'unixepoch') > DATETIME() AND DATETIME((start+60*duration_minutes), 'unixepoch') < DATETIME()) ORDER BY start ASC", Some(Timeframe::Past) => " AND DATETIME(start, 'unixepoch') < DATETIME() ORDER BY start DESC", _ => " AND DATETIME((start+60*duration_minutes), 'unixepoch') > DATETIME() ORDER BY start ASC", }; base_query.push_str(time_query); let stream: Vec = sqlx::query_as::<_, Session>(&base_query) .bind(&filter_f) .bind(&filter_f) .bind(&filter_f) .fetch_all(&ctx.data().database) .await?; let sessions: Vec = stream.iter().map(|s| s.to_string()).collect(); let pages = utils::paginator(sessions, 1900, "\n".to_string()); utils::paginate_string(ctx, pages).await?; Ok(()) } #[derive(sqlx::FromRow)] struct Event { // name: String, #[sqlx(rename = "start_date")] start: String, #[sqlx(rename = "end_date")] end: Option, description: String, #[sqlx(rename = "series_name")] series: String, #[sqlx(rename = "location_name")] location: Option, } impl Event { fn to_string(&self) -> String { let mut result: String = match &self.end { None => format!("`{}`", self.start), Some(end) => format!("`{}` - `{}`", self.start, end), }; result.push_str(&format!(" | **{}** {}", self.series, self.description)); if let Some(loc) = &self.location { result.push_str(&format!("Location: {}", loc)); }; return result; } } /// Get a list of racing events #[poise::command{slash_command}] pub async fn events( ctx: Context<'_>, #[description = "Filter for the session"] filter: Option, #[description = "Filter events for when they are/were happening, defaults to future"] timeframe: Option, ) -> Result<(), Error> { let filter_f: String = match filter { None => "%".to_string(), Some(s) if s.is_empty() => "%".to_string(), Some(s) => { format!("%{}%", s) } }; let mut base_query: String = "SELECT start_date, end_date, description, series_name, location_name FROM events WHERE (series_name LIKE ? or location_name like ? or description like ?)".to_string(); let time_query = match timeframe { Some(Timeframe::Everything) => " ORDER BY abs(julianday() - julianday(start_date)) ASC", Some(Timeframe::Current) => " AND ((end_date is NULL and start_date == date()) OR (start_date <= date() AND end_date >= date())) ORDER BY start_date ASC, end_date ASC", Some(Timeframe::Past) => " AND JULIANDAY(start_date) - JULIANDAY(date()) <= 0 ORDER BY start DESC", _ => " AND start_date >= DATE() ORDER BY start_date ASC, end_date ASC", }; base_query.push_str(time_query); let stream: Vec = sqlx::query_as::<_, Event>(&base_query) .bind(&filter_f) .bind(&filter_f) .bind(&filter_f) .fetch_all(&ctx.data().database) .await?; let events: Vec = stream.iter().map(|e| e.to_string()).collect(); let pages = utils::paginator(events, 1900, "\n".to_string()); utils::paginate_string(ctx, pages).await?; Ok(()) } /// Overall information about racing events and sessions #[poise::command(slash_command)] pub async fn planning(ctx: Context<'_>) -> Result<(), Error> { ctx.say("Hey this is a parent command, how'd you get here?") .await?; Ok(()) } pub fn get_command() -> poise::Command< super::super::Data, Box<(dyn std::error::Error + Sync + std::marker::Send + 'static)>, > { poise::Command { subcommands: vec![ series(), sessions(), events(), // Let's make sure poise isn't confused by the duplicate names! // planning(), ], ..planning() } }