handle join and privmsg irc commands

This commit is contained in:
Nikita Vilunov 2023-02-13 18:08:37 +01:00
parent 20b461e81c
commit 89f85b4fee
4 changed files with 163 additions and 19 deletions

View File

@ -7,12 +7,12 @@ use tokio::net::tcp::{ReadHalf, WriteHalf};
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::oneshot::channel;
use crate::core::player::{PlayerId, PlayerRegistry};
use crate::core::player::{PlayerId, PlayerRegistry, Updates};
use crate::core::room::RoomId;
use crate::prelude::*;
use crate::protos::irc::client::{client_message, ClientMessage};
use crate::protos::irc::server::{ServerMessage, ServerMessageBody};
use crate::protos::irc::Chan;
use crate::protos::irc::{Chan, Recipient};
use crate::util::Terminator;
#[derive(Deserialize, Debug, Clone)]
@ -134,6 +134,7 @@ async fn handle_registered_socket<'a>(
let mut user_handle = players
.get_or_create_player(PlayerId(user.nickname.clone()))
.await;
let mut connnection = user_handle.subscribe().await;
ServerMessage {
tags: vec![],
@ -221,7 +222,18 @@ async fn handle_registered_socket<'a>(
},
Chan::Local(_) => {},
};
}
},
ClientMessage::PrivateMessage { recipient, body } => {
match recipient {
Recipient::Chan(Chan::Global(room)) => {
match String::from_utf8(body) {
Ok(body) => user_handle.send_message(RoomId(room.clone()), body.clone()).await,
Err(err) => log::warn!("failed to parse incoming message: {err}"),
}
},
_ => log::warn!("Unsupported target type"),
}
},
_ => {},
}
},
@ -231,6 +243,19 @@ async fn handle_registered_socket<'a>(
}
buffer.clear();
},
update = connnection.recv() => {
match update.unwrap() {
Updates::RoomJoined { room_id } => {},
Updates::NewMessage { room_id, body } => {
ServerMessage {
tags: vec![],
sender: None,
body: ServerMessageBody::PrivateMessage { target: Recipient::Chan(Chan::Global(room_id.0)), body: body.as_bytes().to_vec() }
}.write_async(&mut writer).await?;
writer.flush().await?
},
}
}
}
}
Ok(())

View File

