diff --git a/src/commands.rs b/src/commands.rs index 49deeda..2eeceaf 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1192,14 +1192,17 @@ async fn room_access_allowed( if let Some(required_gender) = room.gender_restriction_id { if required_gender > 0 && user_gender_id != Some(required_gender) { if room_debug_enabled() { + let (male_id, female_id) = configured_gender_ids(); eprintln!( - "[yourchat2][room-debug][access] denied=room_gender_restricted room='{}' required_gender_id={} required_gender='{}' user_gender_id={:?} user_gender='{}' user_falukant_id={:?}", + "[yourchat2][room-debug][access] denied=room_gender_restricted room='{}' required_gender_id={} required_gender='{}' user_gender_id={:?} user_gender='{}' user_falukant_id={:?} configured_gender_ids={{male:{},female:{}}}", room.name, required_gender, gender_label_from_id(Some(required_gender)), user_gender_id, gender_label_from_id(user_gender_id), - falukant_user_id + falukant_user_id, + male_id, + female_id ); } return Err("room_gender_restricted"); @@ -1639,9 +1642,10 @@ fn value_as_string(value: &Value) -> Option { } fn parse_gender_mf(value: &str) -> Option> { + let (male_id, female_id) = configured_gender_ids(); match value.trim().to_lowercase().as_str() { - "m" | "male" => Some(Some(1)), - "f" | "female" => Some(Some(2)), + "m" | "male" => Some(Some(male_id)), + "f" | "female" => Some(Some(female_id)), "any" | "none" | "-" => Some(None), _ => None, } @@ -1662,14 +1666,29 @@ fn room_debug_enabled() -> bool { } fn gender_label_from_id(gender_id: Option) -> &'static str { + let (male_id, female_id) = configured_gender_ids(); match gender_id { - Some(1) => "m", - Some(2) => "f", + Some(v) if v == male_id => "m", + Some(v) if v == female_id => "f", Some(_) => "unknown", None => "none", } } +fn configured_gender_ids() -> (i32, i32) { + let male_id = env::var("CHAT_GENDER_MALE_ID") + .ok() + .and_then(|v| v.trim().parse::().ok()) + .filter(|v| *v > 0) + .unwrap_or(1); + let female_id = env::var("CHAT_GENDER_FEMALE_ID") + .ok() + .and_then(|v| v.trim().parse::().ok()) + .filter(|v| *v > 0) + .unwrap_or(2); + (male_id, female_id) +} + fn command_from_chat_line(message: &str, token: Option) -> Option { let trimmed = message.trim(); if !trimmed.starts_with('/') { diff --git a/src/db.rs b/src/db.rs index 656fe6b..e005905 100644 --- a/src/db.rs +++ b/src/db.rs @@ -340,6 +340,10 @@ fn decrypt_aes256_ecb_hex(encrypted_hex: &str) -> Option { } async fn load_user_gender(client: Arc, user_id: i32) -> Option { + if let Some(mapped) = load_user_gender_from_user_param(client.clone(), user_id).await { + return Some(mapped); + } + for query in [ "SELECT gender_restriction_id AS gender_id FROM community.\"user\" WHERE id = $1 LIMIT 1", "SELECT gender_type_id AS gender_id FROM community.\"user\" WHERE id = $1 LIMIT 1", @@ -357,6 +361,67 @@ async fn load_user_gender(client: Arc, user_id: i32) -> Option { Err(_) => {} } } + for description in ["gender", "sex", "geschlecht"] { + if let Ok(Some(row)) = client + .query_opt( + "SELECT up.value + FROM community.user_param up + JOIN \"type\".user_param tp ON up.param_type_id = tp.id + WHERE up.user_id = $1 AND tp.description = $2 + LIMIT 1", + &[&user_id, &description], + ) + .await + { + if let Some(value) = row.get::<_, Option>("value") { + if let Some(parsed) = normalize_gender_value(&value) { + return Some(parsed); + } + } + } + } + None +} + +async fn load_user_gender_from_user_param(client: Arc, user_id: i32) -> Option { + let row = client + .query_opt( + "SELECT + up.value AS raw_value, + tpv_id.value AS mapped_by_id_value, + tpv_order.value AS mapped_by_order_value + FROM community.user_param up + JOIN \"type\".user_param tp + ON up.param_type_id = tp.id + LEFT JOIN \"type\".user_param_value tpv_id + ON tpv_id.user_param_type_id = tp.id + AND tpv_id.id::text = up.value + LEFT JOIN \"type\".user_param_value tpv_order + ON tpv_order.user_param_type_id = tp.id + AND tpv_order.order_id::text = up.value + WHERE up.user_id = $1 + AND tp.description = 'gender' + LIMIT 1", + &[&user_id], + ) + .await + .ok()??; + + let raw_value = row.get::<_, Option>("raw_value"); + let mapped_by_id_value = row.get::<_, Option>("mapped_by_id_value"); + let mapped_by_order_value = row.get::<_, Option>("mapped_by_order_value"); + + for candidate in [ + mapped_by_id_value.as_deref(), + mapped_by_order_value.as_deref(), + raw_value.as_deref(), + ] { + if let Some(value) = candidate { + if let Some(parsed) = normalize_gender_value(value) { + return Some(parsed); + } + } + } None } @@ -499,3 +564,33 @@ async fn resolve_owner_falukant_id(client: Arc, room_owner_id: i32) -> .ok()??; row.get::<_, Option>("falukant_user_id") } + +fn normalize_gender_value(raw: &str) -> Option { + let trimmed = raw.trim(); + if trimmed.is_empty() { + return None; + } + if let Ok(id) = trimmed.parse::() { + return (id > 0).then_some(id); + } + let (male_id, female_id) = configured_gender_ids(); + match trimmed.to_lowercase().as_str() { + "m" | "male" | "mann" | "männlich" | "maennlich" | "transmale" => Some(male_id), + "f" | "female" | "frau" | "weiblich" | "transfemale" => Some(female_id), + _ => None, + } +} + +fn configured_gender_ids() -> (i32, i32) { + let male_id = env::var("CHAT_GENDER_MALE_ID") + .ok() + .and_then(|v| v.trim().parse::().ok()) + .filter(|v| *v > 0) + .unwrap_or(1); + let female_id = env::var("CHAT_GENDER_FEMALE_ID") + .ok() + .and_then(|v| v.trim().parse::().ok()) + .filter(|v| *v > 0) + .unwrap_or(2); + (male_id, female_id) +}