forked from lavina/lavina
irc: move CHATHISTORY message handling into a separate module
This commit is contained in:
parent
a22cde0ea8
commit
25fe041698
|
@ -3,15 +3,15 @@ use std::future::Future;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tokio::io::AsyncWrite;
|
use tokio::io::AsyncWrite;
|
||||||
|
|
||||||
|
use crate::RegisteredUser;
|
||||||
use lavina_core::player::PlayerConnection;
|
use lavina_core::player::PlayerConnection;
|
||||||
use lavina_core::prelude::Str;
|
use lavina_core::prelude::Str;
|
||||||
|
|
||||||
pub struct IrcConnection<'a, T: AsyncWrite + Unpin> {
|
pub struct IrcConnection<'a, T: AsyncWrite + Unpin> {
|
||||||
pub server_name: Str,
|
pub server_name: Str,
|
||||||
/// client is nick of requester
|
|
||||||
pub client: Str,
|
|
||||||
pub writer: &'a mut T,
|
pub writer: &'a mut T,
|
||||||
pub player_connection: &'a mut PlayerConnection,
|
pub player_connection: &'a mut PlayerConnection,
|
||||||
|
pub user: &'a RegisteredUser,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a client-to-server IRC message that can be handled by the server.
|
/// Represents a client-to-server IRC message that can be handled by the server.
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,8 @@ use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio::sync::mpsc::channel;
|
use tokio::sync::mpsc::channel;
|
||||||
|
|
||||||
|
use crate::cap::Capabilities;
|
||||||
|
use crate::handler::{IrcCommand, IrcConnection};
|
||||||
use lavina_core::auth::Verdict;
|
use lavina_core::auth::Verdict;
|
||||||
use lavina_core::player::*;
|
use lavina_core::player::*;
|
||||||
use lavina_core::prelude::*;
|
use lavina_core::prelude::*;
|
||||||
|
@ -29,14 +31,9 @@ use proto_irc::{Chan, Recipient, Tag};
|
||||||
use sasl::AuthBody;
|
use sasl::AuthBody;
|
||||||
|
|
||||||
mod cap;
|
mod cap;
|
||||||
|
|
||||||
use handler::{IrcCommand, IrcConnection};
|
|
||||||
|
|
||||||
mod whois;
|
|
||||||
|
|
||||||
use crate::cap::Capabilities;
|
|
||||||
|
|
||||||
mod handler;
|
mod handler;
|
||||||
|
mod history;
|
||||||
|
mod whois;
|
||||||
|
|
||||||
pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION"));
|
pub const APP_VERSION: &str = concat!("lavina", "_", env!("CARGO_PKG_VERSION"));
|
||||||
|
|
||||||
|
@ -829,9 +826,9 @@ async fn handle_incoming_message(
|
||||||
ClientMessage::Whois(cmd) => {
|
ClientMessage::Whois(cmd) => {
|
||||||
let mut conn = IrcConnection {
|
let mut conn = IrcConnection {
|
||||||
server_name: config.server_name.clone(),
|
server_name: config.server_name.clone(),
|
||||||
client: user.nickname.clone(),
|
|
||||||
writer,
|
writer,
|
||||||
player_connection: user_handle,
|
player_connection: user_handle,
|
||||||
|
user,
|
||||||
};
|
};
|
||||||
cmd.handle_with(&mut conn).await?;
|
cmd.handle_with(&mut conn).await?;
|
||||||
writer.flush().await?;
|
writer.flush().await?;
|
||||||
|
@ -874,48 +871,15 @@ async fn handle_incoming_message(
|
||||||
log::info!("Received QUIT");
|
log::info!("Received QUIT");
|
||||||
return Ok(HandleResult::Leave);
|
return Ok(HandleResult::Leave);
|
||||||
}
|
}
|
||||||
ClientMessage::ChatHistory { chan, limit } => {
|
ClientMessage::ChatHistory(cmd) => {
|
||||||
if user.enabled_capabilities.contains(Capabilities::ChatHistory) {
|
let mut conn = IrcConnection {
|
||||||
let channel_name = match chan.clone() {
|
server_name: config.server_name.clone(),
|
||||||
Chan::Global(chan) => chan,
|
writer,
|
||||||
// TODO Respond with an error when a local channel is requested
|
player_connection: user_handle,
|
||||||
Chan::Local(chan) => chan,
|
user,
|
||||||
};
|
};
|
||||||
let room_id = &RoomId::try_from(channel_name.clone())?;
|
cmd.handle_with(&mut conn).await?;
|
||||||
let res = user_handle.get_room_message_history(room_id, limit).await?;
|
writer.flush().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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cmd => {
|
cmd => {
|
||||||
log::warn!("Not implemented handler for client command: {cmd:?}");
|
log::warn!("Not implemented handler for client command: {cmd:?}");
|
||||||
|
|
|
@ -35,7 +35,7 @@ async fn handle_nick_target(nick: Str, conn: &mut IrcConnection<'_, impl AsyncWr
|
||||||
GetInfoResult::UserDoesntExist => {
|
GetInfoResult::UserDoesntExist => {
|
||||||
IrcResponseMessage::empty_tags(
|
IrcResponseMessage::empty_tags(
|
||||||
Some(conn.server_name.clone()),
|
Some(conn.server_name.clone()),
|
||||||
ErrNoSuchNick401::new(conn.client.clone(), nick.clone()),
|
ErrNoSuchNick401::new(conn.user.nickname.clone(), nick.clone()),
|
||||||
)
|
)
|
||||||
.write_response(conn.writer)
|
.write_response(conn.writer)
|
||||||
.await?
|
.await?
|
||||||
|
@ -43,7 +43,7 @@ async fn handle_nick_target(nick: Str, conn: &mut IrcConnection<'_, impl AsyncWr
|
||||||
}
|
}
|
||||||
IrcResponseMessage::empty_tags(
|
IrcResponseMessage::empty_tags(
|
||||||
Some(conn.server_name.clone()),
|
Some(conn.server_name.clone()),
|
||||||
RplEndOfWhois318::new(conn.client.clone(), nick.clone()),
|
RplEndOfWhois318::new(conn.user.nickname.clone(), nick.clone()),
|
||||||
)
|
)
|
||||||
.write_response(conn.writer)
|
.write_response(conn.writer)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -63,10 +63,7 @@ pub enum ClientMessage {
|
||||||
reason: Str,
|
reason: Str,
|
||||||
},
|
},
|
||||||
Authenticate(Str),
|
Authenticate(Str),
|
||||||
ChatHistory {
|
ChatHistory(ChatHistory),
|
||||||
chan: Chan,
|
|
||||||
limit: u32,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -76,6 +73,12 @@ pub enum Whois {
|
||||||
EmptyArgs,
|
EmptyArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ChatHistory {
|
||||||
|
pub chan: Chan,
|
||||||
|
pub limit: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn client_message(input: &str) -> Result<ClientMessage> {
|
pub fn client_message(input: &str) -> Result<ClientMessage> {
|
||||||
let res = all_consuming(alt((
|
let res = all_consuming(alt((
|
||||||
client_message_capability,
|
client_message_capability,
|
||||||
|
@ -273,7 +276,7 @@ fn client_message_chathistory(input: &str) -> IResult<&str, ClientMessage> {
|
||||||
let (input, _) = tag(" * ")(input)?;
|
let (input, _) = tag(" * ")(input)?;
|
||||||
let (input, limit) = limit(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> {
|
fn limit(input: &str) -> IResult<&str, u32> {
|
||||||
|
@ -514,10 +517,10 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_client_chat_history_latest() {
|
fn test_client_chat_history_latest() {
|
||||||
let input = "CHATHISTORY LATEST #chan * 10";
|
let input = "CHATHISTORY LATEST #chan * 10";
|
||||||
let expected = ClientMessage::ChatHistory {
|
let expected = ClientMessage::ChatHistory(ChatHistory {
|
||||||
chan: Chan::Global("chan".into()),
|
chan: Chan::Global("chan".into()),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
};
|
});
|
||||||
|
|
||||||
let result = client_message(input);
|
let result = client_message(input);
|
||||||
assert_matches!(result, Ok(result) => assert_eq!(expected, result));
|
assert_matches!(result, Ok(result) => assert_eq!(expected, result));
|
||||||
|
|
Loading…
Reference in New Issue