home · contact · privacy
Re-organize message matching away from previous Numerics collections.
authorChristian Heller <c.heller@plomlompom.de>
Wed, 20 Aug 2025 23:17:20 +0000 (01:17 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Wed, 20 Aug 2025 23:17:20 +0000 (01:17 +0200)
ircplom/client.py

index 2d5bd3d8c3abb26a2b2b92d442e9080e3a7ea87d..44c7b0a5e715fde1d3bb61a0980cf726f9f66b03 100644 (file)
@@ -16,28 +16,6 @@ from ircplom.irc_conn import (BaseIrcConnection, IrcConnAbortException,
 ClientsDb = dict[str, 'Client']
 _NAMES_DESIRED_SERVER_CAPS = ('sasl',)
 
-# NB: in below numerics accounting, tuples define inclusive ranges
-_NUMERICS_TO_CONFIRM_NICKNAME = (
-    (1, 5),
-    (251, 255),
-    (265, 266),
-    353,
-    366,
-    372,
-    (375, 376),
-    396,
-    401,
-    900,
-    903,
-    904
-)
-_NUMERICS_TO_IGNORE = (
-    (1, 4),      # nothing in this login info we're interested in
-    (250, 255),  # same
-    (265, 266),  # same
-    375,  # RPL_MOTDSTART already implied to us by first appearance of 372
-)
-
 
 class _MsgTok(Enum):
     ANY = auto()
@@ -59,189 +37,306 @@ class _MsgParseExpectation(NamedTuple):
     idx_into_list: int = -1
 
 
-_EXPECTATIONS: tuple[_MsgParseExpectation, ...] = (
+_EXPECTATIONS: list[_MsgParseExpectation] = []
 
+# these we ignore except for confirming/collecting the nickname
+_EXPECTATIONS += [
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '005',
-                         (_MsgTok.NICKNAME,
-                          (_MsgTok.ANY, 'isupports'),
-                          _MsgTok.ANY),
-                         idx_into_list=1),
-
+                         '001',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '353',
-                         (_MsgTok.NICKNAME,
-                          '=',
-                          (_MsgTok.CHANNEL, 'channel'),
-                          (_MsgTok.LIST, 'names'))),
+                         '002',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '366',
-                         (_MsgTok.NICKNAME,
-                          (_MsgTok.CHANNEL, 'channel'),
+                         '003',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           _MsgTok.ANY)),
-
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '372',
-                         (_MsgTok.NICKNAME,
-                          (_MsgTok.ANY, 'line'))),
+                         '004',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY,
+                          _MsgTok.ANY,
+                          _MsgTok.ANY,
+                          _MsgTok.ANY,
+                          _MsgTok.ANY)),
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '376',
-                         (_MsgTok.NICKNAME,
+                         '250',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           _MsgTok.ANY)),
-
-    _MsgParseExpectation(_MsgTok.SERVER, '396',
-                         (_MsgTok.NICKNAME,
-                          (_MsgTok.ANY, 'host_maybe_w_user'),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '251',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           _MsgTok.ANY)),
-
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '401',
-                         (_MsgTok.NICKNAME,
-                          (_MsgTok.NICKNAME, 'target'),
+                         '252',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY,
                           _MsgTok.ANY)),
-
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '432',
-                         ('*',
-                          _MsgTok.NICKNAME,
+                         '253',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY,
                           _MsgTok.ANY)),
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '432',
-                         ((_MsgTok.NICKNAME, 'fallback'),
-                          _MsgTok.NICKNAME,
+                         '254',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY,
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '255',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '265',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '265',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY,
+                          _MsgTok.ANY,
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '266',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '266',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY,
+                          _MsgTok.ANY,
                           _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '375',  # RPL_MOTDSTART already implied by 1st 372
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
+]
 
+# various login stuff
+_EXPECTATIONS += [
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '433',
-                         (_MsgTok.NICKNAME,
-                          _MsgTok.NICKNAME,
+                         '005',  # RPL_ISUPPORT
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.ANY, 'isupports'),
+                          _MsgTok.ANY),
+                         idx_into_list=1),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '372',  # RPL_MOTD
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.ANY, 'line'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '376',  # RPL_ENDOFMOTD
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '396',  # RPL_VISIBLEHOST
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.ANY, 'host_maybe_w_user'),
                           _MsgTok.ANY)),
+]
 
