From 09b95fa6f2d05cdc5c6219958f01b4f23d888505 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Mon, 18 Aug 2025 19:14:30 +0200 Subject: [PATCH] Get rid of .nickname_confirmed confusion by explicitly defining .nickname_wanted vs .nickname set by server. --- ircplom/client.py | 56 +++++++++++++++++++++++-------------------- ircplom/client_tui.py | 25 +++++++------------ 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/ircplom/client.py b/ircplom/client.py index e4e5e60..72e00fd 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -257,7 +257,7 @@ class IrcConnSetup: 'All we need to know to set up a new Client connection.' hostname: str = '' port: int = 0 - nickname: str = '' + nick_wanted: str = '' realname: str = '' password: str = '' @@ -363,14 +363,18 @@ class _ChannelDb(_Db): # channel_modes: str -class _ClientDb(_Db, IrcConnSetup): - caps: _UpdatingDict +class SharedClientDbFields(IrcConnSetup): + 'API for fields shared directly in name and type with TUI.' connection_state: str client_host: str - isupports: _UpdatingDict - nickname_confirmed: bool - user_modes: str + nickname: str sasl_auth_state: str + user_modes: str + + +class _ClientDb(_Db, SharedClientDbFields): + caps: _UpdatingDict + isupports: _UpdatingDict _completable_motd: _CompletableStringsList _channels: dict[str, _ChannelDb] @@ -405,11 +409,11 @@ class _ClientDb(_Db, IrcConnSetup): @property def nick_incremented(self) -> str: - 'Return .nickname with number suffix incremented, or "0" if none yet.' - name, digits = ([(self.nickname, '')] - + [(self.nickname[:i], self.nickname[i:]) - for i in range(len(self.nickname), 0, -1) - if self.nickname[i:].isdigit()] + 'Return .nick_wanted with number suffix incremented, or "0" if none.' + name, digits = ([(self.nick_wanted, '')] + + [(self.nick_wanted[:i], self.nick_wanted[i:]) + for i in range(len(self.nick_wanted), 0, -1) + if self.nick_wanted[i:].isdigit()] )[-1] return name + str(0 if not digits else (int(digits) + 1)) @@ -459,7 +463,7 @@ class Client(ABC, ClientQueueMixin): self._caps.start_negotation() self.send(IrcMessage(verb='USER', params=(getuser(), '0', '*', self._db.realname))) - self.send(IrcMessage(verb='NICK', params=(self._db.nickname,))) + self.send(IrcMessage(verb='NICK', params=(self._db.nick_wanted,))) @abstractmethod def _log(self, msg: str, scope=LogScope.SERVER, **kwargs) -> None: @@ -491,7 +495,7 @@ class Client(ABC, ClientQueueMixin): self.conn.close() self.conn = None self._db.isupports.clear() - self._db.nickname_confirmed = False + self._db.nickname = '' self._db.sasl_auth_state = '' def on_handled_loop_exception(self, e: IrcConnAbortException) -> None: @@ -503,7 +507,7 @@ class Client(ABC, ClientQueueMixin): 'Log msg.raw, then process incoming msg into appropriate client steps.' self._log(msg.raw, scope=LogScope.RAW, out=False) if _NumericsToConfirmNickname.contain(msg.verb): - self.set_nick(msg.params[0], confirm=True) + self.set_nick(msg.params[0], confirmed=True) if _NumericsToIgnore.contain(msg.verb): return if msg.match('005', 2, True): # RPL_ISUPPORT @@ -522,13 +526,13 @@ class Client(ABC, ClientQueueMixin): # claims: " can also be in the form " self._db.client_host = msg.params[1].split('@')[-1] elif msg.match('432', 3): # ERR_ERRONEOUSNICKNAME + alert = 'nickname refused for bad format' if msg.params[0] == '*': - self._log(alert=True, - msg='nickname refused for bad format, giving up') + alert += ', giving up' self.close() else: - self._db.nickname = msg.params[0] - self._db.nickname_confirmed = True + self.set_nick(msg.params[0], confirmed=True) + self._log(alert, alert=True) elif msg.match('433', 3): # ERR_NICKNAMEINUSE self._log('nickname already in use, trying increment', alert=True) self.set_nick(self._db.nick_incremented) @@ -555,7 +559,7 @@ class Client(ABC, ClientQueueMixin): elif msg.match('MODE', 2) and msg.params[0] == self._db.nickname: self._db.user_modes = msg.params[1] elif msg.match('NICK'): - self.set_nick(msg.params[0], confirm=True) + self.set_nick(msg.params[0], confirmed=True) elif msg.match('NOTICE', 2) or msg.match('PRIVMSG', 2): scope = LogScope.CHAT if '!' in msg.source else LogScope.SERVER self._log(msg.params[-1], scope=scope, channel=msg.params[0], @@ -578,13 +582,13 @@ class Client(ABC, ClientQueueMixin): else: self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}') - def set_nick(self, new_nickname: str, confirm=False) -> None: - 'Set ClientDb\'s .nickname, .…_confirmed, and sends NICK if connected.' - if new_nickname != self._db.nickname: - if self.conn: - self.send(IrcMessage(verb='NICK', params=(new_nickname,))) - self._db.nickname = new_nickname - self._db.nickname_confirmed = confirm + def set_nick(self, nickname: str, confirmed=False) -> None: + 'Set ClientDb\'s .nick wanted, send NICK if != .nickname.' + if confirmed: + self._db.nickname = nickname + self._db.nick_wanted = nickname + if self.conn and self._db.nick_wanted != self._db.nickname: + self.send(IrcMessage(verb='NICK', params=(self._db.nick_wanted,))) @dataclass diff --git a/ircplom/client_tui.py b/ircplom/client_tui.py index 2f66d7b..84823a4 100644 --- a/ircplom/client_tui.py +++ b/ircplom/client_tui.py @@ -7,8 +7,9 @@ from typing import Callable, Optional, Sequence from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window, CMD_SHORTCUTS) from ircplom.irc_conn import IrcMessage -from ircplom.client import (Client, ClientQueueMixin, Db, IrcConnSetup, - LogScope, NewClientEvent, ServerCapability) +from ircplom.client import ( + Client, ClientQueueMixin, Db, IrcConnSetup, LogScope, NewClientEvent, + ServerCapability, SharedClientDbFields) CMD_SHORTCUTS['disconnect'] = 'window.disconnect' CMD_SHORTCUTS['join'] = 'window.join' @@ -49,7 +50,7 @@ class _ClientWindow(Window, ClientQueueMixin): def cmd__nick(self, new_nick: str) -> None: 'Attempt nickname change.' - self._client_trigger('set_nick', new_nickname=new_nick) + self._client_trigger('set_nick', nickname=new_nick) def cmd__join(self, channel: str) -> None: 'Attempt joining a channel.' @@ -71,17 +72,13 @@ class _ClientWindow(Window, ClientQueueMixin): class _ChatPrompt(PromptWidget): _nickname: str = '' - _nick_confirmed: bool = False @property def prefix(self) -> str: - return (' ' if self._nick_confirmed else '?') + f'[{self._nickname}] ' + return f'[{self._nickname}] ' - def set_prefix_data(self, nick: str, confirmed: bool) -> None: + def set_prefix_data(self, nick: str) -> None: 'Update prompt prefix with nickname data.' - if confirmed != self._nick_confirmed: - self._tainted = True - self._nick_confirmed = confirmed if nick != self._nickname: self._tainted = True self._nickname = nick @@ -170,15 +167,10 @@ class _ChannelDb(_Db): return super().set_and_check_for_change(update) -class _TuiClientDb(_Db, IrcConnSetup): +class _TuiClientDb(_Db, SharedClientDbFields): caps: dict[str, str] - client_host: str - connection_state: str isupports: dict[str, str] motd: tuple[str] - nickname_confirmed: bool - sasl_auth_state: str - user_modes: str _channels: dict[str, _ChannelDb] def set_and_check_for_change(self, update: _Update) -> bool: @@ -218,8 +210,7 @@ class _ClientWindowsManager: kwargs['win_cls'] = (_ChannelWindow if chatname[0] == '#' else _ChatWindow) kwargs['chatname'] = chatname - kwargs['get_nick_data'] = lambda: (self._db.nickname, - self._db.nickname_confirmed) + kwargs['get_nick_data'] = lambda: (self._db.nickname,) win = self._tui_new_window(**kwargs) self.windows += [win] return win -- 2.30.2