From 360a4d4a96dfcbecc6c257394f081ef9a4f767a6 Mon Sep 17 00:00:00 2001 From: Forest Belton <65484+forestbelton@users.noreply.github.com> Date: Tue, 22 Jun 2021 17:31:38 -0400 Subject: [PATCH] Broadcast message when client updates their nickname --- paircd/channel.py | 10 ++++++++++ paircd/handler/nick.py | 27 +++++++++++++++++---------- paircd/reply.py | 2 ++ paircd/server.py | 8 ++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/paircd/channel.py b/paircd/channel.py index c573f0b..679b9c2 100644 --- a/paircd/channel.py +++ b/paircd/channel.py @@ -34,3 +34,13 @@ class Channel: def get_user_modes_by_nick(self, nick: str) -> Set[str]: return self.modes_by_nick[nick] + + def rename_client(self, name: str, new_name: str) -> None: + if name not in self.clients_by_nick: + raise KeyError(f"No client on channel with nick {name}") + if new_name in self.clients_by_nick: + raise KeyError(f"Client already exists with nick {new_name}") + self.clients_by_nick[new_name] = self.clients_by_nick[name] + del self.clients_by_nick[name] + self.modes_by_nick[new_name] = self.modes_by_nick[name] + del self.modes_by_nick[name] diff --git a/paircd/handler/nick.py b/paircd/handler/nick.py index 46620f6..cd0352d 100644 --- a/paircd/handler/nick.py +++ b/paircd/handler/nick.py @@ -1,7 +1,7 @@ from paircd.client import Client from paircd.command_handler import CommandHandler from paircd.message import Message -from paircd.reply import ERR_NICKNAMEINUSE +from paircd.reply import ERR_NICKNAMEINUSE, ERR_NONICKNAMEGIVEN, NICK from paircd.server import Server @@ -11,6 +11,10 @@ class NickHandler(CommandHandler): async def handle(self, server: Server, client: Client, msg: Message) -> None: nickname = msg.args[0] + if nickname == "": + target = client.nickname if client.nickname else nickname + client.write_message(ERR_NONICKNAMEGIVEN(target)) + return existing_client = server.get_client_by_name(nickname) if existing_client is not None: @@ -19,19 +23,22 @@ class NickHandler(CommandHandler): client.write_message(ERR_NICKNAMEINUSE(target, nickname)) return - # Remove stale references if they exist - if client.nickname: - server.remove_client_by_name(client.nickname) - for channel_name in client.channels: - channel = server.get_channel_by_name(channel_name) - channel.remove_client_by_nick(client.nickname) + rename = client.nickname != "" + old_nick = client.nickname + old_id = client.id() client.nickname = nickname + if rename: + server.rename_client(old_nick, nickname) + else: + server.add_client(client) - # Add references for client - server.add_client(client) for channel_name in client.channels: channel = server.get_channel_by_name(channel_name) - channel.remove_client_by_nick(client.nickname) + if rename: + channel.rename_client(old_nick, nickname) + channel.write_message(NICK(nickname, prefix=old_id)) + else: + raise RuntimeError("shouldn't be in any channels without a nick") client.register() diff --git a/paircd/reply.py b/paircd/reply.py index 07050a2..77bdd2b 100644 --- a/paircd/reply.py +++ b/paircd/reply.py @@ -26,6 +26,7 @@ def reply_fn(cmd: Union[int, str], tmpl: str) -> Callable: # Commands JOIN = cmd_fn("JOIN", "{0}") MODE = cmd_fn("MODE", "{0} {1}") +NICK = cmd_fn("NICK", "{0}") PONG = cmd_fn("PONG", ":{0}") PRIVMSG = cmd_fn("PRIVMSG", "{0} :{1}") @@ -35,6 +36,7 @@ ERR_NOSUCHSERVER = reply_fn(402, "{0} :No such server") ERR_NOSUCHCHANNEL = reply_fn(403, "{0} :No such channel") ERR_CANNOTSENDTOCHAN = reply_fn(404, "{0} :Cannot send to channel") ERR_TOOMANYCHANNELS = reply_fn(405, "{0} :You have joined too many channels") +ERR_NONICKNAMEGIVEN = reply_fn(431, ":No nickname given") ERR_NICKNAMEINUSE = reply_fn(433, "{0} :Nickname is already in use") ERR_NOTEXTTOSEND = reply_fn(412, ":No text to send") ERR_NOTREGISTERED = reply_fn(451, ":You have not registered") diff --git a/paircd/server.py b/paircd/server.py index db491d1..f20b97f 100644 --- a/paircd/server.py +++ b/paircd/server.py @@ -25,3 +25,11 @@ class Server: def remove_client_by_name(self, name: str) -> None: del self.clients_by_nick[name] + + def rename_client(self, name: str, new_name: str) -> None: + if name not in self.clients_by_nick: + raise KeyError(f"No client on server with nick {name}") + if new_name in self.clients_by_nick: + raise KeyError(f"Client already exists with nick {new_name}") + self.clients_by_nick[new_name] = self.clients_by_nick[name] + del self.clients_by_nick[name]