🏡 index : ~doyle/titanirc.git

author Jordan Doyle <jordan@doyle.la> 2023-01-07 21:13:51.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2023-01-07 21:13:51.0 +00:00:00
commit
b736bad1812959761fc49fd492f945028d8b5870 [patch]
tree
34f18c669588b784a8d772cc2df1f950ad3fc23b
parent
250b43f34b0cea3daff6f718a95133b585fe1584
download
b736bad1812959761fc49fd492f945028d8b5870.tar.gz

Implement KICK command



Diff

 src/channel.rs  | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---
 src/client.rs   | 31 ++++++++++++++++++++++++++++---
 src/messages.rs | 18 ++++++++++++++++++
 3 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/src/channel.rs b/src/channel.rs
index 9a12dc3..83fd925 100644
--- a/src/channel.rs
+++ b/src/channel.rs
@@ -4,7 +4,7 @@ use std::collections::HashMap;

use actix::{Actor, Addr, AsyncContext, Context, Handler, MessageResult};
use chrono::{DateTime, Utc};
use irc_proto::Command;
use irc_proto::{Command, Message};
use tracing::{debug, error, info, instrument, Span};

use crate::{
@@ -12,8 +12,9 @@ use crate::{
    client::Client,
    connection::InitiatedConnection,
    messages::{
        Broadcast, ChannelFetchTopic, ChannelJoin, ChannelMemberList, ChannelMessage, ChannelPart,
        ChannelUpdateTopic, ServerDisconnect, UserNickChange,
        Broadcast, ChannelFetchTopic, ChannelJoin, ChannelKickUser, ChannelMemberList,
        ChannelMessage, ChannelPart, ChannelUpdateTopic, ServerDisconnect, UserKickedFromChannel,
        UserNickChange,
    },
};

@@ -179,6 +180,51 @@ impl Handler<ChannelUpdateTopic> for Channel {
    }
}

/// Sent from an oper client to remove a user from the channel.
impl Handler<ChannelKickUser> for Channel {
    type Result = ();

    fn handle(&mut self, msg: ChannelKickUser, _ctx: &mut Self::Context) -> Self::Result {
        let Some(kicker) = self.clients.get(&msg.client) else {
            error!("Kicker is unknown");
            return;
        };
        let kicker = kicker.to_nick();

        let kicked_user = self
            .clients
            .iter()
            .find(|(_handle, client)| client.nick == msg.user)
            .map(|(k, v)| (k.clone(), v));
        let Some((kicked_user_handle, kicked_user_info)) = kicked_user else {
            error!(msg.user, "Attempted to kick unknown user");
            return;
        };

        for client in self.clients.keys() {
            client.do_send(Broadcast {
                message: Message {
                    tags: None,
                    prefix: Some(kicker.clone()),
                    command: Command::KICK(
                        self.name.to_string(),
                        kicked_user_info.nick.to_string(),
                        msg.reason.clone(),
                    ),
                },
                span: Span::current(),
            });
        }

        kicked_user_handle.do_send(UserKickedFromChannel {
            channel: self.name.to_string(),
            span: Span::current(),
        });

        self.clients.remove(&kicked_user_handle);
    }
}

/// Returns the current channel topic to the user.
impl Handler<ChannelFetchTopic> for Channel {
    type Result = MessageResult<ChannelFetchTopic>;
diff --git a/src/client.rs b/src/client.rs
index bf015fb..6768949 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -13,8 +13,9 @@ use crate::{
    channel::Channel,
    connection::{InitiatedConnection, MessageSink},
    messages::{
        Broadcast, ChannelFetchTopic, ChannelJoin, ChannelList, ChannelMemberList, ChannelMessage,
        ChannelPart, ChannelUpdateTopic, FetchClientDetails, ServerDisconnect, UserNickChange,
        Broadcast, ChannelFetchTopic, ChannelJoin, ChannelKickUser, ChannelList, ChannelMemberList,
        ChannelMessage, ChannelPart, ChannelUpdateTopic, FetchClientDetails, ServerDisconnect,
        UserKickedFromChannel, UserNickChange,
    },
    server::Server,
    SERVER_NAME,
@@ -234,6 +235,16 @@ impl Handler<UserNickChange> for Client {
    }
}

/// Sent by channels when the current user is removed from it.
impl Handler<UserKickedFromChannel> for Client {
    type Result = ();

    #[instrument(parent = &msg.span, skip_all)]
    fn handle(&mut self, msg: UserKickedFromChannel, _ctx: &mut Self::Context) -> Self::Result {
        self.channels.remove(&msg.channel);
    }
}

/// Receives messages from the user's incoming TCP stream and processes them, passing them onto
/// other actors or self-notifying and calling a [`Handler`].
impl StreamHandler<Result<irc_proto::Message, ProtocolError>> for Client {
@@ -384,7 +395,21 @@ impl StreamHandler<Result<irc_proto::Message, ProtocolError>> for Client {
                ctx.spawn(fut);
            }
            Command::INVITE(_, _) => {}
            Command::KICK(_, _, _) => {}
            Command::KICK(channel, users, reason) => {
                let Some(channel) = self.channels.get(&channel) else {
                    error!(%channel, "User not connected to channel");
                    return;
                };

                for user in parse_channel_name_list(&users) {
                    channel.do_send(ChannelKickUser {
                        span: Span::current(),
                        client: ctx.address(),
                        user,
                        reason: reason.clone(),
                    });
                }
            }
            Command::PRIVMSG(target, message) => {
                if !target.is_channel_name() {
                    // private message to another user
diff --git a/src/messages.rs b/src/messages.rs
index 83998a9..1dd8bec 100644
--- a/src/messages.rs
+++ b/src/messages.rs
@@ -73,6 +73,24 @@ pub struct ChannelFetchTopic {
    pub span: Span,
}

/// Attempts to kick a user from a channel.
#[derive(Message)]
#[rtype(result = "()")]
pub struct ChannelKickUser {
    pub span: Span,
    pub client: Addr<Client>,
    pub user: String,
    pub reason: Option<String>,
}

/// Sent from channels to users when a user is removed from the channel.
#[derive(Message)]
#[rtype(result = "()")]
pub struct UserKickedFromChannel {
    pub channel: String,
    pub span: Span,
}

/// Sent from a particular user to a channel when the user attempts to update the
/// topic.
#[derive(Message)]