use crate::commands::{EmbedField, HumanNaiveDate}; use crate::db::users::User; use crate::{Context, Error}; use affluences_api::{Reservation, Resource}; use chrono::{NaiveDate, NaiveTime}; use uuid::{uuid, Uuid}; const STERRE_BIB_ID: Uuid = uuid!("4737e57a-ee05-4f7b-901a-7bb541eeb297"); const TIME_FORMAT: &str = "%H:%M"; /// Parent command for all bib-related commands /// /// Commands for reservating study rooms in the bib. #[poise::command(prefix_command, slash_command, subcommands("available", "book"))] pub async fn bib(_ctx: Context<'_>) -> Result<(), Error> { Ok(()) } fn resource_to_embed_field(resource: Resource) -> EmbedField { let available_hours = resource.condensed_available_hours(); let title = format!("{} ({}p)", resource.resource_name, resource.capacity); if available_hours.is_empty() { (title, "Nothing available.".to_string(), false) } else { ( title, available_hours .into_iter() .map(|(start_block, duration)| { format!( "{} - {} ({:02}:{:02})", start_block.hour.format(TIME_FORMAT), (start_block.hour + duration).format(TIME_FORMAT), duration.num_hours(), duration.num_minutes() % 60 ) }) .collect::>() .join("\n"), false, ) } } /// List available timeslots for day #[poise::command(prefix_command, slash_command)] pub async fn available(ctx: Context<'_>, date: HumanNaiveDate) -> Result<(), Error> { let client = &ctx.data().client; let date: NaiveDate = date.into(); let mut resources = client.available(STERRE_BIB_ID, date, 1).await?; // Cloning here isn't super efficient, but this list only consists of a handful of elements so // it's fine resources.sort_by_key(|k| k.resource_name.clone()); ctx.send(|f| { f.embed(|e| { e.description(format!("Available booking dates for {}.", date)) .fields( resources .into_iter() .map(resource_to_embed_field) .collect::>(), ) }) }) .await?; Ok(()) } #[poise::command(prefix_command, slash_command)] pub async fn book( ctx: Context<'_>, date: HumanNaiveDate, start_time: NaiveTime, end_time: NaiveTime, #[description = "Minimum seats the room should have."] capacity: Option, ) -> Result<(), Error> { if ctx.guild_id().is_none() { ctx.say("You have to send this message from a guild.") .await?; return Ok(()); } let guild_id = ctx.guild_id().unwrap(); let discord_id = ctx.author().id.0 as i64; let date: NaiveDate = date.into(); let user = { let mut conn = ctx.data().pool.get()?; User::get(&mut conn, guild_id.into(), discord_id)? }; if user.is_none() { ctx.say("You have to register before being able to book reservations.") .await?; return Ok(()); } let user = user.unwrap(); let client = &ctx.data().client; let resources = client.available(STERRE_BIB_ID, date, 1).await?; let chosen_resource = resources .iter() .filter(|r| capacity.is_none() || capacity.unwrap() <= r.capacity) .find(|r| r.has_slot(start_time, end_time, 1)); if let Some(chosen_resource) = chosen_resource { let reservation = Reservation { auth_type: None, email: user.email.clone(), date, start_time, end_time, note: "coworking space".to_string(), user_firstname: user.first_name, user_lastname: user.last_name, user_phone: None, person_count: capacity.unwrap_or(1), }; client .make_reservation(chosen_resource.resource_id, &reservation) .await?; ctx.send(|f| { f.embed(|e| { e.description("A new reservation has been made.") .field("when", format!("{} {} - {}", date, start_time.format(TIME_FORMAT), end_time.format(TIME_FORMAT)), false) .field("where", &chosen_resource.resource_name, false) .footer(|ft| ft.text( format!("A confirmation mail has been sent to {}. Please check your email and confirm your reservation within two hours.", user.email))) }) }) .await?; } else { ctx.say("No slot is available within your requested bounds.") .await?; } // let resources = if let Some(capacity) = capacity { // resources.filter(|r| r.capacity >= capacity) // }; Ok(()) } // Create a reservation // #[poise::command(prefix_command, slash_command)] // pub async fn reserve( // ctx: Context<'_>, // date: NaiveDate, // ) -> Result<(), Error> { // }