Füge Fehlerberichterstattung für Benachrichtigungen über Charaktertode hinzu und verhindere wiederholte Meldungen innerhalb von fünf Minuten.
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 2m53s
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 2m53s
This commit is contained in:
@@ -3,8 +3,9 @@ use postgres::types::Type;
|
|||||||
use postgres::Error as PgError;
|
use postgres::Error as PgError;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::{Arc, Condvar, Mutex};
|
use std::panic::Location;
|
||||||
use std::time::Duration;
|
use std::sync::{Arc, Condvar, Mutex, OnceLock};
|
||||||
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
pub type Row = HashMap<String, String>;
|
pub type Row = HashMap<String, String>;
|
||||||
pub type Rows = Vec<Row>;
|
pub type Rows = Vec<Row>;
|
||||||
@@ -55,6 +56,30 @@ struct Database {
|
|||||||
prepared: HashMap<String, String>,
|
prepared: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verhindert, dass ein fehlerhafter Sekundentick die Admin-Benachrichtigungen
|
||||||
|
/// flutet. Derselbe Aufrufer/dasselbe Statement wird höchstens alle fünf Minuten
|
||||||
|
/// gemeldet; der Fehler bleibt unabhängig davon im Daemon-Log sichtbar.
|
||||||
|
static LAST_ADMIN_ERROR_AT: OnceLock<Mutex<HashMap<String, u64>>> = OnceLock::new();
|
||||||
|
const ADMIN_ERROR_COOLDOWN_SECS: u64 = 5 * 60;
|
||||||
|
|
||||||
|
fn should_report_to_admins(key: &str) -> bool {
|
||||||
|
let now = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_secs();
|
||||||
|
let reports = LAST_ADMIN_ERROR_AT.get_or_init(|| Mutex::new(HashMap::new()));
|
||||||
|
let Ok(mut reports) = reports.lock() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
match reports.get(key) {
|
||||||
|
Some(last) if now.saturating_sub(*last) < ADMIN_ERROR_COOLDOWN_SECS => false,
|
||||||
|
_ => {
|
||||||
|
reports.insert(key.to_string(), now);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
fn connect(conn_str: &str) -> Result<Self, DbError> {
|
fn connect(conn_str: &str) -> Result<Self, DbError> {
|
||||||
let client = Client::connect(conn_str, NoTls)?;
|
let client = Client::connect(conn_str, NoTls)?;
|
||||||
@@ -71,10 +96,88 @@ impl Database {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
fn report_database_error(
|
||||||
fn query(&mut self, sql: &str) -> Result<Rows, DbError> {
|
&mut self,
|
||||||
let rows = self.client.query(sql, &[])?;
|
statement: &str,
|
||||||
Ok(rows.into_iter().map(Self::row_to_map).collect())
|
sql: &str,
|
||||||
|
error: &DbError,
|
||||||
|
caller: &'static Location<'static>,
|
||||||
|
) {
|
||||||
|
let key = format!("{}:{}:{statement}", caller.file(), caller.line());
|
||||||
|
if !should_report_to_admins(&key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_SQL_LEN: usize = 4_000;
|
||||||
|
let sql_preview = if sql.len() > MAX_SQL_LEN {
|
||||||
|
format!("{} …", &sql[..MAX_SQL_LEN])
|
||||||
|
} else {
|
||||||
|
sql.to_string()
|
||||||
|
};
|
||||||
|
let payload = serde_json::json!({
|
||||||
|
"event": "daemon_database_error",
|
||||||
|
"module": caller.file(),
|
||||||
|
"line": caller.line(),
|
||||||
|
"statement": statement,
|
||||||
|
"sql": sql_preview,
|
||||||
|
"error": error.to_string(),
|
||||||
|
"deduplication_seconds": ADMIN_ERROR_COOLDOWN_SECS,
|
||||||
|
})
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
// `falukant_log.notification.user_id` erwartet die Falukant-User-ID;
|
||||||
|
// Rechte liegen dagegen beim verknüpften Community-User.
|
||||||
|
let admin_rows = match self.client.query(
|
||||||
|
r#"
|
||||||
|
SELECT DISTINCT fu.id
|
||||||
|
FROM falukant_data.falukant_user fu
|
||||||
|
JOIN community.user_right ur ON ur.user_id = fu.user_id
|
||||||
|
JOIN "type".user_right rt ON rt.id = ur.right_type_id
|
||||||
|
WHERE LOWER(COALESCE(rt.title, '')) LIKE '%admin%'
|
||||||
|
"#,
|
||||||
|
&[],
|
||||||
|
) {
|
||||||
|
Ok(rows) => rows,
|
||||||
|
Err(report_err) => {
|
||||||
|
eprintln!(
|
||||||
|
"[Database] Admin-Benachrichtigung konnte keine Admins laden: {report_err}"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for row in admin_rows {
|
||||||
|
let Ok(user_id) = row.try_get::<_, i32>(0) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Err(report_err) = self.client.execute(
|
||||||
|
r#"
|
||||||
|
INSERT INTO falukant_log.notification
|
||||||
|
(user_id, tr, shown, created_at, updated_at)
|
||||||
|
VALUES ($1, $2, FALSE, NOW(), NOW())
|
||||||
|
"#,
|
||||||
|
&[&user_id, &payload],
|
||||||
|
) {
|
||||||
|
eprintln!(
|
||||||
|
"[Database] Admin-Benachrichtigung für user_id={user_id} fehlgeschlagen: {report_err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query(
|
||||||
|
&mut self,
|
||||||
|
sql: &str,
|
||||||
|
caller: &'static Location<'static>,
|
||||||
|
) -> Result<Rows, DbError> {
|
||||||
|
match self.client.query(sql, &[]) {
|
||||||
|
Ok(rows) => Ok(rows.into_iter().map(Self::row_to_map).collect()),
|
||||||
|
Err(err) => {
|
||||||
|
let error = DbError::from(err);
|
||||||
|
self.report_database_error("raw_query", sql, &error, caller);
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, name: &str, sql: &str) -> Result<(), DbError> {
|
fn prepare(&mut self, name: &str, sql: &str) -> Result<(), DbError> {
|
||||||
@@ -86,13 +189,15 @@ impl Database {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
params: &[&(dyn postgres::types::ToSql + Sync)],
|
params: &[&(dyn postgres::types::ToSql + Sync)],
|
||||||
|
caller: &'static Location<'static>,
|
||||||
) -> Result<Rows, DbError> {
|
) -> Result<Rows, DbError> {
|
||||||
let sql = self
|
let sql = self
|
||||||
.prepared
|
.prepared
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| DbError::new(format!("Unbekanntes Statement: {name}")))?;
|
.ok_or_else(|| DbError::new(format!("Unbekanntes Statement: {name}")))?
|
||||||
|
.clone();
|
||||||
|
|
||||||
match self.client.query(sql.as_str(), params) {
|
match self.client.query(&sql, params) {
|
||||||
Ok(rows) => Ok(rows.into_iter().map(Self::row_to_map).collect()),
|
Ok(rows) => Ok(rows.into_iter().map(Self::row_to_map).collect()),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if let Some(db_err) = err.as_db_error() {
|
if let Some(db_err) = err.as_db_error() {
|
||||||
@@ -107,14 +212,18 @@ impl Database {
|
|||||||
sql_preview.truncate(MAX_SQL_PREVIEW);
|
sql_preview.truncate(MAX_SQL_PREVIEW);
|
||||||
sql_preview.push_str(" …");
|
sql_preview.push_str(" …");
|
||||||
}
|
}
|
||||||
Err(DbError::new(format!(
|
let error = DbError::new(format!(
|
||||||
"Postgres-Fehler bei Statement '{name}': {} (SQLSTATE: {}, Detail: {}, Hint: {}) | SQL: {}",
|
"Postgres-Fehler bei Statement '{name}': {} (SQLSTATE: {}, Detail: {}, Hint: {}) | SQL: {}",
|
||||||
message, code, detail, hint, sql_preview
|
message, code, detail, hint, sql_preview
|
||||||
)))
|
));
|
||||||
|
self.report_database_error(name, &sql, &error, caller);
|
||||||
|
Err(error)
|
||||||
} else {
|
} else {
|
||||||
Err(DbError::new(format!(
|
let error = DbError::new(format!(
|
||||||
"Postgres-Fehler (Client) bei Statement '{name}': {err}"
|
"Postgres-Fehler (Client) bei Statement '{name}': {err}"
|
||||||
)))
|
));
|
||||||
|
self.report_database_error(name, &sql, &error, caller);
|
||||||
|
Err(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,20 +362,22 @@ pub struct DbConnection {
|
|||||||
|
|
||||||
impl DbConnection {
|
impl DbConnection {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[track_caller]
|
||||||
pub fn query(&mut self, sql: &str) -> Result<Rows, DbError> {
|
pub fn query(&mut self, sql: &str) -> Result<Rows, DbError> {
|
||||||
self.database_mut().query(sql)
|
self.database_mut().query(sql, Location::caller())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare(&mut self, name: &str, sql: &str) -> Result<(), DbError> {
|
pub fn prepare(&mut self, name: &str, sql: &str) -> Result<(), DbError> {
|
||||||
self.database_mut().prepare(name, sql)
|
self.database_mut().prepare(name, sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
params: &[&(dyn postgres::types::ToSql + Sync)],
|
params: &[&(dyn postgres::types::ToSql + Sync)],
|
||||||
) -> Result<Rows, DbError> {
|
) -> Result<Rows, DbError> {
|
||||||
self.database_mut().execute(name, params)
|
self.database_mut().execute(name, params, Location::caller())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn database_mut(&mut self) -> &mut Database {
|
fn database_mut(&mut self) -> &mut Database {
|
||||||
@@ -283,5 +394,3 @@ impl Drop for DbConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1923,12 +1923,14 @@ impl EventsWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "director_death", user_id)
|
death_log::wrap_death_notification(c, "director_death", user_id)
|
||||||
});
|
});
|
||||||
Self::notify_user(
|
if let Err(e) = Self::notify_user(
|
||||||
pool,
|
pool,
|
||||||
broker,
|
broker,
|
||||||
user_id,
|
user_id,
|
||||||
tr.as_deref().unwrap_or("director_death"),
|
tr.as_deref().unwrap_or("director_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[EventsWorker] director_death notification: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,12 +1945,14 @@ impl EventsWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "relationship_death", related_user_id)
|
death_log::wrap_death_notification(c, "relationship_death", related_user_id)
|
||||||
});
|
});
|
||||||
Self::notify_user(
|
if let Err(e) = Self::notify_user(
|
||||||
pool,
|
pool,
|
||||||
broker,
|
broker,
|
||||||
related_user_id,
|
related_user_id,
|
||||||
tr.as_deref().unwrap_or("relationship_death"),
|
tr.as_deref().unwrap_or("relationship_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[EventsWorker] relationship_death notification: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1981,12 +1985,14 @@ impl EventsWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "child_death", father_user_id)
|
death_log::wrap_death_notification(c, "child_death", father_user_id)
|
||||||
});
|
});
|
||||||
Self::notify_user(
|
if let Err(e) = Self::notify_user(
|
||||||
pool,
|
pool,
|
||||||
broker,
|
broker,
|
||||||
father_user_id,
|
father_user_id,
|
||||||
tr.as_deref().unwrap_or("child_death"),
|
tr.as_deref().unwrap_or("child_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[EventsWorker] child_death notification (father): {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(mother_user_id) = row
|
if let Some(mother_user_id) = row
|
||||||
.get("mother_user_id")
|
.get("mother_user_id")
|
||||||
@@ -1995,12 +2001,14 @@ impl EventsWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "child_death", mother_user_id)
|
death_log::wrap_death_notification(c, "child_death", mother_user_id)
|
||||||
});
|
});
|
||||||
Self::notify_user(
|
if let Err(e) = Self::notify_user(
|
||||||
pool,
|
pool,
|
||||||
broker,
|
broker,
|
||||||
mother_user_id,
|
mother_user_id,
|
||||||
tr.as_deref().unwrap_or("child_death"),
|
tr.as_deref().unwrap_or("child_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[EventsWorker] child_death notification (mother): {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2150,7 +2150,14 @@ pub const QUERY_GET_MARRIAGE_BIRTH_DELIVERIES: &str = r#"
|
|||||||
r.id AS relationship_id,
|
r.id AS relationship_id,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
||||||
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.title_of_nobility ELSE c2.title_of_nobility END AS title_of_nobility,
|
-- Kinder eines Spielers erben stets dessen Titel, unabhängig vom Geschlecht.
|
||||||
|
-- Bei reinen NPC-Paaren bleibt der Titel des Vaters maßgeblich.
|
||||||
|
CASE
|
||||||
|
WHEN c1.user_id IS NOT NULL THEN c1.title_of_nobility
|
||||||
|
WHEN c2.user_id IS NOT NULL THEN c2.title_of_nobility
|
||||||
|
WHEN c1.gender = 'male' THEN c1.title_of_nobility
|
||||||
|
ELSE c2.title_of_nobility
|
||||||
|
END AS title_of_nobility,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
||||||
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
||||||
@@ -2204,7 +2211,12 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#"
|
|||||||
r.id AS relationship_id,
|
r.id AS relationship_id,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
||||||
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.title_of_nobility ELSE c2.title_of_nobility END AS title_of_nobility,
|
CASE
|
||||||
|
WHEN c1.user_id IS NOT NULL THEN c1.title_of_nobility
|
||||||
|
WHEN c2.user_id IS NOT NULL THEN c2.title_of_nobility
|
||||||
|
WHEN c1.gender = 'male' THEN c1.title_of_nobility
|
||||||
|
ELSE c2.title_of_nobility
|
||||||
|
END AS title_of_nobility,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
||||||
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
||||||
@@ -2269,7 +2281,12 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#"
|
|||||||
SELECT
|
SELECT
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
||||||
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.title_of_nobility ELSE c2.title_of_nobility END AS title_of_nobility,
|
CASE
|
||||||
|
WHEN c1.user_id IS NOT NULL THEN c1.title_of_nobility
|
||||||
|
WHEN c2.user_id IS NOT NULL THEN c2.title_of_nobility
|
||||||
|
WHEN c1.gender = 'male' THEN c1.title_of_nobility
|
||||||
|
ELSE c2.title_of_nobility
|
||||||
|
END AS title_of_nobility,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
||||||
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
||||||
@@ -2343,7 +2360,12 @@ pub const QUERY_GET_PLANNED_CHARACTER_BIRTH_DELIVERIES: &str = r#"
|
|||||||
SELECT
|
SELECT
|
||||||
c_m.id AS mother_cid,
|
c_m.id AS mother_cid,
|
||||||
c_f.id AS father_cid,
|
c_f.id AS father_cid,
|
||||||
CASE WHEN c_f.gender = 'male' THEN c_f.title_of_nobility ELSE c_m.title_of_nobility END AS title_of_nobility,
|
CASE
|
||||||
|
WHEN c_f.user_id IS NOT NULL THEN c_f.title_of_nobility
|
||||||
|
WHEN c_m.user_id IS NOT NULL THEN c_m.title_of_nobility
|
||||||
|
WHEN c_f.gender = 'male' THEN c_f.title_of_nobility
|
||||||
|
ELSE c_m.title_of_nobility
|
||||||
|
END AS title_of_nobility,
|
||||||
CASE WHEN c_f.gender = 'male' THEN c_f.last_name ELSE c_m.last_name END AS last_name,
|
CASE WHEN c_f.gender = 'male' THEN c_f.last_name ELSE c_m.last_name END AS last_name,
|
||||||
CASE WHEN c_f.gender = 'male' THEN c_f.region_id ELSE c_m.region_id END AS region_id,
|
CASE WHEN c_f.gender = 'male' THEN c_f.region_id ELSE c_m.region_id END AS region_id,
|
||||||
fu_f.id AS father_uid,
|
fu_f.id AS father_uid,
|
||||||
@@ -3899,7 +3921,12 @@ pub const QUERY_GET_LOVER_PREGNANCY_CANDIDATES: &str = r#"
|
|||||||
SELECT
|
SELECT
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END AS father_cid,
|
||||||
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END AS mother_cid,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.title_of_nobility ELSE c2.title_of_nobility END AS title_of_nobility,
|
CASE
|
||||||
|
WHEN c1.user_id IS NOT NULL THEN c1.title_of_nobility
|
||||||
|
WHEN c2.user_id IS NOT NULL THEN c2.title_of_nobility
|
||||||
|
WHEN c1.gender = 'male' THEN c1.title_of_nobility
|
||||||
|
ELSE c2.title_of_nobility
|
||||||
|
END AS title_of_nobility,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
CASE WHEN c1.gender = 'male' THEN c1.last_name ELSE c2.last_name END AS last_name,
|
||||||
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
||||||
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
||||||
|
|||||||
@@ -864,11 +864,13 @@ impl UserCharacterWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "director_death", user_id)
|
death_log::wrap_death_notification(c, "director_death", user_id)
|
||||||
});
|
});
|
||||||
self.notify_user_death(
|
if let Err(e) = self.notify_user_death(
|
||||||
&mut conn,
|
&mut conn,
|
||||||
user_id,
|
user_id,
|
||||||
tr.as_deref().unwrap_or("director_death"),
|
tr.as_deref().unwrap_or("director_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[UserCharacterWorker] director_death notification: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,11 +883,13 @@ impl UserCharacterWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "relationship_death", related_user_id)
|
death_log::wrap_death_notification(c, "relationship_death", related_user_id)
|
||||||
});
|
});
|
||||||
self.notify_user_death(
|
if let Err(e) = self.notify_user_death(
|
||||||
&mut conn,
|
&mut conn,
|
||||||
related_user_id,
|
related_user_id,
|
||||||
tr.as_deref().unwrap_or("relationship_death"),
|
tr.as_deref().unwrap_or("relationship_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[UserCharacterWorker] relationship_death notification: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,11 +903,13 @@ impl UserCharacterWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "child_death", father_user_id)
|
death_log::wrap_death_notification(c, "child_death", father_user_id)
|
||||||
});
|
});
|
||||||
self.notify_user_death(
|
if let Err(e) = self.notify_user_death(
|
||||||
&mut conn,
|
&mut conn,
|
||||||
father_user_id,
|
father_user_id,
|
||||||
tr.as_deref().unwrap_or("child_death"),
|
tr.as_deref().unwrap_or("child_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[UserCharacterWorker] child_death notification (father): {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(mother_user_id) = row
|
if let Some(mother_user_id) = row
|
||||||
.get("mother_user_id")
|
.get("mother_user_id")
|
||||||
@@ -912,11 +918,13 @@ impl UserCharacterWorker {
|
|||||||
let tr = death_ctx.as_ref().map(|c| {
|
let tr = death_ctx.as_ref().map(|c| {
|
||||||
death_log::wrap_death_notification(c, "child_death", mother_user_id)
|
death_log::wrap_death_notification(c, "child_death", mother_user_id)
|
||||||
});
|
});
|
||||||
self.notify_user_death(
|
if let Err(e) = self.notify_user_death(
|
||||||
&mut conn,
|
&mut conn,
|
||||||
mother_user_id,
|
mother_user_id,
|
||||||
tr.as_deref().unwrap_or("child_death"),
|
tr.as_deref().unwrap_or("child_death"),
|
||||||
)?;
|
) {
|
||||||
|
eprintln!("[UserCharacterWorker] child_death notification (mother): {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.execute("delete_knowledge", &[&character_id])?;
|
conn.execute("delete_knowledge", &[&character_id])?;
|
||||||
|
|||||||
Reference in New Issue
Block a user