From f07221b4e1d8aa464a44fe566884ccc0565406e9 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 16:17:19 +0100 Subject: v3.2.0: straight and split bets anywhere on the felt; add history command --- src/server/core.rs | 34 ++++++++++++++++++++++++++++++++++ src/server/database.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) (limited to 'src/server') diff --git a/src/server/core.rs b/src/server/core.rs index 4b68ccb..89264f3 100644 --- a/src/server/core.rs +++ b/src/server/core.rs @@ -172,6 +172,21 @@ impl ServerCore { self.handle_auth(state, parts[1], parts[2]) } } + "HISTORY" => { + if parts.len() > 2 { + "ERROR Invalid HISTORY format. Usage: HISTORY [count]".to_string() + } else { + match parts.get(1) { + Some(raw) => match raw.parse::() { + Ok(count) if count > 0 => { + self.handle_history(state, count.min(crate::history::MAX_HISTORY)) + } + _ => "ERROR Invalid HISTORY count".to_string(), + }, + None => self.handle_history(state, crate::history::DEFAULT_HISTORY), + } + } + } "PING" => "PONG".to_string(), "INFO" => self.handle_info(state), _ => msg.to_string(), @@ -234,6 +249,25 @@ impl ServerCore { } } + fn handle_history(&mut self, state: &ConnectionState, limit: u32) -> String { + let Some(user) = state.user.as_deref() else { + return "ERROR Not logged in".to_string(); + }; + match database::recent_plays(&self.conn, user, limit) { + Ok(records) => match serde_json::to_string(&records) { + Ok(json) => format!("HISTORY {json}"), + Err(error) => { + eprintln!("History serialization failed for {user}: {error}"); + "ERROR Could not read history".to_string() + } + }, + Err(error) => { + eprintln!("History query failed for {user}: {error}"); + "ERROR Could not read history".to_string() + } + } + } + fn handle_info(&self, state: &ConnectionState) -> String { let Some(user) = state.user.as_deref() else { return "ERROR Not logged in".to_string(); diff --git a/src/server/database.rs b/src/server/database.rs index 6dd71a3..cfc48c1 100644 --- a/src/server/database.rs +++ b/src/server/database.rs @@ -399,3 +399,30 @@ fn abandoned_details(raw: &str) -> String { } value.to_string() } + +/// Most recent settled plays for a user, newest first. +pub fn recent_plays( + conn: &Connection, + username: &str, + limit: u32, +) -> rusqlite::Result> { + let mut statement = conn.prepare( + "SELECT game_type, bet_amount, winnings, balance_after, details, created_at + FROM plays + WHERE username = ?1 + ORDER BY created_at DESC, rowid DESC + LIMIT ?2", + )?; + let rows = statement.query_map(params![username, limit], |row| { + let details: String = row.get(4)?; + Ok(crate::history::PlayRecord { + game: row.get(0)?, + bet: row.get(1)?, + winnings: row.get(2)?, + balance_after: row.get(3)?, + at: row.get(5)?, + details: serde_json::from_str(&details).unwrap_or(serde_json::Value::Null), + }) + })?; + rows.collect() +} -- cgit v1.2.3