home · contact · privacy
Improve CAPS collection logic, display. master
authorChristian Heller <c.heller@plomlompom.de>
Sun, 27 Jul 2025 17:18:14 +0000 (19:18 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 27 Jul 2025 17:18:14 +0000 (19:18 +0200)
ircplom/irc_conn.py

index b24f5558f959d0bd30645bfcb2316135e64688f4..deafb544add12004dcc674c8e23cf4c5bae16776 100644 (file)
@@ -153,8 +153,6 @@ class _ConnectedEvent(ClientEvent):
     def affect(self, target: 'Client') -> None:
         target.log(msg='# connected to server', chat=CHAT_GLOB)
         target.send(IrcMessage(verb='CAP', params=('LS', '302')))
-        target.send(IrcMessage(verb='CAP', params=('LIST',)))
-        target.send(IrcMessage(verb='CAP', params=('END',)))
         target.send(IrcMessage(verb='USER', params=(getuser(), '0', '*',
                                                     target.realname)))
         target.send(IrcMessage(verb='NICK', params=(target.nickname,)))
@@ -203,8 +201,8 @@ class Client(ABC, ClientQueueMixin):
         self._hostname = hostname
         self._socket: Optional[socket] = None
         self._recv_loop: Optional[Loop] = None
-        self._cap_ls_done = False
-        self._caps: dict[str, Optional[bool]] = {}
+        self.cap_neg_state: set[str] = set()
+        self.caps: dict[str, tuple[bool, str]] = {}
         self.id_ = uuid4()
         self.assumed_open = False
         self.realname = realname
@@ -235,26 +233,30 @@ class Client(ABC, ClientQueueMixin):
         Thread(target=connect, daemon=True, args=(self,)).start()
 
     def collect_caps(self, params: tuple[str, ...]) -> None:
-        'Record and list available or enabled server capabilities.'
-        collect_enabled = params[0] != 'LS'
+        'Record available and enabled server capabilities.'
+        verb = params[0]
+        items = params[-1].strip().split()
         is_final_line = params[1] != '*'
-        listed_names = params[-1].strip().split()
-        if collect_enabled:
-            for k in [k for k, v in self._caps.items() if v is None]:
-                self._caps[k] = False
-            for name in [name for name in self._caps
-                         if name.split('=')[0] in listed_names]:
-                self._caps[name] = True
-        else:
-            if self._cap_ls_done:
-                self._caps.clear()
-            for cap_name in listed_names:
-                self._caps[cap_name] = None
-            self._cap_ls_done = is_final_line
-        if is_final_line and collect_enabled:
-            self.log('# server capabilities available (* if enabled): '
-                     + ', '.join([(f'(*){k}' if v else k)
-                                  for k, v in self._caps.items()]))
+        doneness = f'{verb} done'
+        waiting = f'{verb} wait'
+        if doneness in self.cap_neg_state:
+            if verb == 'LS':
+                self.caps.clear()
+            else:
+                for name in self.caps:
+                    self.caps[name] = (False, self.caps[name][1])
+            self.cap_neg_state.remove(doneness)
+        if waiting not in self.cap_neg_state:
+            self.cap_neg_state.add(waiting)
+        for item in items:
+            if verb == 'LS':
+                splitted = item.split('=', maxsplit=1)
+                self.caps[splitted[0]] = (False, ''.join(splitted[1:]))
+            else:
+                self.caps[item] = (True, self.caps[item][1])
+        if is_final_line:
+            self.cap_neg_state.remove(waiting)
+            self.cap_neg_state.add(doneness)
 
     @abstractmethod
     def log(self, msg: str, chat: str = '') -> None:
@@ -348,3 +350,14 @@ class _RecvEvent(ClientEvent, PayloadMixin):
         elif msg.verb == 'CAP':
             if msg.params[1] in {'LS', 'LIST'}:
                 target.collect_caps(msg.params[1:])
+            if ('LIST done' in target.cap_neg_state
+                    and 'listed' not in target.cap_neg_state):
+                target.send(IrcMessage(verb='CAP', params=('END',)))
+                target.log('# available server capabilities (enabled: "+"):')
+                for cap_name, config in target.caps.items():
+                    target.log(f'# {"+" if config[0] else "-"} {cap_name}'
+                               + (f' ({config[1]})' if config[1] else ''))
+                target.cap_neg_state.add('listed')
+            elif ('LS done' in target.cap_neg_state
+                    and 'LIST wait' not in target.cap_neg_state):
+                target.send(IrcMessage(verb='CAP', params=('LIST',)))