home · contact · privacy
Add channel topic messages.
authorChristian Heller <c.heller@plomlompom.de>
Tue, 2 Sep 2025 13:30:35 +0000 (15:30 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Tue, 2 Sep 2025 13:30:35 +0000 (15:30 +0200)
ircplom/client.py
ircplom/client_tui.py
ircplom/msg_parse_expectations.py

index 6af09d7847b97d00322d76e5a7601d498a24a066..ce55b04c3f12ce6676df7e23a5cb46afc4c97624 100644 (file)
@@ -306,6 +306,7 @@ class _IrcConnection(BaseIrcConnection, _ClientIdMixin):
 
 class _Channel:
     user_ids: _CompletableStringsList
+    topic: tuple[str, str] = ('', '')
 
     def __init__(self,
                  get_id_for_nick: Callable,
@@ -689,6 +690,12 @@ class Client(ABC, ClientQueueMixin):
                 else:
                     key, data = _Dict.key_val_from_eq_str(item)
                     self._db.isupport[key] = data
+        elif ret['verb'] == '332':  # RPL_TOPIC
+            self._db.channels[ret['channel']].topic = (ret['topic'], '')
+        elif ret['verb'] == '333':  # RPL_TOPICWHOTIME
+            self._db.channels[ret['channel']].topic = (
+                    self._db.channels[ret['channel']].topic[0],
+                    str(ret['author']))
         elif ret['verb'] == '353':  # RPL_NAMREPLY
             self._db.channels[ret['channel']].add_from_namreply(ret['names'])
         elif ret['verb'] == '366':  # RPL_ENDOFNAMES
@@ -757,6 +764,9 @@ class Client(ABC, ClientQueueMixin):
                 ch.remove_nick(ret['quitter'])
                 self._log(f'{ret["quitter"]} quits: {ret["message"]}',
                           LogScope.CHAT, target=ch_name)
+        elif ret['verb'] == 'TOPIC':
+            self._db.channels[ret['channel']].topic = (ret['topic'],
+                                                       str(ret['author']))
 
 
 ClientsDb = dict[str, Client]
index b506d75b068fce1e95fb03641460bc180f87383d..d7085d86ac052a3f0be35d04a925a3f640199668 100644 (file)
@@ -191,24 +191,29 @@ class _ChannelWindow(_ChatWindow):
 
 class _UpdatingChannel(_UpdatingNode):
     user_ids: tuple[str, ...] = tuple()
+    topic: tuple[str, str] = ('', '')
     log_scopes = {tuple(): LogScope.CHAT}
 
     def set_and_check_for_change(self, update: _Update
                                  ) -> Optional[tuple[LogScope, Any]]:
+        assert isinstance(update.value, tuple)
+        msg: str | dict[str, tuple[str, ...]] = {}
         if update.path == ('user_ids',):
-            assert isinstance(update.value, tuple)
-            d: dict[str, tuple[str, ...]] = {}
+            msg = {}
             if not self.user_ids:
-                d['nicks:residents'] = tuple(update.value)
+                msg['nicks:residents'] = tuple(update.value)
             else:
-                d['nuhs:joining'] = tuple(id_ for id_ in update.value
-                                          if id_ not in self.user_ids)
-                d['nuhs:parting'] = tuple(id_ for id_ in self.user_ids
-                                          if id_ not in update.value)
-            if super().set_and_check_for_change(update):
-                return (self._scope(update.path), d)
-            return None
-        return super().set_and_check_for_change(update)
+                msg['nuhs:joining'] = tuple(id_ for id_ in update.value
+                                            if id_ not in self.user_ids)
+                msg['nuhs:parting'] = tuple(id_ for id_ in self.user_ids
+                                            if id_ not in update.value)
+        elif update.path == ('topic',):
+            if '' in update.value:
+                return None
+            msg = f'raw:topic set by {update.value[1]} to: {update.value[0]}'
+        if super().set_and_check_for_change(update):
+            return (self._scope(update.path), msg)
+        return None
 
 
 class _UpdatingNickUserHost(_UpdatingNode, NickUserHost):
@@ -306,6 +311,8 @@ class _ClientWindowsManager:
                         (str(nuh) if transform == 'nuhs' else nuh.nick)
                         for nuh in nuhs])
                 self.log(f'{verb}: {item}', **log_kwargs)
+        elif isinstance(value, str) and value.startswith('raw:'):
+            self.log(value.split(':', maxsplit=1)[1], **log_kwargs)
         else:
             announcement = f'{log_path} set to:'
             if isinstance(value, tuple):
index 55fb414714b77ec974bfe2e8c7f75bb666ccf00e..23991695c6bd93562afff6f1a6c1e5690c0d3c11 100644 (file)
@@ -253,6 +253,17 @@ MSG_EXPECTATIONS += [
 
 # joining/leaving
 MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '332',  # RPL_TOPIC
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.CHANNEL, ':channel'),
+                          (MsgTok.ANY, ':topic'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '333',  # RPL_TOPICWHOTIME
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.CHANNEL, ':channel'),
+                          (MsgTok.NICK_USER_HOST, ':author'),
+                          (MsgTok.ANY, ':timestamp'))),
     _MsgParseExpectation(MsgTok.SERVER,
                          '353',  # RPL_NAMREPLY
                          ((MsgTok.NICKNAME, 'set_me_attr:nick'),
@@ -276,6 +287,10 @@ MSG_EXPECTATIONS += [
     _MsgParseExpectation((MsgTok.NICK_USER_HOST, ':parter'),
                          'PART',
                          ((MsgTok.CHANNEL, ':channel'),)),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, ':author'),
+                         'TOPIC',
+                         ((MsgTok.CHANNEL, ':channel'),
+                          (MsgTok.ANY, ':topic'))),
 ]
 
 # messaging