home · contact · privacy
Start moving params parsing out of .handle_msg.
authorChristian Heller <c.heller@plomlompom.de>
Tue, 19 Aug 2025 06:22:24 +0000 (08:22 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Tue, 19 Aug 2025 06:22:24 +0000 (08:22 +0200)
ircplom/client.py

index df36cac46cf33c5af25b3028f6d2c21b24570223..7736ddf445a632a655523faa7156b15d4a1cbaa2 100644 (file)
@@ -45,11 +45,16 @@ class _MsgSource(Enum):
     SERVER = auto()
 
 
+class _MsgParam(Enum):
+    ANY = auto()
+    CHANNEL = auto()
+
+
 class _MsgParseExpectation(NamedTuple):
     verb: str
     len_min_params: int = 0
     len_max_params: int = 0
-    params: tuple[str, ...] = tuple()
+    params: tuple[str | _MsgParam, ...] = tuple()
     source: Optional[_MsgSource] = None
 
 
@@ -68,17 +73,24 @@ _EXPECTATIONS: tuple[_MsgParseExpectation, ...] = (
    _MsgParseExpectation('904', 2, source=_MsgSource.SERVER),
    _MsgParseExpectation('AUTHENTICATE', params=('+',), source=_MsgSource.NONE),
    _MsgParseExpectation('CAP', 3, 15, source=_MsgSource.SERVER),
-   _MsgParseExpectation('ERROR', 1, source=_MsgSource.NONE),
-   _MsgParseExpectation('JOIN', 1, source=_MsgSource.USER_ADDRESS),
+   _MsgParseExpectation('ERROR', params=(_MsgParam.ANY,),
+                        source=_MsgSource.NONE),
+   _MsgParseExpectation('JOIN', params=(_MsgParam.CHANNEL,),
+                        source=_MsgSource.USER_ADDRESS),
    _MsgParseExpectation('MODE', 2, source=_MsgSource.USER_ADDRESS),
    _MsgParseExpectation('NICK', 1, source=_MsgSource.USER_ADDRESS),
    _MsgParseExpectation('NOTICE', 2, source=_MsgSource.USER_ADDRESS),
    _MsgParseExpectation('NOTICE', 2, source=_MsgSource.SERVER),
-   _MsgParseExpectation('PART', 1, 2, source=_MsgSource.USER_ADDRESS),
-   _MsgParseExpectation('PING', 1, source=_MsgSource.NONE),
+   _MsgParseExpectation('PART', params=(_MsgParam.CHANNEL,),
+                        source=_MsgSource.USER_ADDRESS),
+   _MsgParseExpectation('PART', params=(_MsgParam.CHANNEL, _MsgParam.ANY),
+                        source=_MsgSource.USER_ADDRESS),
+   _MsgParseExpectation('PING', params=(_MsgParam.ANY,),
+                        source=_MsgSource.NONE),
    _MsgParseExpectation('PRIVMSG', 2, source=_MsgSource.USER_ADDRESS),
    _MsgParseExpectation('PRIVMSG', 2, source=_MsgSource.SERVER),
-   _MsgParseExpectation('QUIT', 1, source=_MsgSource.USER_ADDRESS),
+   _MsgParseExpectation('QUIT', params=(_MsgParam.ANY,),
+                        source=_MsgSource.USER_ADDRESS),
 )
 
 
@@ -564,10 +576,8 @@ class Client(ABC, ClientQueueMixin):
 
     def _match_msg(self, msg: _IrcMsg, verb: str):
         'Test .source, .verb, .params.'
-        to_return: dict[str, Optional[str]] = {'_': None}
-        if not msg.verb == verb:
-            return False
-        for expect in [x for x in _EXPECTATIONS if x.verb == msg.verb]:
+        for expect in [x for x in _EXPECTATIONS if verb == x.verb == msg.verb]:
+            to_return: dict[str, Any] = {'params': []}
             if expect.source is _MsgSource.NONE:
                 if msg.source != '':
                     continue
@@ -586,10 +596,22 @@ class Client(ABC, ClientQueueMixin):
                 if len(toks) != 3:
                     continue
                 to_return['nickname'] = toks[0]
-            if expect.params and msg.params != expect.params:
-                continue
             n_len_params = len(msg.params)
-            if expect.len_max_params:
+            if expect.params:
+                if n_len_params != len(expect.params):
+                    continue
+                for idx, exp_param in enumerate(expect.params):
+                    param = msg.params[idx]
+                    if isinstance(exp_param, str) and exp_param != param:
+                        continue
+                    if exp_param is _MsgParam.CHANNEL:
+                        if param[0] != '#':
+                            continue
+                        to_return['ch_name'] = param
+                        to_return['channel'] = self._db.chan(param)
+                    if exp_param is _MsgParam.ANY:
+                        to_return['params'] += [param]
+            elif expect.len_max_params:
                 if not (expect.len_min_params <= n_len_params
                         <= expect.len_max_params):
                     continue
@@ -662,12 +684,11 @@ class Client(ABC, ClientQueueMixin):
         elif self._match_msg(msg, 'ERROR'):
             self.close()
         elif (ret := self._match_msg(msg, 'JOIN')):
-            channel = msg.params[0]
-            log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {channel}'
-            self._log(log_msg, scope=LogScope.CHAT, target=channel)
+            log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {ret["ch_name"]}'
+            self._log(log_msg, scope=LogScope.CHAT, target=ret['ch_name'])
             if ret['nickname'] != self._db.nickname:
-                self._db.chan(channel).append_completable(
-                        'users', ret['nickname'], stay_complete=True)
+                ret['channel'].append_completable('users', ret['nickname'],
+                                                  stay_complete=True)
         elif self._match_msg(msg, 'MODE')\
                 and msg.params[0] == self._db.nickname:
             self._db.user_modes = msg.params[1]
@@ -685,25 +706,24 @@ class Client(ABC, ClientQueueMixin):
                   } if 'nickname' in ret else {}
             self._log(msg.params[-1], out=False, target=msg.params[0], **kw)
         elif (ret := self._match_msg(msg, 'PART')):
