diff --git a/src/main.rs b/src/main.rs index 66167ce..2f4a8f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::time::Instant; use std::{io::Cursor, net::SocketAddr, str::FromStr, sync::Arc, time::Duration}; use irc_rust::Message; @@ -25,6 +26,8 @@ struct Client { modes: HashSet, // [byte per mode](https://www.unrealircd.org/docs/User_modes) + last_pong: Instant, + c2s_tx: Option>, s2c_rx: Option>, } @@ -43,6 +46,7 @@ impl Client { realname: None, modes: HashSet::new(), + last_pong: Instant::now(), c2s_tx: Some(c2s_tx), s2c_rx: Some(s2c_rx), @@ -80,6 +84,13 @@ impl AppState { } } + pub async fn nick(&self, peer_addr: SocketAddr) -> Option { + if let Some(client) = self.clients.read().await.get(&peer_addr) { + return client.nick.clone(); + } + None + } + pub async fn user( &self, peer_addr: SocketAddr, @@ -101,10 +112,17 @@ impl AppState { false } + async fn pong(&self, peer_addr: SocketAddr) { + if let Some(client) = self.clients.write().await.get_mut(&peer_addr) { + client.last_pong = Instant::now(); + } + } + async fn quit(&self, peer_addr: SocketAddr, reason: Option<&str>) { // broadcast user leaving unless invisible flag was set self.clients.write().await.remove(&peer_addr); + println!("Bye {}: {}", peer_addr, reason.unwrap_or_default()); } // clients still needs mod/op status stuff @@ -138,6 +156,7 @@ impl AppState { let Some(client) = clients.get(&peer_addr) else { return Err("Client not found".into()); }; + dbg!(&msg); client.tx.send(msg).await?; Ok(()) } @@ -204,7 +223,12 @@ async fn main() -> Result<()> { tokio::spawn(async move { loop { let messages = state.get_messages().await; - // println!("Got {} messages", messages.len()); + if messages.is_empty() { + tokio::time::sleep(Duration::from_millis(50)).await; + continue; + } + + println!("Got {} messages", messages.len()); for (peer_addr, msg) in messages { println!("{}: {}", peer_addr, msg); @@ -212,7 +236,6 @@ async fn main() -> Result<()> { eprintln!("Error handling command: {:?}", e); } } - tokio::time::sleep(Duration::from_millis(1)).await; } }); @@ -222,16 +245,23 @@ async fn main() -> Result<()> { loop { let clients = state.clients.read().await; for (peer_addr, client) in clients.iter() { + if Instant::now().duration_since(client.last_pong).as_secs() > 120 { + println!( + "It's been 2 minutes, where the fuck did you go {}?", + peer_addr + ); + // disconnected + } println!("PING :{}", client.username.clone().unwrap_or_default()); - state + let _ = state .send( *peer_addr, Message::from_str(&format!( - ":localhost PING :{}", + "PING :{}", client.username.clone().unwrap_or_default() ))?, ) - .await?; + .await; } drop(clients); @@ -255,7 +285,13 @@ async fn handle( ) -> Result> { match msg.command()? { "CAP" => { - state.send(peer_addr, Message::from_str("NONE")?).await?; + dbg!(&msg); + state + .send( + peer_addr, + "CAP * LS :multi-prefix sasl=PLAIN,EXTERNAL".into(), + ) + .await?; } "NICK" => { let nick = msg.params()?.next().unwrap_or_default(); @@ -268,16 +304,24 @@ async fn handle( params.next().unwrap_or_default(); // unused let realname = msg.trailing()?; // realname - - dbg!(username, realname); - state.user(peer_addr, username, mode, realname).await; + + let nick = state.nick(peer_addr).await.unwrap_or(username.to_string()); + state + .send( + peer_addr, + format!(":localhost 001 {} :Welcome to the IRC server.", nick).into(), + ) + .await?; } "PASS" => { let mut params = msg.params()?; let password = params.next().unwrap_or_default(); // aviinl state.pass(peer_addr, password).await; } + "PONG" => { + state.pong(peer_addr).await; + } "QUIT" => { let reason = msg.trailing()?; // realname state.quit(peer_addr, reason).await; @@ -340,7 +384,8 @@ async fn connect( // fire off a send loop tokio::spawn(async move { while let Some(i) = s2c_rx.recv().await { - if let Err(e) = tx.write(i.to_string().as_bytes()).await { + let data = i.to_string() + "\r\n"; + if let Err(e) = tx.write(data.as_bytes()).await { eprintln!("Error sending message: {:?}", e); break; }