+# SASL
+_EXPECTATIONS += [
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '900',
-                         (_MsgTok.NICKNAME,
+                         '900',  # RPL_LOGGEDIN
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           (_MsgTok.USER_ADDRESS, 'full_address'),
                           (_MsgTok.ANY, 'account'),
                           _MsgTok.ANY)),
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '903',
-                         (_MsgTok.NICKNAME,
+                         '903',  # RPL_SASLSUCCESS
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           (_MsgTok.ANY, 'result'))),
     _MsgParseExpectation(_MsgTok.SERVER,
-                         '904',
-                         (_MsgTok.NICKNAME,
+                         '904',  # ERR_SASLFAIL
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           (_MsgTok.ANY, 'result'))),
-
     _MsgParseExpectation(_MsgTok.NONE,
                          'AUTHENTICATE',
                          ('+',)),
+]
 
+# capability negotation
+_EXPECTATIONS += [
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
-                          ('NEW', 'verb'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('NEW', 'subverb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
-                          ('DEL', 'verb'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('DEL', 'subverb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
-                          ('ACK', 'verb'),
+                         ('*',
+                          ('ACK', 'subverb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
-                          ('NAK', 'verb'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('ACK', 'subverb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
                          ('*',
-                          ('LS', 'verb'),
-                          ('*', 'tbc'),
+                          ('NAK', 'subverb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
-                          ('LS', 'verb'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('NAK', 'subverb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
+                         ('*',
+                          ('LS', 'subverb'),
+                          (_MsgTok.LIST, 'items'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('LS', 'subverb'),
                           ('*', 'tbc'),
-                          ('LIST', 'verb'),
                           (_MsgTok.LIST, 'items'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'CAP',
-                         (_MsgTok.NICKNAME,
-                          ('LIST', 'verb'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('LS', 'subverb'),
+                          (_MsgTok.LIST, 'items'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         'CAP',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('LS', 'subverb'),
+                          ('*', 'tbc'),
                           (_MsgTok.LIST, 'items'))),
 
-    _MsgParseExpectation(_MsgTok.NONE,
-                         'ERROR',
-                         ((_MsgTok.ANY, 'reason'),)),
-
-    _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'joiner'),
-                         'JOIN',
-                         ((_MsgTok.CHANNEL, 'channel'),)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('LIST', 'subverb'),
+                          (_MsgTok.LIST, 'items'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('LIST', 'subverb'),
+                          ('*', 'tbc'),
+                          (_MsgTok.LIST, 'items'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         'CAP',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('LIST', 'subverb'),
+                          (_MsgTok.LIST, 'items'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         'CAP',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          ('LIST', 'subverb'),
+                          ('*', 'tbc'),
+                          (_MsgTok.LIST, 'items'))),
+]
 
-    _MsgParseExpectation(_MsgTok.NICKNAME,
-                         'MODE',
-                         (_MsgTok.NICKNAME,
-                          (_MsgTok.ANY, 'mode'))),
-    _MsgParseExpectation(_MsgTok.USER_ADDRESS,
-                         'MODE',
+# nickname management
+_EXPECTATIONS += [
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '432',  # ERR_ERRONEOUSNICKNAME
+                         ('*',
+                          _MsgTok.NICKNAME,
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '432',  # ERR_ERRONEOUSNICKNAME
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          _MsgTok.NICKNAME,
+                          _MsgTok.ANY)),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '433',  # ERR_NICKNAMEINUSE
                          (_MsgTok.NICKNAME,
-                          (_MsgTok.ANY, 'mode'))),
-
+                          _MsgTok.NICKNAME,
+                          _MsgTok.ANY)),
     _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'named'),
                          'NICK',
                          ((_MsgTok.NICKNAME, 'nickname'),)),
+]
 
+# joining/leaving
+_EXPECTATIONS += [
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '353',  # RPL_NAMREPLY
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          '=',
+                          (_MsgTok.CHANNEL, 'channel'),
+                          (_MsgTok.LIST, 'names'))),
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '366',  # RPL_ENDOFNAMES
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.CHANNEL, 'channel'),
+                          _MsgTok.ANY)),
+    _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'joiner'),
+                         'JOIN',
+                         ((_MsgTok.CHANNEL, 'channel'),)),
+    _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'),
+                         'PART',
+                         ((_MsgTok.CHANNEL, 'channel'),)),
+    _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'),
+                         'PART',
+                         ((_MsgTok.CHANNEL, 'channel'),
+                          (_MsgTok.ANY, 'reason'))),
+]
+
+# messaging
+_EXPECTATIONS += [
+    _MsgParseExpectation(_MsgTok.SERVER,
+                         '401',  # ERR_NOSUCKNICK
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.NICKNAME, 'target'),
+                          _MsgTok.ANY)),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'NOTICE',
                          ('*',
                           (_MsgTok.ANY, 'message'))),
     _MsgParseExpectation(_MsgTok.SERVER,
                          'NOTICE',
-                         ((_MsgTok.NICKNAME, 'nickname'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           (_MsgTok.ANY, 'message'))),
     _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'),
                          'NOTICE',
-                         ((_MsgTok.NICKNAME, 'nickname'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           (_MsgTok.ANY, 'message'))),
     _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'),
                          'NOTICE',
                          ((_MsgTok.CHANNEL, 'channel'),
                           (_MsgTok.ANY, 'message'))),
