forked from lavina/lavina
handle join and privmsg irc commands
This commit is contained in:
parent
20b461e81c
commit
89f85b4fee
|
@ -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(())
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?;
|
||||
|
|
Loading…
Reference in New Issue