add subscribe command

This commit is contained in:
Pierre de Lacroix 2026-06-30 18:26:04 +02:00
parent 9a14ae4a62
commit 578f28f51a
Signed by: lateralus23
GPG key ID: 53E0CEC29C24EF39
3 changed files with 103 additions and 8 deletions

View file

@ -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<DateTime<FixedOffset>> {
@ -89,7 +107,7 @@ pub fn format_shifts(shifts: Vec<Shift>) -> String {
output
}
pub async fn handle(command: impl AsRef<str>, room: Room, client: Client) -> Res<()> {
pub async fn handle(command: impl AsRef<str>, 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<str>, 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) => {

View file

@ -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<u64>,
pub shift_type: CustomOrDefined,
pub start_time: DateTime<FixedOffset>,
pub inscriptions: Vec<String>,
pub inscriptions: Vec<Inscription>,
}
impl Shift {
pub fn new(
id: u64,
shift_type: CustomOrDefined,
start_time: u64,
inscriptions: Vec<String>,
inscriptions: Vec<Inscription>,
) -> 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<u64> {
record
.fields
@ -341,6 +356,27 @@ pub async fn get_records(doc: &str, table: &str, sort: Option<&str>) -> Res<Reco
.context("Failed to list records")
}
pub async fn add_record(doc: &str, table: &str, fields: Vec<(&str, Value)>) -> 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<ShiftType> {
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<ShiftType> {
))
}
pub async fn get_inscription(index: u64) -> Res<String> {
let record_list = get_records(SHIFTS_DOC, INSCRIPTIONS_TABLE, None).await?;
pub async fn get_inscriptions() -> Res<RecordsList> {
get_records(SHIFTS_DOC, INSCRIPTIONS_TABLE, None).await
}
pub async fn get_inscription(index: u64) -> Res<Inscription> {
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<CustomShiftValues> {
@ -417,3 +459,26 @@ pub async fn list_shifts() -> Res<Vec<Shift>> {
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(())
}

View file

@ -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(())