home · contact · privacy
Get rid of .nickname_confirmed confusion by explicitly defining .nickname_wanted...
authorChristian Heller <c.heller@plomlompom.de>
Mon, 18 Aug 2025 17:14:30 +0000 (19:14 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 18 Aug 2025 17:14:30 +0000 (19:14 +0200)
ircplom/client.py
ircplom/client_tui.py

index e4e5e601df3afbcf7742548f4876f51472a3af51..72e00fddeef31afd123c95fcba4931e188459eec 100644 (file)
@@ -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: "<hostname> can also be in the form <user@hostname>"
             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
index 2f66d7bb1ec2bb5db7625d23b6d834c9b5cb30b7..84823a4d96d95ac377d18440e458a85f29537d60 100644 (file)
@@ -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