diff --git a/Cargo.lock b/Cargo.lock index bfefbbf..0fec035 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,7 +99,7 @@ dependencies = [ "mime", "percent-encoding", "pin-project 1.0.5", - "rand", + "rand 0.7.3", "regex", "serde", "serde_json", @@ -128,6 +128,7 @@ dependencies = [ "actix-web-actors", "derive_more", "env_logger", + "rand 0.8.3", "serde", "serde_json", "uuid", @@ -399,7 +400,7 @@ dependencies = [ "log", "mime", "percent-encoding", - "rand", + "rand 0.7.3", "serde", "serde_json", "serde_urlencoded", @@ -1269,9 +1270,21 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ "getrandom 0.1.16", "libc", - "rand_chacha", - "rand_core", - "rand_hc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha 0.3.0", + "rand_core 0.6.2", + "rand_hc 0.3.0", ] [[package]] @@ -1281,7 +1294,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", ] [[package]] @@ -1293,13 +1316,31 @@ dependencies = [ "getrandom 0.1.16", ] +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom 0.2.2", +] + [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", ] [[package]] @@ -1724,7 +1765,7 @@ dependencies = [ "idna", "lazy_static", "log", - "rand", + "rand 0.7.3", "smallvec", "thiserror", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 6eea767..47bdb2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ uuid = { version = "0.8", features = ["serde", "v4"] } derive_more = "0.99.11" serde_json = "1.0.64" serde = "1.0.124" -env_logger = "0.8.3" \ No newline at end of file +env_logger = "0.8.3" +rand = "0.8.3" \ No newline at end of file diff --git a/src/actors/chat_server.rs b/src/actors/chat_server.rs index eb245ab..c80350f 100644 --- a/src/actors/chat_server.rs +++ b/src/actors/chat_server.rs @@ -27,13 +27,16 @@ impl ChatServer { .unwrap_or(false) } - pub fn send_message(&self, room: &Uuid, message: &str, skip_id: &Uuid) { + pub fn send_message(&self, room: &Uuid, message: &str, skip_id: &Uuid, user: Option) { self.rooms.get(room).map(|sessions| { sessions.iter().for_each(|id| { if id != skip_id { - self.sessions - .get(id) - .map(|addr| addr.do_send(Message(message.into()))); + self.sessions.get(id).map(|addr| { + addr.do_send(Message { + nickname: user.clone(), + msg: message.into(), + }) + }); } }); }); @@ -49,7 +52,7 @@ impl ChatServer { } // send message to other users for room in rooms { - self.send_message(&room, "Someone disconnected", &session_id); + self.send_message(&room, "Someone disconnected", &session_id, None); if self.is_empty(&room) { self.rooms.remove(&room); } @@ -95,8 +98,13 @@ impl Handler for ChatServer { type Result = (); fn handle(&mut self, msg: ClientMessage, _ctx: &mut Self::Context) -> Self::Result { - let ClientMessage { session, room, msg } = msg; - self.send_message(&room, &msg, &session); + let ClientMessage { + session, + user, + room, + msg, + } = msg; + self.send_message(&room, &msg, &session, Some(user)); } } @@ -129,7 +137,7 @@ impl Handler for ChatServer { .rooms .get_mut(&room) .map(|sessions| sessions.insert(session)) - .map(|_| self.send_message(&room, "Someone connected", &session)) + .map(|_| self.send_message(&room, "Someone connected", &session, None)) .ok_or("The room doesn't exists".into()); MessageResult(result) diff --git a/src/actors/chat_session.rs b/src/actors/chat_session.rs index 7bc2611..1f3647b 100644 --- a/src/actors/chat_session.rs +++ b/src/actors/chat_session.rs @@ -1,7 +1,7 @@ use crate::{ actors::chat_server::ChatServer, constants::CLIENT_TIMEOUT, - models::{RoomId, SessionId}, + models::{RoomId, SessionId, UserInfo}, }; use crate::{ constants::HEARTBEAT_INTERVAL, @@ -20,6 +20,7 @@ use actix::{ }; use actix::{Actor, Addr, AsyncContext}; use actix_web_actors::ws::{self, WebsocketContext}; +use serde_json::json; use std::str::FromStr; use uuid::Uuid; @@ -28,6 +29,7 @@ pub struct WsChatSession { pub room: Option, pub addr: Addr, pub hb: Instant, + pub user: UserInfo, } impl WsChatSession { @@ -36,6 +38,7 @@ impl WsChatSession { id: Uuid::new_v4(), room: None, hb: Instant::now(), + user: UserInfo::default(), addr, } } @@ -92,7 +95,10 @@ impl Handler for WsChatSession { type Result = (); fn handle(&mut self, msg: Message, ctx: &mut Self::Context) -> Self::Result { - ctx.text(msg.0); + ctx.text(WsMessage { + ty: MessageType::Msg, + data: json!(msg), + }); } } @@ -132,14 +138,14 @@ impl Handler for WsChatSession { type Result = (); fn handle(&mut self, msg: WsMessage, ctx: &mut Self::Context) -> Self::Result { - let data = msg.data.unwrap_or("".into()); + let data = msg.data.as_str().unwrap(); match msg.ty { MessageType::Create => self.create(ctx), MessageType::Join => match Uuid::from_str(&data) { Ok(uuid) => self.join(uuid, ctx), Err(err) => ctx.text(WsMessage::err(err.to_string())), }, - MessageType::Msg => self.msg(data, ctx), + MessageType::Msg => self.msg(data.into(), ctx), MessageType::Leave => self.leave(ctx), _ => (), } @@ -184,7 +190,7 @@ impl WsChatSession { act.room = Some(room_id.clone()); ctx.text(WsMessage { ty: MessageType::Msg, - data: Some("Joined!".into()), + data: json!("Joined!"), }) } Ok(res) => ctx.text(WsMessage::err(res.unwrap_err().to_string())), @@ -242,10 +248,12 @@ impl Handler for WsChatSession { Command::Msg(msg) => { self.addr.do_send(ClientMessage { session: self.id.clone(), + user: self.user.nickname.clone(), room: room_id, msg, }); } + Command::SetName(name) => self.user.nickname = name, Command::GetRoomId => { ctx.text(WsMessage::info(room_id.to_string())); } diff --git a/src/messages/server/mod.rs b/src/messages/server/mod.rs index 815a119..ac8b66d 100644 --- a/src/messages/server/mod.rs +++ b/src/messages/server/mod.rs @@ -7,6 +7,7 @@ use uuid::Uuid; #[rtype(result = "()")] pub struct ClientMessage { pub session: SessionId, + pub user: String, pub room: RoomId, pub msg: String, } diff --git a/src/messages/session/command.rs b/src/messages/session/command.rs index 19fc2ea..33690d2 100644 --- a/src/messages/session/command.rs +++ b/src/messages/session/command.rs @@ -8,6 +8,7 @@ use std::str::FromStr; pub enum Command { Msg(String), GetRoomId, + SetName(String), } #[derive(Debug, Display, Error)] @@ -22,11 +23,15 @@ impl FromStr for Command { fn from_str(data: &str) -> Result { let words: Vec<&str> = data.trim().split_whitespace().collect(); - let first_word = words.first(); + let opt = words.split_first(); - if let Some(&word) = first_word { - return match word { + if let Some((&command, words)) = opt { + return match command { "/roomId" => Ok(Command::GetRoomId), + "/setName" if words.len() > 0 => Ok(Command::SetName(words[0].into())), + "/setName" => Err(CommandError { + msg: "Invalid empty name", + }), _ => Ok(Command::Msg(data.into())), }; } diff --git a/src/messages/session/mod.rs b/src/messages/session/mod.rs index 0d823a0..e05f26d 100644 --- a/src/messages/session/mod.rs +++ b/src/messages/session/mod.rs @@ -1,7 +1,11 @@ pub mod command; pub mod wsmessage; use actix::Message as ActixMessage; +use serde::Serialize; -#[derive(ActixMessage)] +#[derive(Serialize, ActixMessage)] #[rtype(result = "()")] -pub struct Message(pub String); +pub struct Message { + pub nickname: Option, + pub msg: String, +} diff --git a/src/messages/session/wsmessage.rs b/src/messages/session/wsmessage.rs index 8fcd9e8..8d41390 100644 --- a/src/messages/session/wsmessage.rs +++ b/src/messages/session/wsmessage.rs @@ -1,26 +1,27 @@ use actix::Message as ActixMessage; use serde::{Deserialize, Serialize}; +use serde_json::{Value, json}; use std::convert::Into; #[derive(Serialize, Deserialize, ActixMessage)] #[rtype(result = "()")] pub struct WsMessage { pub ty: MessageType, - pub data: Option, + pub data: Value, } impl WsMessage { pub fn err(msg: String) -> Self { WsMessage { ty: MessageType::Err, - data: Some(msg), + data: json!(msg), } } pub fn info(msg: String) -> Self { WsMessage { ty: MessageType::Info, - data: Some(msg), + data: json!(msg), } } } diff --git a/src/models/mod.rs b/src/models/mod.rs index 5873e17..653f4a3 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,5 +1,7 @@ use crate::actors::chat_server::ChatServer; use actix::Addr; +use rand::distributions::Alphanumeric; +use rand::{thread_rng, Rng}; use uuid::Uuid; pub type SessionId = Uuid; @@ -8,3 +10,21 @@ pub type RoomId = Uuid; pub struct AppState { pub chat: Addr, } + +pub struct UserInfo { + pub nickname: String, +} + +impl Default for UserInfo { + fn default() -> Self { + let rand_string: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(15) + .map(char::from) + .collect(); + + let nickname = format!("User-{}", rand_string); + + UserInfo { nickname } + } +}