-
-    _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'),
-                         'PART',
-                         ((_MsgTok.CHANNEL, 'channel'),)),
-    _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'),
-                         'PART',
-                         ((_MsgTok.CHANNEL, 'channel'),
-                          (_MsgTok.ANY, 'reason'))),
-
-    _MsgParseExpectation(_MsgTok.NONE,
-                         'PING',
-                         ((_MsgTok.ANY, 'reply'),)),
-
     _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'),
                          'PRIVMSG',
-                         ((_MsgTok.NICKNAME, 'nickname'),
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
                           (_MsgTok.ANY, 'message'))),
     _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'),
                          'PRIVMSG',
                          ((_MsgTok.CHANNEL, 'channel'),
                           (_MsgTok.ANY, 'message'))),
+]
 
+# misc.
+_EXPECTATIONS += [
+    _MsgParseExpectation(_MsgTok.NONE,
+                         'ERROR',
+                         ((_MsgTok.ANY, 'reason'),)),
+    _MsgParseExpectation(_MsgTok.NICKNAME,
+                         'MODE',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.ANY, 'mode'))),
+    _MsgParseExpectation(_MsgTok.USER_ADDRESS,
+                         'MODE',
+                         ((_MsgTok.NICKNAME, 'my_nickname'),
+                          (_MsgTok.ANY, 'mode'))),
+    _MsgParseExpectation(_MsgTok.NONE,
+                         'PING',
+                         ((_MsgTok.ANY, 'reply'),)),
     _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'quitter'),
                          'QUIT',
                          ((_MsgTok.ANY, 'message'),)),
-)
+]
 
 
 class LogScope(Enum):
@@ -253,24 +348,6 @@ class LogScope(Enum):
     SAME = auto()
 
 
-class _Numerics:
-
-    def __init__(self, numerics: tuple[int | tuple[int, int], ...]) -> None:
-        as_ints = []
-        for item in numerics:
-            as_ints += ([item] if isinstance(item, int)
-                        else list(range(item[0], item[1] + 1)))
-        self._numerics = tuple(as_ints)
-
-    def contain(self, msg_verb: str) -> bool:
-        'Is int(msg_verb) within the listed numbers?'
-        return msg_verb.isdigit() and int(msg_verb) in self._numerics
-
-
-_NumericsToIgnore = _Numerics(_NUMERICS_TO_IGNORE)
-_NumericsToConfirmNickname = _Numerics(_NUMERICS_TO_CONFIRM_NICKNAME)
-
-
 @dataclass
 class ClientIdMixin:
     'Collects a Client\'s ID at .client_id.'
@@ -709,7 +786,7 @@ class Client(ABC, ClientQueueMixin):
         self._log(f'connection broken: {e}', alert=True)
         self.close()
 
-    def _match_msg(self, msg: IrcMessage, verb: str):
+    def _match_msg(self, msg: IrcMessage) -> dict[str, Any]:
         'Test .source, .verb, .params.'
         tok_type = str | tuple[str, ...] | dict[str, str | _ChannelDb]
 
@@ -743,8 +820,8 @@ class Client(ABC, ClientQueueMixin):
                 return tuple(msg_tok.split())
             return msg_tok
 
-        for ex in [ex for ex in _EXPECTATIONS if verb == ex.verb == msg.verb]:
-            to_return: dict[str, Any] = {'': ''}  # non-emtpy so boolish True
+        for ex in [ex for ex in _EXPECTATIONS if ex.verb == msg.verb]:
+            to_return: dict[str, Any] = {'verb': ex.verb}
             ex_tok_fields = tuple([ex.source] + list(ex.params))
             msg_params: list[str | list[str]]
             if ex.idx_into_list < 0:
@@ -758,27 +835,27 @@ class Client(ABC, ClientQueueMixin):
             msg_tok_fields = tuple([msg.source] + msg_params)
             if ex.params and len(ex_tok_fields) != len(msg_tok_fields):
                 continue
