diff --git a/paircd/channel.py b/paircd/channel.py index f636126..35fb0c2 100644 --- a/paircd/channel.py +++ b/paircd/channel.py @@ -47,6 +47,9 @@ class Channel: return f"+{nick}" return nick + def get_channel_size(self) -> int: + return len(self.clients_by_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}") diff --git a/paircd/client.py b/paircd/client.py index 4ad6880..5afa036 100644 --- a/paircd/client.py +++ b/paircd/client.py @@ -5,7 +5,7 @@ from datetime import datetime from logging import log, INFO from typing import Any, Set -from paircd.reply import QUIT, RPL_CREATED, RPL_MYINFO, RPL_WELCOME, RPL_YOURHOST +from paircd.reply import RPL_CREATED, RPL_MYINFO, RPL_WELCOME, RPL_YOURHOST from paircd.message import Message @@ -48,22 +48,9 @@ class Client: try: await self.writer.drain() except ConnectionResetError: - await self.quit(server, "Connection reset by peer") - - async def quit(self, server: Any, msg: str) -> None: - if self.closed: - return - - quit_msg = QUIT(msg, prefix=self.id()) - for client in server.clients_by_nick.values(): - client.write_message(quit_msg) - for channel_name in self.channels: - channel = server.get_channel_by_name(channel_name, create=False) - if channel is None: - continue - channel.remove_client_by_nick(self.nickname) - server.remove_client_by_name(self.nickname) - await self.close() + await server.disconnect_client( + self.nickname, "Connection reset by peer" + ) async def close(self) -> None: if self.closed: diff --git a/paircd/handler/quit.py b/paircd/handler/quit.py index 5e426ce..e8010a4 100644 --- a/paircd/handler/quit.py +++ b/paircd/handler/quit.py @@ -12,4 +12,4 @@ class QuitHandler(CommandHandler): quit_msg = "" if len(msg.args) == 1: quit_msg = msg.args[0] - await client.quit(server, f"Quit: {quit_msg}") + await server.disconnect_client(client.nickname, f"Quit: {quit_msg}") diff --git a/paircd/main.py b/paircd/main.py index 3e2b1eb..08e0b8e 100644 --- a/paircd/main.py +++ b/paircd/main.py @@ -23,7 +23,7 @@ async def read_forever(server: Server, client: Client) -> None: raw_msg = await client.reader.readuntil(b"\r\n") except IncompleteReadError: logging.warning("client connection closed", exc_info=exc_info()) - await client.quit(server, "Connection reset by peer") + await server.disconnect_client(client.nickname, "Connection reset by peer") break msg = parse_message(raw_msg) diff --git a/paircd/server.py b/paircd/server.py index 0e77304..8303912 100644 --- a/paircd/server.py +++ b/paircd/server.py @@ -4,6 +4,7 @@ from typing import Dict, Optional from paircd.client import Client from paircd.channel import Channel +from paircd.reply import QUIT @dataclass @@ -33,3 +34,27 @@ class Server: 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] + + async def disconnect_client(self, nick: str, quit_msg: str) -> None: + client = self.clients_by_nick[nick] + + if client.closed: + return + + client.log("is quitting: {quit_msg}") + msg = QUIT(quit_msg, prefix=client.id()) + for client in self.clients_by_nick.values(): + if client.nickname == nick: + continue + client.write_message(msg) + + for channel_name in client.channels: + channel = self.get_channel_by_name(channel_name, create=False) + if channel is None: + continue + channel.remove_client_by_nick(client.nickname) + if channel.get_channel_size() == 0: + del self.channels_by_name[channel_name] + + self.remove_client_by_name(client.nickname) + await client.close()