-            channel = msg.params[0]
-            log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {channel}'
-            if len(msg.params) == 2:
-                log_msg += f': {msg.params[1]}'
-            self._log(log_msg, scope=LogScope.CHAT, target=channel)
+            log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {ret["ch_name"]}'
+            if len(ret['params']):
+                log_msg += f': {ret["params"][0]}'
+            self._log(log_msg, scope=LogScope.CHAT, target=ret['ch_name'])
             if ret['nickname'] == self._db.nickname:
-                self._db.del_chan(channel)
+                self._db.del_chan(ret['ch_name'])
             else:
-                self._db.chan(channel).remove_completable(
-                        'users', ret['nickname'], stay_complete=True)
-        elif self._match_msg(msg, 'PING'):
-            self.send(IrcMessage(verb='PONG', params=(msg.params[0],)))
+                ret['channel'].remove_completable('users', ret['nickname'],
+                                                  stay_complete=True)
+        elif (ret := self._match_msg(msg, 'PING')):
+            self.send(IrcMessage(verb='PONG', params=tuple(ret['params'])))
         elif (ret := self._match_msg(msg, 'QUIT')):
             for chan_name in self._db.chan_names:
                 chan = self._db.chan(chan_name)
                 if ret['nickname'] in chan.users:
                     chan.remove_completable('users', ret['nickname'],
                                             stay_complete=True)
-                    self._log(f'{ret["nickname"]} quits: {msg.params[0]}',
+                    self._log(f'{ret["nickname"]} quits: {ret["params"][0]}',
                               scope=LogScope.CHAT, target=chan_name)
         else:
             self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}')