home · contact · privacy
Move server message expectations into their own file.
authorChristian Heller <c.heller@plomlompom.de>
Tue, 2 Sep 2025 05:14:08 +0000 (07:14 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Tue, 2 Sep 2025 05:14:08 +0000 (07:14 +0200)
ircplom/client.py
ircplom/msg_parse_expectations.py [new file with mode: 0644]

index bc10127f382b1ae8f9bff30cf7aef9495a88c11e..c0c352ea6a35adcac319f2c64cf33d1d6e47dd44 100644 (file)
@@ -6,7 +6,7 @@ from dataclasses import dataclass, InitVar
 from enum import Enum, auto
 from getpass import getuser
 from threading import Thread
-from typing import Any, Callable, Generic, NamedTuple, Optional, Self, TypeVar
+from typing import Any, Callable, Generic, Optional, Self, TypeVar
 from uuid import uuid4
 # ourselves
 from ircplom.events import (
@@ -14,6 +14,7 @@ from ircplom.events import (
 from ircplom.irc_conn import (
     BaseIrcConnection, IrcConnAbortException, IrcMessage,
     ILLEGAL_NICK_CHARS, ILLEGAL_NICK_FIRSTCHARS, ISUPPORT_DEFAULTS, PORT_SSL)
+from ircplom.msg_parse_expectations import MsgTok, MSG_EXPECTATIONS
 
 ClientsDb = dict[str, 'Client']
 
@@ -143,324 +144,6 @@ class ClientQueueMixin(QueueMixin, _ClientIdMixin):
 _NAMES_DESIRED_SERVER_CAPS = ('sasl',)
 
 
-class _MsgTok(Enum):
-    ANY = auto()
-    CHANNEL = auto()
-    LIST = auto()
-    NICKNAME = auto()
-    NONE = auto()
-    SERVER = auto()
-    NICK_USER_HOST = auto()
-
-
-_MsgTokGuide = str | _MsgTok | tuple[str | _MsgTok, str]
-
-
-class _MsgParseExpectation(NamedTuple):
-    source: _MsgTokGuide
-    verb: str
-    params: tuple[_MsgTokGuide, ...] = tuple()
-    idx_into_list: int = -1
-
-
-_EXPECTATIONS: list[_MsgParseExpectation] = []
-
-# these we ignore except for confirming/collecting the nickname
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '001',  # RPL_WELCOME
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '002',  # RPL_YOURHOST
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '003',  # RPL_CREATED
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '004',  # RPL_MYINFO
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY,
-                          _MsgTok.ANY,
-                          _MsgTok.ANY,
-                          _MsgTok.ANY,
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '250',  # RPL_STATSDLINE / RPL_STATSCONN
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '251',  # RPL_LUSERCLIENT
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '252',  # RPL_LUSEROP
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY,
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '253',  # RPL_LUSERUNKNOWN
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY,
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '254',  # RPL_LUSERCHANNELS
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY,
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '255',  # RPL_LUSERME
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '265',  # RPL_LOCALUSERS
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '265',  # RPL_LOCALUSERS
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY,
-                          _MsgTok.ANY,
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '266',  # RPL_GLOBALUSERS
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '266',  # RPL_GLOBALUSERS
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY,
-                          _MsgTok.ANY,
-                          _MsgTok.ANY)),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '375',  # RPL_MOTDSTART already implied by 1st 372
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),
-]
-
-# various login stuff
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '005',  # RPL_ISUPPORT
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, ':isupport'),
-                          _MsgTok.ANY),  # comment
-                         idx_into_list=1),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '372',  # RPL_MOTD
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, ':line'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '376',  # RPL_ENDOFMOTD
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '396',  # RPL_VISIBLEHOST
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.SERVER, 'set_me_attr:host'),
-                          _MsgTok.ANY)),  # comment
-]
-
-# SASL
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '900',  # RPL_LOGGEDIN
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.NICK_USER_HOST, 'set_me_attr:nickuserhost'),
-                          (_MsgTok.ANY, 'set_db_attr:sasl_account'),
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '903',  # RPL_SASLSUCCESS
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, 'set_db_attr:sasl_auth_state'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '904',  # ERR_SASLFAIL
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, 'set_db_attr:sasl_auth_state'))),
-    _MsgParseExpectation(_MsgTok.NONE,
-                         'AUTHENTICATE',
-                         ('+',)),
-]
-
-# capability negotation
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('NEW', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('DEL', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ('*',
-                          ('ACK', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('ACK', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ('*',
-                          ('NAK', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('NAK', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ('*',
-                          ('LS', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ('*',
-                          ('LS', ':subverb'),
-                          ('*', ':tbc'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('LS', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('LS', ':subverb'),
-                          ('*', ':tbc'),
-                          (_MsgTok.LIST, ':items'))),
-
-    _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, 'set_me_attr:nick'),
-                          ('LIST', ':subverb'),
-                          (_MsgTok.LIST, ':items'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'CAP',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          ('LIST', ':subverb'),
-                          ('*', ':tbc'),
-                          (_MsgTok.LIST, ':items'))),
-]
-
-# nickname management
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '432',  # ERR_ERRONEOUSNICKNAME
-                         ('*',
-                          _MsgTok.NICKNAME,  # no need to re-use the bad one
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '432',  # ERR_ERRONEOUSNICKNAME
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          _MsgTok.NICKNAME,  # no need to re-use the bad one
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '433',  # ERR_NICKNAMEINUSE
-                         (_MsgTok.NICKNAME,  # we rather go for incrementation
-                          (_MsgTok.NICKNAME, ':used'),
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, ':named'),
-                         'NICK',
-                         ((_MsgTok.NICKNAME, ':nick'),)),
-]
-
-# joining/leaving
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '353',  # RPL_NAMREPLY
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          '=',
-                          (_MsgTok.CHANNEL, ':channel'),
-                          (_MsgTok.LIST, ':names'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '366',  # RPL_ENDOFNAMES
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.CHANNEL, ':channel'),
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, ':joiner'),
-                         'JOIN',
-                         ((_MsgTok.CHANNEL, ':channel'),)),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, ':parter'),
-                         'PART',
-                         ((_MsgTok.CHANNEL, ':channel'),)),
-]
-
-# messaging
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         '401',  # ERR_NOSUCKNICK
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.NICKNAME, ':target'),
-                          _MsgTok.ANY)),  # comment
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'NOTICE',
-                         ('*',
-                          (_MsgTok.ANY, ':message'))),
-    _MsgParseExpectation(_MsgTok.SERVER,
-                         'NOTICE',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, ':message'))),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, 'set_user:sender'),
-                         'NOTICE',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, ':message'))),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, 'set_user:sender'),
-                         'NOTICE',
-                         ((_MsgTok.CHANNEL, ':channel'),
-                          (_MsgTok.ANY, ':message'))),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, 'set_user:sender'),
-                         'PRIVMSG',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, ':message'))),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, 'set_user:sender'),
-                         'PRIVMSG',
-                         ((_MsgTok.CHANNEL, ':channel'),
-                          (_MsgTok.ANY, ':message'))),
-]
-
-# misc.
-_EXPECTATIONS += [
-    _MsgParseExpectation(_MsgTok.NONE,
-                         'ERROR',
-                         ((_MsgTok.ANY, 'set_db_attr:connection_state'),)),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, 'set_me_attr:nickuserhost'),
-                         'MODE',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, 'set_db_attr:user_modes'))),
-    _MsgParseExpectation(_MsgTok.NICKNAME,
-                         'MODE',
-                         ((_MsgTok.NICKNAME, 'set_me_attr:nick'),
-                          (_MsgTok.ANY, 'set_db_attr:user_modes'))),
-    _MsgParseExpectation(_MsgTok.NONE,
-                         'PING',
-                         ((_MsgTok.ANY, ':reply'),)),
-    _MsgParseExpectation((_MsgTok.NICK_USER_HOST, ':quitter'),
-                         'QUIT',
-                         ((_MsgTok.ANY, ':message'),)),
-]
-
-
 def _nick_incremented(nickname: str) -> str:
     'Return nickname with number suffix incremented, or "0" if none.'
     name, digits = ([(nickname, '')]
@@ -944,7 +627,7 @@ class Client(ABC, ClientQueueMixin):
         tok_type = (str | _NickUserHost | tuple[str, ...]
                     | dict[str, str | _Channel])
 
-        def param_match(ex_tok: str | _MsgTok, msg_tok: str | list[str]
+        def param_match(ex_tok: str | MsgTok, msg_tok: str | list[str]
                         ) -> Optional[tok_type | tuple[tok_type, ...]]:
             if isinstance(msg_tok, list):
                 to_return = []
@@ -956,28 +639,28 @@ class Client(ABC, ClientQueueMixin):
                 return tuple(to_return)
             if isinstance(ex_tok, str):
                 return msg_tok if msg_tok == ex_tok else None
-            if ex_tok is _MsgTok.NONE:
+            if ex_tok is MsgTok.NONE:
                 return msg_tok if msg_tok == '' else None
-            if ex_tok is _MsgTok.SERVER:
+            if ex_tok is MsgTok.SERVER:
                 return msg_tok if ('.' in msg_tok
                                    and not set('@!') & set(msg_tok)) else None
-            if ex_tok is _MsgTok.CHANNEL:
+            if ex_tok is MsgTok.CHANNEL:
                 return {'id': msg_tok, 'db': self._db.channels[msg_tok]
                         } if self._db.is_chan_name(msg_tok) else None
-            if ex_tok is _MsgTok.NICKNAME:
+            if ex_tok is MsgTok.NICKNAME:
                 return (msg_tok
                         if msg_tok[0] not in self._db.illegal_nick_firstchars
                         else None)
-            if ex_tok is _MsgTok.NICK_USER_HOST:
+            if ex_tok is MsgTok.NICK_USER_HOST:
                 try:
                     return _NickUserHost.from_str(msg_tok)
                 except AssertionError:
                     return None
-            if ex_tok is _MsgTok.LIST:
+            if ex_tok is MsgTok.LIST:
                 return tuple(msg_tok.split())
             return msg_tok
 
-        for ex in [ex for ex in _EXPECTATIONS if ex.verb == msg.verb]:
+        for ex in [ex for ex in MSG_EXPECTATIONS if ex.verb == msg.verb]:
             tasks: dict[str, list[str]] = {}
             to_return: dict[str, Any] = {'verb': ex.verb, '_tasks': tasks}
             ex_tok_fields = tuple([ex.source] + list(ex.params))
diff --git a/ircplom/msg_parse_expectations.py b/ircplom/msg_parse_expectations.py
new file mode 100644 (file)
index 0000000..0f2e26a
--- /dev/null
@@ -0,0 +1,322 @@
+'Structured expectations and processing hints for server messages.'
+from enum import Enum, auto
+from typing import NamedTuple
+
+
+class MsgTok(Enum):
+    'Server message token classifications.'
+    ANY = auto()
+    CHANNEL = auto()
+    LIST = auto()
+    NICKNAME = auto()
+    NONE = auto()
+    SERVER = auto()
+    NICK_USER_HOST = auto()
+
+
+_MsgTokGuide = str | MsgTok | tuple[str | MsgTok, str]
+
+
+class _MsgParseExpectation(NamedTuple):
+    source: _MsgTokGuide
+    verb: str
+    params: tuple[_MsgTokGuide, ...] = tuple()
+    idx_into_list: int = -1
+
+
+MSG_EXPECTATIONS: list[_MsgParseExpectation] = []
+
+# these we ignore except for confirming/collecting the nickname
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '001',  # RPL_WELCOME
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '002',  # RPL_YOURHOST
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '003',  # RPL_CREATED
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '004',  # RPL_MYINFO
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY,
+                          MsgTok.ANY,
+                          MsgTok.ANY,
+                          MsgTok.ANY,
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '250',  # RPL_STATSDLINE / RPL_STATSCONN
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '251',  # RPL_LUSERCLIENT
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '252',  # RPL_LUSEROP
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY,
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '253',  # RPL_LUSERUNKNOWN
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY,
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '254',  # RPL_LUSERCHANNELS
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY,
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '255',  # RPL_LUSERME
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '265',  # RPL_LOCALUSERS
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '265',  # RPL_LOCALUSERS
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY,
+                          MsgTok.ANY,
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '266',  # RPL_GLOBALUSERS
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '266',  # RPL_GLOBALUSERS
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY,
+                          MsgTok.ANY,
+                          MsgTok.ANY)),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '375',  # RPL_MOTDSTART already implied by 1st 372
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),
+]
+
+# various login stuff
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '005',  # RPL_ISUPPORT
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, ':isupport'),
+                          MsgTok.ANY),  # comment
+                         idx_into_list=1),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '372',  # RPL_MOTD
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, ':line'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '376',  # RPL_ENDOFMOTD
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '396',  # RPL_VISIBLEHOST
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.SERVER, 'set_me_attr:host'),
+                          MsgTok.ANY)),  # comment
+]
+
+# SASL
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '900',  # RPL_LOGGEDIN
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.NICK_USER_HOST, 'set_me_attr:nickuserhost'),
+                          (MsgTok.ANY, 'set_db_attr:sasl_account'),
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '903',  # RPL_SASLSUCCESS
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, 'set_db_attr:sasl_auth_state'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '904',  # ERR_SASLFAIL
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, 'set_db_attr:sasl_auth_state'))),
+    _MsgParseExpectation(MsgTok.NONE,
+                         'AUTHENTICATE',
+                         ('+',)),
+]
+
+# capability negotation
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('NEW', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('DEL', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('ACK', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('ACK', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('NAK', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('NAK', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('LS', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ('*',
+                          ('LS', ':subverb'),
+                          ('*', ':tbc'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('LS', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('LS', ':subverb'),
+                          ('*', ':tbc'),
+                          (MsgTok.LIST, ':items'))),
+
+    _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, 'set_me_attr:nick'),
+                          ('LIST', ':subverb'),
+                          (MsgTok.LIST, ':items'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'CAP',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          ('LIST', ':subverb'),
+                          ('*', ':tbc'),
+                          (MsgTok.LIST, ':items'))),
+]
+
+# nickname management
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '432',  # ERR_ERRONEOUSNICKNAME
+                         ('*',
+                          MsgTok.NICKNAME,  # no need to re-use the bad one
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '432',  # ERR_ERRONEOUSNICKNAME
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          MsgTok.NICKNAME,  # no need to re-use the bad one
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '433',  # ERR_NICKNAMEINUSE
+                         (MsgTok.NICKNAME,  # we rather go for incrementation
+                          (MsgTok.NICKNAME, ':used'),
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, ':named'),
+                         'NICK',
+                         ((MsgTok.NICKNAME, ':nick'),)),
+]
+
+# joining/leaving
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '353',  # RPL_NAMREPLY
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          '=',
+                          (MsgTok.CHANNEL, ':channel'),
+                          (MsgTok.LIST, ':names'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '366',  # RPL_ENDOFNAMES
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.CHANNEL, ':channel'),
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, ':joiner'),
+                         'JOIN',
+                         ((MsgTok.CHANNEL, ':channel'),)),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, ':parter'),
+                         'PART',
+                         ((MsgTok.CHANNEL, ':channel'),)),
+]
+
+# messaging
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.SERVER,
+                         '401',  # ERR_NOSUCKNICK
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.NICKNAME, ':target'),
+                          MsgTok.ANY)),  # comment
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'NOTICE',
+                         ('*',
+                          (MsgTok.ANY, ':message'))),
+    _MsgParseExpectation(MsgTok.SERVER,
+                         'NOTICE',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, ':message'))),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, 'set_user:sender'),
+                         'NOTICE',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, ':message'))),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, 'set_user:sender'),
+                         'NOTICE',
+                         ((MsgTok.CHANNEL, ':channel'),
+                          (MsgTok.ANY, ':message'))),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, 'set_user:sender'),
+                         'PRIVMSG',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, ':message'))),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, 'set_user:sender'),
+                         'PRIVMSG',
+                         ((MsgTok.CHANNEL, ':channel'),
+                          (MsgTok.ANY, ':message'))),
+]
+
+# misc.
+MSG_EXPECTATIONS += [
+    _MsgParseExpectation(MsgTok.NONE,
+                         'ERROR',
+                         ((MsgTok.ANY, 'set_db_attr:connection_state'),)),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, 'set_me_attr:nickuserhost'),
+                         'MODE',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, 'set_db_attr:user_modes'))),
+    _MsgParseExpectation(MsgTok.NICKNAME,
+                         'MODE',
+                         ((MsgTok.NICKNAME, 'set_me_attr:nick'),
+                          (MsgTok.ANY, 'set_db_attr:user_modes'))),
+    _MsgParseExpectation(MsgTok.NONE,
+                         'PING',
+                         ((MsgTok.ANY, ':reply'),)),
+    _MsgParseExpectation((MsgTok.NICK_USER_HOST, ':quitter'),
+                         'QUIT',
+                         ((MsgTok.ANY, ':message'),)),
+]