home · contact · privacy
Allow disconnected /nick changes, and only disconnect on 432 if no fallback.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 18 Aug 2025 16:23:44 +0000 (18:23 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 18 Aug 2025 16:23:44 +0000 (18:23 +0200)
ircplom/client.py
ircplom/client_tui.py

index 248a3ba68e5702ae84d3e403099e3e1b40eacc86..e4e5e601df3afbcf7742548f4876f51472a3af51 100644 (file)
@@ -45,15 +45,6 @@ class _IrcMsg(IrcMessage):
                 and (n_msg_params == len_params
                      or len_is_min and n_msg_params > len_params))
 
-    def nick_conforms(self, db: '_ClientDb') -> bool:
-        'Test .nick_from_source == db.nickname.'
-        return self.nick_from_source == db.nickname
-
-    def confirm_nick(self, db: '_ClientDb') -> None:
-        'Assume .params[0] confirms nickname.'
-        db.nickname = self.params[0]
-        db.nickname_confirmed = True
-
     @property
     def nick_from_source(self) -> str:
         'Parse .source into user nickname.'
@@ -512,7 +503,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):
-            msg.confirm_nick(self._db)
+            self.set_nick(msg.params[0], confirm=True)
         if _NumericsToIgnore.contain(msg.verb):
             return
         if msg.match('005', 2, True):  # RPL_ISUPPORT
@@ -531,12 +522,16 @@ 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
-            self._log('nickname refused for bad format, giving up', alert=True)
-            self.close()
+            if msg.params[0] == '*':
+                self._log(alert=True,
+                          msg='nickname refused for bad format, giving up')
+                self.close()
+            else:
+                self._db.nickname = msg.params[0]
+                self._db.nickname_confirmed = True
         elif msg.match('433', 3):  # ERR_NICKNAMEINUSE
-            new_nick = self._db.nick_incremented
-            self._log('nickname already in use, trying {new_nick}', alert=True)
-            self.send(IrcMessage(verb='NICK', params=(new_nick,)))
+            self._log('nickname already in use, trying increment', alert=True)
+            self.set_nick(self._db.nick_incremented)
         elif msg.match('903', 2) or msg.match('904', 2):  # RPL_SUCESS, or …
             self._db.sasl_auth_state = 'WIN' if msg.verb == '903' else 'FAIL'
             self._caps.end_negotiation()                  # … ERR_SASLFAIL
@@ -559,8 +554,8 @@ class Client(ABC, ClientQueueMixin):
             self.close()
         elif msg.match('MODE', 2) and msg.params[0] == self._db.nickname:
             self._db.user_modes = msg.params[1]
-        elif msg.match('NICK') and msg.nick_conforms(self._db):
-            msg.confirm_nick(self._db)
+        elif msg.match('NICK'):
+            self.set_nick(msg.params[0], confirm=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],
@@ -574,7 +569,7 @@ class Client(ABC, ClientQueueMixin):
             self._log(log_msg, scope=LogScope.CHAT, channel=channel)
             if msg.verb == 'JOIN':
                 self._db.chan(channel).users.append(msg.nick_from_source)
-            elif msg.nick_conforms(self._db):
+            elif msg.nick_from_source == self._db.nickname:
                 self._db.del_chan(channel)
             else:
                 self._db.chan(channel).users.remove(msg.nick_from_source)
@@ -583,6 +578,14 @@ 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
+
 
 @dataclass
 class NewClientEvent(AffectiveEvent):
index f0fbd3f5a213e033fdae5f65d6bf82bb06db6f21..2f66d7bb1ec2bb5db7625d23b6d834c9b5cb30b7 100644 (file)
@@ -49,7 +49,7 @@ class _ClientWindow(Window, ClientQueueMixin):
 
     def cmd__nick(self, new_nick: str) -> None:
         'Attempt nickname change.'
-        self._send_msg('NICK', (new_nick,))
+        self._client_trigger('set_nick', new_nickname=new_nick)
 
     def cmd__join(self, channel: str) -> None:
         'Attempt joining a channel.'