From 25fe041698c22082a599ca9aa1e0141439310b8d Mon Sep 17 00:00:00 2001 From: Nikita Vilunov Date: Wed, 5 Jun 2024 02:59:22 +0200 Subject: [PATCH] irc: move CHATHISTORY message handling into a separate module --- crates/projection-irc/src/handler.rs | 4 +- crates/projection-irc/src/history.rs | 57 +++++++++++++++++++++++++ crates/projection-irc/src/lib.rs | 64 ++++++---------------------- crates/projection-irc/src/whois.rs | 4 +- crates/proto-irc/src/client.rs | 17 +++++--- 5 files changed, 85 insertions(+), 61 deletions(-) create mode 100644 crates/projection-irc/src/history.rs diff --git a/crates/projection-irc/src/handler.rs b/crates/projection-irc/src/handler.rs index 3d54626..f536c0e 100644 --- a/crates/projection-irc/src/handler.rs +++ b/crates/projection-irc/src/handler.rs @@ -3,15 +3,15 @@ use std::future::Future; use anyhow::Result; use tokio::io::AsyncWrite; +use crate::RegisteredUser; use lavina_core::player::PlayerConnection; use lavina_core::prelude::Str; pub struct IrcConnection<'a, T: AsyncWrite + Unpin> { pub server_name: Str, - /// client is nick of requester - pub client: Str, pub writer: &'a mut T, pub player_connection: &'a mut PlayerConnection, + pub user: &'a RegisteredUser, } /// Represents a client-to-server IRC message that can be handled by the server. diff --git a/crates/projection-irc/src/history.rs b/crates/projection-irc/src/history.rs new file mode 100644 index 0000000..269be49 --- /dev/null +++ b/crates/projection-irc/src/history.rs @@ -0,0 +1,57 @@ +use anyhow::Result; +use chrono::SecondsFormat; +use tokio::io::{AsyncWrite, AsyncWriteExt}; + +use crate::cap::Capabilities; +use crate::handler::{IrcCommand, IrcConnection}; +use lavina_core::player::RoomHistoryResult; +use lavina_core::room::RoomId; +use proto_irc::client::ChatHistory; +use proto_irc::server::{ServerMessage, ServerMessageBody}; +use proto_irc::{Chan, Recipient, Tag}; + +impl IrcCommand for ChatHistory { + async fn handle_with(&self, conn: &mut IrcConnection<'_, impl AsyncWrite + Unpin>) -> Result<()> { + if !conn.user.enabled_capabilities.contains(Capabilities::ChatHistory) { + tracing::debug!( + "Requested chat history for user {:?} even though the capability was not negotiated", + conn.user.nickname + ); + return Ok(()); + } + let channel_name = match self.chan.clone() { + Chan::Global(chan) => chan, + // TODO Respond with an error when a local channel is requested + Chan::Local(chan) => chan, + }; + let room_id = &RoomId::try_from(channel_name.clone())?; + let res = conn.player_connection.get_room_message_history(room_id, self.limit).await?; + match res { + RoomHistoryResult::Success(messages) => { + for message in messages { + let mut tags = vec![]; + if conn.user.enabled_capabilities.contains(Capabilities::ServerTime) { + let tag = Tag { + key: "time".into(), + value: Some(message.created_at.to_rfc3339_opts(SecondsFormat::Millis, true).into()), + }; + tags.push(tag); + } + ServerMessage { + tags, + sender: Some(message.author_name.into()), + body: ServerMessageBody::PrivateMessage { + target: Recipient::Chan(self.chan.clone()), + body: message.content.into(), + }, + } + .write_async(conn.writer) + .await?; + } + conn.writer.flush().await?; + } + RoomHistoryResult::NoSuchRoom => {} + } + Ok(()) + } +} diff --git a/crates/projection-irc/src/lib.rs b/crates/projection-irc/src/lib.rs index 080ee69..4e5f1b4 100644 --- a/crates/projection-irc/src/lib.rs +++ b/crates/projection-irc/src/lib.rs @@ -14,6 +14,8 @@ use tokio::net::tcp::{ReadHalf, WriteHalf}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::mpsc::channel; +use crate::cap::Capabilities; +use crate::handler::{IrcCommand, IrcConnection}; use lavina_core::auth::Verdict; use lavina_core::player::*; use lavina_core::prelude::*; @@ -29,14 +31,9 @@ use proto_irc::{Chan, Recipient, Tag}; use sasl::AuthBody; mod cap; - -use handler::{IrcCommand, IrcConnection}; - -mod whois; - -use crate::cap::Capabilities; - mod handler; +mod history; +mod whois; pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION")); @@ -829,9 +826,9 @@ async fn handle_incoming_message( ClientMessage::Whois(cmd) => { let mut conn = IrcConnection { server_name: config.server_name.clone(), - client: user.nickname.clone(), writer, player_connection: user_handle, + user, }; cmd.handle_with(&mut conn).await?; writer.flush().await?; @@ -874,48 +871,15 @@ async fn handle_incoming_message( log::info!("Received QUIT"); return Ok(HandleResult::Leave); } - ClientMessage::ChatHistory { chan, limit } => { - if user.enabled_capabilities.contains(Capabilities::ChatHistory) { - let channel_name = match chan.clone() { - Chan::Global(chan) => chan, - // TODO Respond with an error when a local channel is requested - Chan::Local(chan) => chan, - }; - let room_id = &RoomId::try_from(channel_name.clone())?; - let res = user_handle.get_room_message_history(room_id, limit).await?; - match res { - RoomHistoryResult::Success(messages) => { - for message in messages { - let mut tags = vec![]; - if user.enabled_capabilities.contains(Capabilities::ServerTime) { - let tag = Tag { - key: "time".into(), - value: Some( - message.created_at.to_rfc3339_opts(SecondsFormat::Millis, true).into(), - ), - }; - tags.push(tag); - } - ServerMessage { - tags, - sender: Some(message.author_name.into()), - body: ServerMessageBody::PrivateMessage { - target: Recipient::Chan(chan.clone()), - body: message.content.into(), - }, - } - .write_async(writer) - .await?; - } - writer.flush().await?; - } - RoomHistoryResult::NoSuchRoom => {} - } - } else { - log::warn!( - "Requested chat history for user {user:?} even though the capability was not negotiated" - ); - } + ClientMessage::ChatHistory(cmd) => { + let mut conn = IrcConnection { + server_name: config.server_name.clone(), + writer, + player_connection: user_handle, + user, + }; + cmd.handle_with(&mut conn).await?; + writer.flush().await?; } cmd => { log::warn!("Not implemented handler for client command: {cmd:?}"); diff --git a/crates/projection-irc/src/whois.rs b/crates/projection-irc/src/whois.rs index 888ef0e..eeb65c7 100644 --- a/crates/projection-irc/src/whois.rs +++ b/crates/projection-irc/src/whois.rs @@ -35,7 +35,7 @@ async fn handle_nick_target(nick: Str, conn: &mut IrcConnection<'_, impl AsyncWr GetInfoResult::UserDoesntExist => { IrcResponseMessage::empty_tags( Some(conn.server_name.clone()), - ErrNoSuchNick401::new(conn.client.clone(), nick.clone()), + ErrNoSuchNick401::new(conn.user.nickname.clone(), nick.clone()), ) .write_response(conn.writer) .await? @@ -43,7 +43,7 @@ async fn handle_nick_target(nick: Str, conn: &mut IrcConnection<'_, impl AsyncWr } IrcResponseMessage::empty_tags( Some(conn.server_name.clone()), - RplEndOfWhois318::new(conn.client.clone(), nick.clone()), + RplEndOfWhois318::new(conn.user.nickname.clone(), nick.clone()), ) .write_response(conn.writer) .await?; diff --git a/crates/proto-irc/src/client.rs b/crates/proto-irc/src/client.rs index 17e8376..4d05cae 100644 --- a/crates/proto-irc/src/client.rs +++ b/crates/proto-irc/src/client.rs @@ -63,10 +63,7 @@ pub enum ClientMessage { reason: Str, }, Authenticate(Str), - ChatHistory { - chan: Chan, - limit: u32, - }, + ChatHistory(ChatHistory), } #[derive(Clone, Debug, PartialEq, Eq)] @@ -76,6 +73,12 @@ pub enum Whois { EmptyArgs, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ChatHistory { + pub chan: Chan, + pub limit: u32, +} + pub fn client_message(input: &str) -> Result { let res = all_consuming(alt(( client_message_capability, @@ -273,7 +276,7 @@ fn client_message_chathistory(input: &str) -> IResult<&str, ClientMessage> { let (input, _) = tag(" * ")(input)?; let (input, limit) = limit(input)?; - Ok((input, ClientMessage::ChatHistory { chan, limit })) + Ok((input, ClientMessage::ChatHistory(ChatHistory { chan, limit }))) } fn limit(input: &str) -> IResult<&str, u32> { @@ -514,10 +517,10 @@ mod test { #[test] fn test_client_chat_history_latest() { let input = "CHATHISTORY LATEST #chan * 10"; - let expected = ClientMessage::ChatHistory { + let expected = ClientMessage::ChatHistory(ChatHistory { chan: Chan::Global("chan".into()), limit: 10, - }; + }); let result = client_message(input); assert_matches!(result, Ok(result) => assert_eq!(expected, result));