diff --git a/src/command.rs b/src/command.rs index 4d508d4..bf69c05 100644 --- a/src/command.rs +++ b/src/command.rs @@ -45,6 +45,24 @@ enum Action { #[arg(id = "GUIDE", short = 'g', long = "guide")] guide: String, }, + + /// S'inscrire à un shift + #[command(name = "inscrire")] + Subscribe { + /// L'id du shift + #[arg()] + id: u64, + }, + + // should we add an option to (un)subscribe someone else? + + /// Se désinscrire d'un shift + #[command(name = "désinscrire")] + Unsubscribe { + /// L'id du shift + #[arg()] + id: u64, + }, } fn parse_date(input: &str) -> Res> { @@ -89,7 +107,7 @@ pub fn format_shifts(shifts: Vec) -> String { output } -pub async fn handle(command: impl AsRef, room: Room, client: Client) -> Res<()> { +pub async fn handle(command: impl AsRef, sender: OwnedUserId, room: Room, client: Client) -> Res<()> { debug!("handling command: {}", command.as_ref()); let args = shlex::split(command.as_ref()).ok_or_else(lazyhow!("error: Invalid quoting"))?; @@ -131,6 +149,18 @@ pub async fn handle(command: impl AsRef, room: Room, client: Client) -> Res send_room_msg_html(client, room, format!("Nouveau shift ajouté : {}", shift)) .await?; } + Action::Subscribe { id } => { + debug!("user asked for subscribe"); + let _ = crate::grist::subscribe(id, sender.to_string()).await?; + debug!("subscribe ok"); + // confirm with tick reaction + } + Action::Unsubscribe { id } => { + debug!("user asked for unsubscribe"); + let _ = crate::grist::unsubscribe(id, sender.to_string()).await?; + debug!("unsubscribe ok"); + // confirm with tick reaction + } } } Err(err) => { diff --git a/src/grist.rs b/src/grist.rs index a233992..cf22d91 100644 --- a/src/grist.rs +++ b/src/grist.rs @@ -8,6 +8,7 @@ use grist_client::apis::records_api::{add_records, list_records}; use grist_client::models::{ RecordsList, RecordsListRecordsInner, RecordsWithoutId, RecordsWithoutIdRecordsInner, }; +use serde_json::Value; pub const API_URL: &str = "https://grist.interhacker.space/api"; pub const SHIFTS_DOC: &str = "iwSaC82TsQiuD3MYSG9RXX"; @@ -142,14 +143,14 @@ pub struct Shift { pub id: Option, pub shift_type: CustomOrDefined, pub start_time: DateTime, - pub inscriptions: Vec, + pub inscriptions: Vec, } impl Shift { pub fn new( id: u64, shift_type: CustomOrDefined, start_time: u64, - inscriptions: Vec, + inscriptions: Vec, ) -> Self { Self { id: Some(id), @@ -281,6 +282,20 @@ impl Display for Shift { } } +#[derive(Clone, Debug)] +pub struct Inscription { + pub shift_id: u64, + pub person_name: String, +} +impl Inscription { + pub fn new(shift_id: u64, person_name: String) -> Self { + Self { + shift_id: shift_id, + person_name: person_name, + } + } +} + pub fn get_number(record: &RecordsListRecordsInner, column: &str) -> Res { record .fields @@ -341,6 +356,27 @@ pub async fn get_records(doc: &str, table: &str, sort: Option<&str>) -> Res) -> Res<()> { + let mut fields_map = serde_json::Map::new(); + for (key, val) in fields { + fields_map.insert(key.to_string(), val); + } + let record = RecordsWithoutIdRecordsInner { + fields: fields_map.into(), + }; + let records = RecordsWithoutId { + records: vec![record], + }; + + let conf = conf::get(); + let api_key = conf.grist_api_key(); + let grist_conf = Configuration::new(API_URL.into(), Some(api_key.into())); + add_records(&grist_conf, doc, table, records, None) + .await + .context("Failed to add inscriptions record")?; + Ok(()) +} + pub async fn get_shift_type(index: u64) -> Res { let record_list = get_records(SHIFTS_DOC, SHIFT_TYPES_TABLE, None).await?; let record = record_list @@ -358,16 +394,22 @@ pub async fn get_shift_type(index: u64) -> Res { )) } -pub async fn get_inscription(index: u64) -> Res { - let record_list = get_records(SHIFTS_DOC, INSCRIPTIONS_TABLE, None).await?; +pub async fn get_inscriptions() -> Res { + get_records(SHIFTS_DOC, INSCRIPTIONS_TABLE, None).await +} + +pub async fn get_inscription(index: u64) -> Res { + let record_list = get_inscriptions().await?; let record = record_list .records .iter() .find(|record| record.id == index) .ok_or(anyhow!("can't find inscription"))? .clone(); - let name = get_string(&record, "Personne")?.into(); - Ok(name) + Ok(Inscription::new( + get_number(&record, "Shift")?, + get_string(&record, "Personne")?.into(), + )) } pub fn get_shift_type_custom(record: &RecordsListRecordsInner) -> Res { @@ -417,3 +459,26 @@ pub async fn list_shifts() -> Res> { Ok(shifts) } + +// this is weird that we use both wordings, inscription and (un)subscribe +pub async fn subscribe(id: u64, user_id: String) -> Res<()> { + let inscriptions = get_inscriptions().await?.records; + if let Some(_) = inscriptions.into_iter().find(|inscription| { + get_number(&inscription, "Shift").expect("failed to get shift column") == id + && get_string(&inscription, "Personne").expect("failed to get personne column") + == user_id + }) { + return Ok(()); + } + let fields = vec![ + ("Shift", serde_json::to_value(id)?), + ("Personne", serde_json::to_value(user_id)?), + ]; + add_record(SHIFTS_DOC, INSCRIPTIONS_TABLE, fields).await?; + + Ok(()) +} + +pub async fn unsubscribe(id: u64, user_id: String) -> Res<()> { + Ok(()) +} diff --git a/src/serve/message.rs b/src/serve/message.rs index 8da399a..c80af65 100644 --- a/src/serve/message.rs +++ b/src/serve/message.rs @@ -84,7 +84,7 @@ pub async fn on_room_message( event.sender, text_content.body, body ); - command::handle(body, room, client).await?; + command::handle(body, event.sender, room, client).await?; debug!("message successfully sent"); Ok(())