-            passing = False
+            passing = True
             for idx, ex_tok in enumerate(ex_tok_fields):
                 ex_tok, key = ((ex_tok[0], ex_tok[1])
                                if isinstance(ex_tok, tuple) else (ex_tok, ''))
                 to_return[key] = param_match(ex_tok, msg_tok_fields[idx])
                 if to_return[key] is None:
+                    passing = False
                     break
-                passing = True
             if passing:
                 return to_return
-        return False
+        return {}
 
     def handle_msg(self, msg: IrcMessage) -> None:
         '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):
-            self.set_nick(msg.params[0], confirmed=True)
-        if _NumericsToIgnore.contain(msg.verb):
-            return
-
-        if (ret := self._match_msg(msg, '005')):  # RPL_ISUPPORT
+        ret = self._match_msg(msg)
+        if 'my_nickname' in ret:
+            self.set_nick(ret['my_nickname'], confirmed=True)
+        if 'verb' not in ret:
+            self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}')
+        elif ret['verb'] == '005':   # RPL_ISUPPORT
             for item in ret['isupports']:
                 toks = item.split('=', maxsplit=1)
                 if toks[0][0] == '-':
@@ -786,59 +863,48 @@ class Client(ABC, ClientQueueMixin):
                 else:
                     self._db.isupports[toks[0]] = (toks[1] if len(toks) > 1
                                                    else '')
-
-        elif (ret := self._match_msg(msg, '353')):  # RPL_NAMREPLY
+        elif ret['verb'] == '353':  # RPL_NAMREPLY
             for name in ret['names']:
                 ret['channel']['db'].append_completable('users',
                                                         name.lstrip('~&@%+'))
-        elif (ret := self._match_msg(msg, '366')):  # RPL_ENDOFNAMES
+        elif ret['verb'] == '366':  # RPL_ENDOFNAMES
             ret['channel']['db'].declare_complete('users')
-
-        elif (ret := self._match_msg(msg, '372')):  # RPL_MOTD
+        elif ret['verb'] == '372':  # RPL_MOTD
             self._db.append_completable('motd', ret['line'])
-        elif self._match_msg(msg, '376'):  # RPL_ENDOFMOTD
+        elif ret['verb'] == '376':  # RPL_ENDOFMOTD
             self._db.declare_complete('motd')
-
-        elif (ret := self._match_msg(msg, '396')):  # RPL_VISIBLEHOST
+        elif ret['verb'] == '396':  # RPL_VISIBLEHOST
             # '@'-split because <https://defs.ircdocs.horse/defs/numerics>
             # claims: "<hostname> can also be in the form <user@hostname>"
             self._db.client_host = ret['host_maybe_w_user'].split('@')[-1]
-
-        elif (ret := self._match_msg(msg, '401')):  # ERR_NOSUCHNICK
+        elif ret['verb'] == '401':  # ERR_NOSUCHNICK
             self._log(f'{ret["target"]} not online', scope=LogScope.CHAT,
                       target=ret['target'], alert=True)
-
-        elif (ret := self._match_msg(msg, '432')):  # ERR_ERRONEOUSNICKNAME
+        elif ret['verb'] == '432':  # ERR_ERRONEOUSNICKNAME
             alert = 'nickname refused for bad format'
-            if 'fallback' in ret:
-                self.set_nick(ret['fallback'], confirmed=True)
-            else:
+            if 'my_nickname' not in ret:
                 alert += ', giving up'
                 self.close()
             self._log(alert, alert=True)
-
-        elif self._match_msg(msg, '433'):  # ERR_NICKNAMEINUSE
+        elif ret['verb'] == '433':  # ERR_NICKNAMEINUSE
             self._log('nickname already in use, trying increment', alert=True)
             self.set_nick(self._db.nick_incremented)
-
-        elif (ret := self._match_msg(msg, '900')):  # RPL_LOGGEDIN
+        elif ret['verb'] == '900':  # RPL_LOGGEDIN
             self.set_nick(ret['full_address'][0], True)
             self._db.username, self._db.client_host = ret['full_address'][1:]
             self._db.sasl_account = ret['account']
-        elif ((ret := self._match_msg(msg, '903'))  # RPL_SASLSUCCESS
-                or (ret := self._match_msg(msg, '904'))):  # ERR_SASLFAIL
+        elif ret['verb'] in {'903', '904'}:  # RPL_SASLSUCCESS, ERR_SASLFAIL
             self._db.sasl_auth_state = ret['result']
             self._caps.end_negotiation()
