🏡 index : ~doyle/titanirc.git

author Jordan Doyle <jordan@doyle.la> 2023-01-11 0:22:31.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2023-01-11 0:27:57.0 +00:00:00
commit
b7a472e34e89ec2bf0a128e30fba86c2fff16ca4 [patch]
tree
5b75f7b0ae6de96d0e7e0dfb768ed06ea12a0cc4
parent
150368f3bc01d79c4d6ecc0b12aa755e5a7bf3dc
download
b7a472e34e89ec2bf0a128e30fba86c2fff16ca4.tar.gz

Implement peer-to-peer messaging



Diff

 src/channel.rs  |  1 +
 src/client.rs   | 12 ++++++++++--
 src/messages.rs | 10 ++++++++++
 src/server.rs   | 36 +++++++++++++++++++++++++++++++++++-
 4 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/src/channel.rs b/src/channel.rs
index 25e08d4..d77662f 100644
--- a/src/channel.rs
+++ a/src/channel.rs
@@ -116,6 +116,7 @@
        // build the nick prefix for the message we're about to broadcast
        let nick = sender.to_nick();

        // TODO: implement client msg recv acks
        self.persistence
            .do_send(crate::persistence::events::ChannelMessage {
                channel_id: self.channel_id,
diff --git a/src/client.rs b/src/client.rs
index f96afd1..5dcb23b 100644
--- a/src/client.rs
+++ a/src/client.rs
@@ -16,8 +16,8 @@
    messages::{
        Broadcast, ChannelFetchTopic, ChannelInvite, ChannelJoin, ChannelKickUser, ChannelList,
        ChannelMemberList, ChannelMessage, ChannelPart, ChannelSetMode, ChannelUpdateTopic,
        FetchClientDetails, ServerDisconnect, ServerFetchMotd, UserKickedFromChannel,
        UserNickChange, UserNickChangeInternal,
        FetchClientDetails, PeerToPeerMessage, ServerDisconnect, ServerFetchMotd,
        UserKickedFromChannel, UserNickChange, UserNickChangeInternal,
    },
    persistence::{
        events::{FetchUnseenMessages, FetchUserChannels, ReserveNick},
@@ -511,7 +511,12 @@
            Command::PRIVMSG(target, message) => {
                if !target.is_channel_name() {
                    // private message to another user
                    error!("Private messages not implemented");
                    self.server.do_send(PeerToPeerMessage {
                        destination: target,
                        message,
                        from: ctx.address(),
                        span: Span::current(),
                    });
                } else if let Some(channel) = self.channels.get(&target) {
                    channel.do_send(ChannelMessage {
                        client: ctx.address(),
@@ -586,6 +591,7 @@
            Command::SAJOIN(_, _) => {}
            Command::SAMODE(_, _, _) => {}
            Command::SANICK(old_nick, new_nick) => {
                // TODO: permission checks
                self.server.do_send(UserNickChangeInternal {
                    old_nick,
                    new_nick,
diff --git a/src/messages.rs b/src/messages.rs
index f261b95..68dfbdf 100644
--- a/src/messages.rs
+++ a/src/messages.rs
@@ -168,3 +168,13 @@
pub struct FetchClientByNick {
    pub nick: String,
}

/// Sends a private message between two users.

#[derive(Message)]
#[rtype(result = "()")]
pub struct PeerToPeerMessage {
    pub destination: String,
    pub message: String,
    pub from: Addr<Client>,
    pub span: Span,
}
diff --git a/src/server.rs b/src/server.rs
index ac57ed1..548e56a 100644
--- a/src/server.rs
+++ a/src/server.rs
@@ -21,8 +21,8 @@
    connection::InitiatedConnection,
    messages::{
        Broadcast, ChannelFetchTopic, ChannelJoin, ChannelList, ChannelMemberList,
        FetchClientByNick, ServerDisconnect, ServerFetchMotd, UserConnected, UserNickChange,
        UserNickChangeInternal,
        FetchClientByNick, PeerToPeerMessage, ServerDisconnect, ServerFetchMotd, UserConnected,
        UserNickChange, UserNickChangeInternal,
    },
    persistence::Persistence,
    server::response::Motd,
@@ -250,6 +250,38 @@
            });

        Box::pin(fut)
    }
}

// TODO: implement offline messaging and replay
impl Handler<PeerToPeerMessage> for Server {
    type Result = ();

    #[instrument(parent = &msg.span, skip_all)]
    fn handle(&mut self, msg: PeerToPeerMessage, _ctx: &mut Self::Context) -> Self::Result {
        let Some(source) = self.clients.get(&msg.from) else {
            // user is not yet registered with the server
            return;
        };

        // TODO: O(1) lookup of users by nick
        let target = self
            .clients
            .iter()
            .find(|(_handle, connection)| connection.nick == msg.destination);
        let Some((target, _)) = target else {
            // return error to caller that user does not exist
            return;
        };

        target.do_send(Broadcast {
            message: Message {
                tags: None,
                prefix: Some(source.to_nick()),
                command: Command::PRIVMSG(msg.destination, msg.message),
            },
            span: msg.span,
        });
    }
}