@ -4,29 +4,24 @@ use super::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ClientMessage {
/// CAP. Capability-related commands.
Capability {
subcommand: CapabilitySubcommand,
},
Capability { subcommand: CapabilitySubcommand },
/// PING <token>
Ping {
token: ByteVec,
},
Ping { token: ByteVec },
/// PONG <token>
Pong {
token: ByteVec,
},
/// NICK <name>
Nick {
nickname: ByteVec,
},
Pong { token: ByteVec },
/// NICK <nickname>
Nick { nickname: ByteVec },
/// USER <username> 0 * :<realname>
User {
username: ByteVec,
realname: ByteVec,
},
/// JOIN <chan>
Join(Chan),
Quit {
reason: ByteVec,
},
/// PRIVMSG <target> :<msg>
PrivateMessage { recipient: Recipient, body: ByteVec },
/// QUIT :<reason>
Quit { reason: ByteVec },
}
pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> {
@ -37,6 +32,7 @@ pub fn client_message(input: &[u8]) -> IResult<&[u8], ClientMessage> {
client_message_nick,
client_message_user,
client_message_join,
client_message_privmsg,
client_message_quit,
))(input)
}
@ -107,6 +103,16 @@ fn client_message_join(input: &[u8]) -> IResult<&[u8], ClientMessage> {
Ok((input, ClientMessage::Join(chan)))
}
fn client_message_privmsg(input: &[u8]) -> IResult<&[u8], ClientMessage> {
let (input, _) = tag("PRIVMSG ")(input)?;
let (input, recipient) = recipient(input)?;
let (input, _) = tag(" :")(input)?;
let (input, body) = token(input)?;
let body = body.to_vec();
Ok((input, ClientMessage::PrivateMessage { recipient, body }))
}
fn client_message_quit(input: &[u8]) -> IResult<&[u8], ClientMessage> {
let (input, _) = tag("QUIT :")(input)?;
let (input, reason) = token(input)?;

View File

@ -2,11 +2,14 @@
pub mod client;
pub mod server;
use std::io::Result;
use nom::{
branch::alt,
bytes::complete::{tag, take, take_while},
IResult,
};
use tokio::io::{AsyncWrite, AsyncWriteExt};
type ByteVec = Vec<u8>;
@ -32,6 +35,21 @@ pub enum Chan {
/// &<name> — server-local channel, available only to connections to the same server. Rarely used in practice.
Local(ByteVec),
}
impl Chan {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self {
Chan::Global(name) => {
writer.write_all(b"#").await?;
writer.write_all(&name).await?;
}
Chan::Local(name) => {
writer.write_all(b"&").await?;
writer.write_all(&name).await?;
}
}
Ok(())
}
}
fn chan(input: &[u8]) -> IResult<&[u8], Chan> {
fn chan_global(input: &[u8]) -> IResult<&[u8], Chan> {
@ -48,3 +66,88 @@ fn chan(input: &[u8]) -> IResult<&[u8], Chan> {
alt((chan_global, chan_local))(input)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Recipient {
Nick(ByteVec),
Chan(Chan),
}
impl Recipient {
pub async fn write_async(&self, writer: &mut (impl AsyncWrite + Unpin)) -> Result<()> {
match self {
Recipient::Nick(nick) => writer.write_all(&nick).await?,
Recipient::Chan(chan) => chan.write_async(writer).await?,
}
Ok(())
}
}
fn recipient(input: &[u8]) -> IResult<&[u8], Recipient> {
fn recipient_chan(input: &[u8]) -> IResult<&[u8], Recipient> {
let (input, chan) = chan(input)?;
Ok((input, Recipient::Chan(chan)))
}
fn recipient_nick(input: &[u8]) -> IResult<&[u8], Recipient> {
let (input, nick) = receiver(input)?;
Ok((input, Recipient::Nick(nick.to_vec())))
}
alt((recipient_chan, recipient_nick))(input)
}
#[cfg(test)]
mod test {
use assert_matches::*;
use super::*;
use crate::util::testkit::*;
#[test]
fn test_chan_global() {
let input = b"#testchan";
let expected = Chan::Global(b"testchan".to_vec());
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
sync_future(expected.write_async(&mut bytes))
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
}
#[test]
fn test_chan_local() {
let input = b"&localchan";
let expected = Chan::Local(b"localchan".to_vec());
let result = chan(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
sync_future(expected.write_async(&mut bytes))
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
}
#[test]
fn test_recipient_user() {
let input = b"User";
let expected = Recipient::Nick(b"User".to_vec());
let result = recipient(input);
assert_matches!(result, Ok((_, result)) => assert_eq!(expected, result));
let mut bytes = vec![];
sync_future(expected.write_async(&mut bytes))
.unwrap()
.unwrap();
assert_eq!(bytes.as_slice(), input);
}
}

View File

@ -55,6 +55,10 @@ pub enum ServerMessageBody {
from: ByteVec,
token: ByteVec,
},
PrivateMessage {
target: Recipient,
body: ByteVec,
},
N001Welcome {
client: ByteVec,
text: ByteVec,
@ -102,6 +106,12 @@ impl ServerMessageBody {
writer.write_all(b" :").await?;
writer.write_all(&token).await?;
}
ServerMessageBody::PrivateMessage { target, body } => {
writer.write_all(b"PRIVMSG ").await?;
target.write_async(writer).await?;
writer.write_all(b" :").await?;
writer.write_all(&body).await?;
}
ServerMessageBody::N001Welcome { client, text } => {
writer.write_all(b"001 ").await?;
writer.write_all(&client).await?;