Initialisiere yourchat2 als eigenständigen Rust-Chatdienst und portiere die Kernfunktionen aus der Altanwendung.
Die Implementierung enthält modulare Command-/State-/DB-Strukturen, DB-basierte Authentifizierung inkl. Rechte- und Raumzugriffsprüfung sowie kompatible Chat- und Dice-Commands. Made-with: Cursor
This commit is contained in:
154
src/state.rs
Normal file
154
src/state.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use crate::types::{ChatState, ClientId, Command, RoomInfo};
|
||||
use serde_json::{json, Value};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
pub async fn authorize(client_id: ClientId, command: &Command, state: Arc<RwLock<ChatState>>) -> bool {
|
||||
let auth_error = {
|
||||
let guard = state.read().await;
|
||||
let Some(client) = guard.clients.get(&client_id) else {
|
||||
return false;
|
||||
};
|
||||
if !client.logged_in {
|
||||
Some("not_initialized")
|
||||
} else {
|
||||
match (&client.token, &command.token) {
|
||||
(Some(expected), Some(got)) if expected == got => None,
|
||||
(Some(_), Some(_)) => Some("invalid_token"),
|
||||
(Some(_), None) => Some("missing_token"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(message) = auth_error {
|
||||
send_to_client(client_id, state, json!({"type":"error","message": message})).await;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_room_list(client_id: ClientId, state: Arc<RwLock<ChatState>>) {
|
||||
let list = {
|
||||
let guard = state.read().await;
|
||||
let mut rooms: Vec<RoomInfo> = guard
|
||||
.rooms
|
||||
.iter()
|
||||
.map(|(name, members)| RoomInfo {
|
||||
name: name.clone(),
|
||||
users: members.len(),
|
||||
})
|
||||
.collect();
|
||||
rooms.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
rooms
|
||||
};
|
||||
send_to_client(client_id, state, json!({"type":3, "message": list})).await;
|
||||
}
|
||||
|
||||
pub async fn send_user_list(client_id: ClientId, state: Arc<RwLock<ChatState>>) {
|
||||
let users = {
|
||||
let guard = state.read().await;
|
||||
let Some(client) = guard.clients.get(&client_id) else {
|
||||
return;
|
||||
};
|
||||
if client.room.is_empty() {
|
||||
Vec::<Value>::new()
|
||||
} else if let Some(members) = guard.rooms.get(&client.room) {
|
||||
members
|
||||
.iter()
|
||||
.filter_map(|id| guard.clients.get(id))
|
||||
.map(|u| json!({"name": u.user_name, "color": u.color}))
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::<Value>::new()
|
||||
}
|
||||
};
|
||||
send_to_client(client_id, state, json!({"type":2, "message": users})).await;
|
||||
}
|
||||
|
||||
pub async fn send_to_client(client_id: ClientId, state: Arc<RwLock<ChatState>>, payload: Value) {
|
||||
let msg = payload.to_string();
|
||||
let tx = {
|
||||
let guard = state.read().await;
|
||||
guard.clients.get(&client_id).map(|c| c.tx.clone())
|
||||
};
|
||||
if let Some(tx) = tx {
|
||||
let _ = tx.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn broadcast_all(state: Arc<RwLock<ChatState>>, payload: Value) {
|
||||
let msg = payload.to_string();
|
||||
let targets = {
|
||||
let guard = state.read().await;
|
||||
guard.clients.values().map(|c| c.tx.clone()).collect::<Vec<_>>()
|
||||
};
|
||||
for tx in targets {
|
||||
let _ = tx.send(msg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn broadcast_room(
|
||||
room: &str,
|
||||
state: Arc<RwLock<ChatState>>,
|
||||
payload: Value,
|
||||
exclude: Option<ClientId>,
|
||||
) {
|
||||
let msg = payload.to_string();
|
||||
let targets = {
|
||||
let guard = state.read().await;
|
||||
let Some(members) = guard.rooms.get(room) else {
|
||||
return;
|
||||
};
|
||||
members
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
if exclude.is_some() && exclude == Some(*id) {
|
||||
return None;
|
||||
}
|
||||
guard.clients.get(id).map(|c| c.tx.clone())
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
for tx in targets {
|
||||
let _ = tx.send(msg.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn disconnect_client(client_id: ClientId, state: Arc<RwLock<ChatState>>) {
|
||||
let (old_room, old_name, token, was_logged_in) = {
|
||||
let mut guard = state.write().await;
|
||||
let Some(client) = guard.clients.remove(&client_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !client.room.is_empty() {
|
||||
if let Some(members) = guard.rooms.get_mut(&client.room) {
|
||||
members.remove(&client_id);
|
||||
if members.is_empty() {
|
||||
guard.rooms.remove(&client.room);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(client.room, client.user_name, client.token, client.logged_in)
|
||||
};
|
||||
|
||||
if let Some(token) = token {
|
||||
let mut guard = state.write().await;
|
||||
guard.tokens.remove(&token);
|
||||
if was_logged_in {
|
||||
guard.logged_in_names.remove(&old_name);
|
||||
}
|
||||
}
|
||||
|
||||
if !old_room.is_empty() {
|
||||
broadcast_room(
|
||||
&old_room,
|
||||
state,
|
||||
json!({"type":"system","message": format!("{old_name} disconnected")}),
|
||||
Some(client_id),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user