home · contact · privacy
To _UpdatingDict add deletion of items, explicitly for 005 reactions.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 17 Aug 2025 05:09:44 +0000 (07:09 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 17 Aug 2025 05:09:44 +0000 (07:09 +0200)
ircplom/client.py
ircplom/client_tui.py

index c648c20451108493fb37df373756dd754ecdf91b..777e1ad140a8a4c75defc1feda4dce5198fb4095 100644 (file)
@@ -336,6 +336,10 @@ class _UpdatingDict:
         self._dict[key] = val
         self._on_update(key)
 
+    def __delitem__(self, key: str) -> None:
+        del self._dict[key]
+        self._on_update(f'-{key}')
+
 
 class _ClientDb(_Db, IrcConnSetup):
     connection_state: str
@@ -456,16 +460,19 @@ class Client(ABC, ClientQueueMixin):
             channel = msg.params[0]
             log_msg = f'{msg.nick_from_source} {msg.verb.lower()}s {channel}'
         match msg.verb:
-            case '005' if len(msg.params) > 2:
+            case '005' if len(msg.params) > 2:  # RPL_ISUPPORT
                 for param in msg.params[1:-1]:
                     toks = param.split('=', maxsplit=1)
-                    self._db.isupports[toks[0]] = (toks[1] if len(toks) > 1
-                                                   else '')
-            case '353' if len(msg.params) == 4:
+                    if toks[0][0] == '-':
+                        del self._db.isupports[toks[0][1:]]
+                    else:
+                        self._db.isupports[toks[0]] = (
+                                toks[1] if len(toks) > 1 else '')
+            case '353' if len(msg.params) == 4:  # RPL_NAMREPLY
                 for user in msg.params[3].split():
                     self._db.chan(msg.params[2]
                                   ).append_completable('users', user)
-            case '366' if len(msg.params) == 3:
+            case '366' if len(msg.params) == 3:  # RPL_ENDOFNAMES
                 self._db.chan(msg.params[1]).declare_complete('users')
             case '372' if len(msg.params) == 2:  # RPL_MOTD
                 self._db.append_completable('motd', msg.params[1])
@@ -475,6 +482,11 @@ class Client(ABC, ClientQueueMixin):
                 # '@'-split because <https://defs.ircdocs.horse/defs/numerics>
                 # claims: "<hostname> can also be in the form <user@hostname>"
                 self._db.client_host = msg.params[1].split('@')[-1]
+            case '903' | '904' if len(msg.params) == 2:  # RPL_SASLSUCESS
+                alert = msg.verb == '904'                # ERR_SASLFAIL
+                self._log(f'SASL auth {"failed" if alert else "succeeded"}',
+                          alert=alert)
+                self._caps.end_negotiation()
             case 'AUTHENTICATE' if msg.params == ('+',):
                 auth = b64encode((self._db.nickname + '\0' +
                                   self._db.nickname + '\0' +
@@ -518,11 +530,6 @@ class Client(ABC, ClientQueueMixin):
                     self._db.chan(channel).users.remove(msg.nick_from_source)
             case 'PING' if len(msg.params) == 1:
                 self.send(IrcMessage(verb='PONG', params=(msg.params[0],)))
-            case '903' | '904' if len(msg.params) == 2:
-                alert = msg.verb == '904'
-                self._log(f'SASL auth {"failed" if alert else "succeeded"}',
-                          alert=alert)
-                self._caps.end_negotiation()
             case _:
                 self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}')
 
index a0277c7eeb88ec4f26452c12b2dd659be1cb983f..1e55a6010b2cb8d82bf87fd9f5b2a6358eba5a51 100644 (file)
@@ -157,10 +157,13 @@ class _TuiClientDb(_Db, IrcConnSetup):
             d = getattr(self, dict_name)
             if arg == CLEAR_WORD:
                 d.clear()
-                return True
-            old_value = d.get(arg, None)
-            d[arg] = value
-            return value != old_value
+            elif arg[0] == '-':
+                del d[arg[1:]]
+            else:
+                old_value = d.get(arg, None)
+                d[arg] = value
+                return value != old_value
+            return True
         return super().set_and_check_for_change(key, value)
 
     def chan(self, name: str) -> _ChannelDb:
@@ -222,33 +225,37 @@ class _ClientWindowsManager:
         db: _TuiClientDb | _ChannelDb = self._db
         scope = LogScope.SERVER
         log_kwargs: dict[str, str] = {}
-        clearing = False
+        verb = 'changed to:'
         what = key
         if ' ' in key:
             type_char, parent_name, subkey = key.split()
-            clearing = subkey == CLEAR_WORD
             if type_char == 'c':
                 db = self._db.chan(parent_name)
                 scope = LogScope.CHAT
                 log_kwargs |= {'channel': parent_name}
+            if subkey == CLEAR_WORD:
+                verb = 'cleared'
+                what = parent_name
+            elif type_char == 'c':
                 key = what = subkey
             else:
+                if subkey[0] == '-':
+                    verb = 'unset'
+                    subkey = subkey[1:]
                 what = f'{parent_name}:{subkey}'
         elif key == 'connection_state':
             scope = LogScope.ALL
         if not db.set_and_check_for_change(key, value):
             return False
-        if not clearing:
-            announcement = f'changed {what} to:'
-            if isinstance(value, tuple):
-                self.log(announcement, scope=scope, **log_kwargs)
-                for item in value:
-                    self.log(f'  {item}', scope=scope, **log_kwargs)
-            else:
-                self.log(f'{announcement} [{value}]',
-                         scope=scope, **log_kwargs)
-        for win in [w for w in self.windows
-                    if isinstance(w, _ChatWindow)]:
+        announcement = f'{what} {verb}'
+        if isinstance(value, tuple) or announcement[-1] != ':':
+            self.log(announcement, scope=scope, **log_kwargs)
+        if isinstance(value, tuple):
+            for item in value:
+                self.log(f'  {item}', scope=scope, **log_kwargs)
+        elif announcement[-1] == ':':
+            self.log(f'{announcement} [{value}]', scope=scope, **log_kwargs)
+        for win in [w for w in self.windows if isinstance(w, _ChatWindow)]:
             win.set_prompt_prefix()
         return bool([w for w in self.windows if w.tainted])
 
@@ -353,7 +360,7 @@ class _ClientKnowingTui(Client):
             value = tuple(lines)
         elif ' ' in key:
             type_char, parent_name, arg = key.split()
-            if arg == CLEAR_WORD:
+            if arg == CLEAR_WORD or arg[0] == '-':
                 value = ''
             elif type_char == 'c':
                 value = getattr(self._db.chan(parent_name), arg)