-
-        elif self._match_msg(msg, 'AUTHENTICATE'):
+        elif ret['verb'] == 'AUTHENTICATE':
             auth = b64encode((self._db.nick_wanted + '\0'
                               + self._db.nick_wanted + '\0'
                               + self._db.password
                               ).encode('utf-8')).decode('utf-8')
             self.send(IrcMessage('AUTHENTICATE', (auth,)))
-
-        elif (ret := self._match_msg(msg, 'CAP')):
-            if (self._caps.process_msg(ret['verb'], ret['items'], 'tbc' in ret)
+        elif ret['verb'] == 'CAP':
+            if (self._caps.process_msg(verb=ret['subverb'], items=ret['items'],
+                                       incomplete='tbc' in ret)
                     and self._db.caps.has('sasl')
                     and 'PLAIN' in self._db.caps['sasl'].data.split(',')):
                 if self._db.password:
@@ -846,23 +912,19 @@ class Client(ABC, ClientQueueMixin):
                     self.send(IrcMessage('AUTHENTICATE', ('PLAIN',)))
                 else:
                     self._caps.end_negotiation()
-
-        elif (ret := self._match_msg(msg, 'ERROR')):
+        elif ret['verb'] == 'ERROR':
             self._db.connection_state = ret['reason']
             self.close()
-
-        elif (ret := self._match_msg(msg, 'JOIN')):
+        elif ret['verb'] == 'JOIN':
             self._log(f'{ret["joiner"][0]} {msg.verb.lower()}s '
                       + f'{ret["channel"]["id"]}',
                       scope=LogScope.CHAT, target=ret['channel']['id'])
             if ret['joiner'][0] != self._db.nickname:
                 ret['channel']['db'].append_completable('users',
                                                         ret['joiner'][0], True)
-
-        elif (ret := self._match_msg(msg, 'MODE')):
+        elif ret['verb'] == 'MODE':
             self._db.user_modes = ret['mode']
-
-        elif (ret := self._match_msg(msg, 'NICK')):
+        elif ret['verb'] == 'NICK':
             if ret['named'][0] == self._db.nickname:
                 self.set_nick(ret['nickname'], confirmed=True)
             else:
@@ -871,20 +933,15 @@ class Client(ABC, ClientQueueMixin):
                     ch.append_completable('users', ret['nickname'], True)
                     self._log(f'{ret["named"][0]} becomes {ret["nickname"]}',
                               scope=LogScope.CHAT, target=id_)
-
-        elif (ret := self._match_msg(msg, 'NOTICE'))\
-                or (ret := self._match_msg(msg, 'PRIVMSG')):
-            if 'nickname' in ret:
-                self.set_nick(ret['nickname'], confirmed=True)
+        elif ret['verb'] in {'NOTICE', 'PRIVMSG'}:
             kw: dict[str, bool | str | LogScope] = {
                     'as_notice': msg.verb == 'NOTICE'}
             if 'sender' in ret:  # not just server message
                 kw |= {'sender': ret['sender'][0], 'scope': LogScope.CHAT,
-                       'target': (ret['sender'][0] if 'nickname' in ret
+                       'target': (ret['sender'][0] if 'my_nickname' in ret
                                   else ret['channel']['id'])}
             self._log(ret['message'], out=False, **kw)
-
-        elif (ret := self._match_msg(msg, 'PART')):
+        elif ret['verb'] == 'PART':
             reason = f': {ret["reason"]}' if 'reason' in ret else ''
             self._log(f'{ret["parter"][0]} {msg.verb.lower()}s '
                       + f'{ret["channel"]["id"]}{reason}',
@@ -894,19 +951,14 @@ class Client(ABC, ClientQueueMixin):
             else:
                 ret['channel']['db'].remove_completable('users',
                                                         ret['parter'][0], True)
-
-        elif (ret := self._match_msg(msg, 'PING')):
+        elif ret['verb'] == 'PING':
             self.send(IrcMessage(verb='PONG', params=(ret['reply'],)))
-
-        elif (ret := self._match_msg(msg, 'QUIT')):
+        elif ret['verb'] == 'QUIT':
             for id_, chan in self._db.chans_of_user(ret['quitter'][0]).items():
                 chan.remove_completable('users', ret['quitter'][0], True)
                 self._log(f'{ret["quitter"][0]} quits: {ret["message"]}',
                           LogScope.CHAT, target=id_)
 
-        else:
-            self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}')
-
     def set_nick(self, nickname: str, confirmed=False) -> None:
         'Set ClientDb\'s .nick wanted, send NICK if != .nickname.'
         if confirmed: