home · contact · privacy
Move NOTICE/PRIVMSG processing into msg_parse_expectations/client:ChatMessage.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 22 Sep 2025 02:47:49 +0000 (04:47 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 22 Sep 2025 02:47:49 +0000 (04:47 +0200)
ircplom/client.py
ircplom/client_tui.py
ircplom/msg_parse_expectations.py
test.txt

index 75e375f59351758af32d052957d7d60b5266881f..e79399a1fd3ebec5ea581251daac3e5f6b354263 100644 (file)
@@ -274,6 +274,21 @@ class IrcConnSetup:
     password: str = ''
 
 
+class ChatMessage:
+    'Collects all we want to know on incoming PRIVMSG or NOTICE chat message.'
+    content: str = ''
+    sender: str = ''
+    target: str = ''
+    is_notice: bool = False
+
+    def __str__(self) -> str:
+        return f'{"N" if self.is_notice else "P"} '\
+                + f'{self.sender} {self.target} :{self.content}'
+
+    def __bool__(self) -> bool:
+        return bool(self.content + self.sender + self.target) | self.is_notice
+
+
 class SharedClientDbFields(IrcConnSetup):
     'API for fields shared directly in name and type with TUI.'
     connection_state: str = ''
@@ -281,6 +296,7 @@ class SharedClientDbFields(IrcConnSetup):
     motd: Iterable[str]
     sasl_account: str = ''
     sasl_auth_state: str = ''
+    message: ChatMessage = ChatMessage()
 
     def is_chan_name(self, name: str) -> bool:
         'Tests name to match CHANTYPES prefixes.'
@@ -418,6 +434,30 @@ class _Channel(Channel):
         self.purge_users()
 
 
+class _ChatMessage(ChatMessage):
+
+    def __init__(self, sender: str = '', db: Optional['_ClientDb'] = None
+                 ) -> None:
+        self.sender = sender
+        self._db = db
+
+    def to(self, target: str) -> Self:
+        'Extend self with .target (empty if input as "me"), return self.'
+        self.target = '' if target == 'me' else target
+        return self
+
+    def __setattr__(self, key: str, value: str) -> None:
+        if key in {'privmsg', 'notice'}:
+            assert self._db is not None
+            self.is_notice = key == 'notice'
+            self.content = value
+            self._db.message = self
+            # to clean update cache, enabling equal messages in direct sequence
+            self._db.message = ChatMessage()
+        else:
+            super().__setattr__(key, value)
+
+
 class _SetNickuserhostMixin:
 
     def __setattr__(self, key: str, value: NickUserHost | str) -> None:
@@ -629,6 +669,11 @@ class _ClientDb(_Clearable, _UpdatingAttrsMixin, SharedClientDbFields):
             attr._create_if_none = {}
         return attr
 
+    def messaging(self, src: str | NickUserHost) -> ChatMessage:
+        'Start input chain for chat message data.'
+        return _ChatMessage(sender=f':{src}' if isinstance(src, str)
+                            else src.nick, db=self)
+
     def into_endnode_updates(self, path: tuple[str, ...]
                              ) -> list[tuple[tuple[str, ...], Any]]:
         'Return path-value pairs for update-worthy (sub-)elements at path.'
@@ -863,7 +908,8 @@ class Client(ABC, ClientQueueMixin):
                 for step in task.path:
                     key = ret[step] if step.isupper() else step
                     node = (node[key] if isinstance(node, Dict)
-                            else getattr(node, key))
+                            else (node(key) if callable(node)
+                                  else getattr(node, key)))
                 for tok_name in tok_names:
                     if task.verb == 'setattr':
                         setattr(node, tok_name, ret[tok_name])
@@ -917,13 +963,6 @@ class Client(ABC, ClientQueueMixin):
             self.db.users[user_id].nick = ret['nick']
             if user_id == 'me':
                 self.db.nick_wanted = ret['nick']
-        elif ret['_verb'] in {'NOTICE', 'PRIVMSG'}:
-            kw: dict[str, str | bool] = {'as_notice': ret['_verb'] == 'NOTICE'}
-            if (scope := LogScope.CHAT if 'sender' in ret else None):
-                kw['them'] = ret['sender'].nick
-                kw['log_target'] = (kw['them'] if 'nick' in ret
-                                    else ret['channel'])
-            self._log(ret['message'], out=False, scope=scope, **kw)
         elif ret['_verb'] == 'PART':
             ret['parter'].part(ret['channel'], ret.get('message', ''))
             if ret['parter'] is self.db.users['me']:
index 4e72f684d32b8e4358281bfd791eca3f2228539f..15616205a013e2b7a593d38a4369f64604183968 100644 (file)
@@ -6,8 +6,8 @@ from typing import Any, Callable, Optional, Sequence
 from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window,
                               CMD_SHORTCUTS)
 from ircplom.client import (
-        AutoAttrMixin, Channel, Client, ClientQueueMixin, Dict, DictItem,
-        ImplementationFail, IrcConnSetup, LogScope, NewClientEvent,
+        AutoAttrMixin, Channel, ChatMessage, Client, ClientQueueMixin, Dict,
+        DictItem, ImplementationFail, IrcConnSetup, LogScope, NewClientEvent,
         NickUserHost, SendFail, ServerCapability, SharedClientDbFields, User)
 from ircplom.irc_conn import IrcMessage
 
@@ -285,6 +285,9 @@ class _TuiClientDb(_UpdatingNode, SharedClientDbFields):
                 update.results += [(LogScope.ALL, [':CONNECTED'])]
             elif not update.value:
                 update.results += [(LogScope.ALL, [':DISCONNECTED'])]
+        elif update.key == 'message' and update.value:
+            assert isinstance(update.value, ChatMessage)
+            update.results += [(LogScope.CHAT, [':' + update.value.content])]
 
 
 class _ClientWindowsManager:
@@ -360,7 +363,14 @@ class _ClientWindowsManager:
             log_kwargs: dict[str, Any] = {'scope': scope}
             if scope in {LogScope.CHAT, LogScope.USER,
                          LogScope.USER_NO_CHANNELS}:
-                log_kwargs |= {'log_target': update.full_path[1]}
+                if update.full_path == ('message',):
+                    log_kwargs['log_target'] = (update.value.target
+                                                or update.value.sender)
+                    log_kwargs['them'] = update.value.sender
+                    log_kwargs['as_notice'] = update.value.is_notice
+                    log_kwargs['out'] = False
+                else:
+                    log_kwargs['log_target'] = update.full_path[1]
             if isinstance(result, list):
                 msg = ''
                 for item in result:
@@ -376,12 +386,12 @@ class _ClientWindowsManager:
                     self.log(f'{log_path} cleared', **log_kwargs)
                 else:
                     announcement = f'{log_path} set to:'
-                    if isinstance(result, tuple):
+                    if isinstance(result, ChatMessage) or not isinstance(result, tuple):
+                        self.log(f'{announcement} [{result}]', **log_kwargs)
+                    else:
                         self.log(announcement, **log_kwargs)
                         for item in result:
                             self.log(f'  {item}', **log_kwargs)
-                    else:
-                        self.log(f'{announcement} [{result}]', **log_kwargs)
         for win in [w for w in self.windows if isinstance(w, _ChatWindow)]:
             win.set_prompt_prefix()
         return bool([w for w in self.windows if w.tainted])
@@ -404,10 +414,7 @@ class ClientTui(BaseTui):
             if scope == LogScope.DEBUG:
                 return [m.window(LogScope.DEBUG), m.window(LogScope.RAW)]
             if scope == LogScope.CHAT:
-                chatname = (kwargs['log_target']
-                            if kwargs['log_target'] != m.db.users['me'].nick
-                            else kwargs['them'])
-                return [m.window(LogScope.CHAT, chatname=chatname)]
+                return [m.window(LogScope.CHAT, chatname=kwargs['log_target'])]
             if scope == LogScope.USER:
                 return m.windows_for_userid(kwargs['log_target'])
             if scope == LogScope.USER_NO_CHANNELS:
index bf8c3f6102d72f61c28cbe0722cd8c35c0922496..dd39bd7b75e105a537e7c622e0547de239e7432c 100644 (file)
@@ -491,33 +491,35 @@ MSG_EXPECTATIONS: list[_MsgParseExpectation] = [
         'NOTICE',
         _MsgTok.SERVER,
         ('*',
-         (_MsgTok.ANY, ':message'))),
+         (_MsgTok.ANY, 'setattr_db.messaging.server.to.me:notice'))),
     _MsgParseExpectation(
         'NOTICE',
         _MsgTok.SERVER,
         ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
-         (_MsgTok.ANY, ':message'))),
+         (_MsgTok.ANY, 'setattr_db.messaging.server.to.me:notice'))),
+
     _MsgParseExpectation(
         'NOTICE',
-        (_MsgTok.NICK_USER_HOST, ':sender'),
+        (_MsgTok.NICK_USER_HOST, ':USER'),
         ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
-         (_MsgTok.ANY, ':message'))),
+         (_MsgTok.ANY, 'setattr_db.messaging.USER.to.me:notice'))),
+
     _MsgParseExpectation(
         'NOTICE',
-        (_MsgTok.NICK_USER_HOST, ':sender'),
-        ((_MsgTok.CHANNEL, ':channel'),
-         (_MsgTok.ANY, ':message'))),
+        (_MsgTok.NICK_USER_HOST, ':USER'),
+        ((_MsgTok.CHANNEL, ':CHANNEL'),
+         (_MsgTok.ANY, 'setattr_db.messaging.USER.to.CHANNEL:notice'))),
 
     _MsgParseExpectation(
         'PRIVMSG',
-        (_MsgTok.NICK_USER_HOST, ':sender'),
+        (_MsgTok.NICK_USER_HOST, ':USER'),
         ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
-         (_MsgTok.ANY, ':message'))),
+         (_MsgTok.ANY, 'setattr_db.messaging.USER.to.me:privmsg'))),
     _MsgParseExpectation(
         'PRIVMSG',
-        (_MsgTok.NICK_USER_HOST, ':sender'),
-        ((_MsgTok.CHANNEL, ':channel'),
-         (_MsgTok.ANY, ':message'))),
+        (_MsgTok.NICK_USER_HOST, ':USER'),
+        ((_MsgTok.CHANNEL, ':CHANNEL'),
+         (_MsgTok.ANY, 'setattr_db.messaging.USER.to.CHANNEL:privmsg'))),
 
     # misc.
 
index 583407ba6a4ea818135e376a1f2a96e031985c14..0ab00d395181e7188aeacb3dac08bed5a7c01994 100644 (file)
--- a/test.txt
+++ b/test.txt
 
 # expect some NOTICE and PING to process/reply during initiation
 0:2 < :*.?.net NOTICE * :*** Looking up your ident...
-1,2 $$$ *** Looking up your ident...
+1,2 $ message set to: [N :server  :*** Looking up your ident...]
+3 <<< [:server] *** Looking up your ident...
+1,2 $ message set to: [P   :]
 0:2 < :*.?.net NOTICE * :*** Looking up your hostname...
-1,2 $$$ *** Looking up your hostname...
+1,2 $ message set to: [N :server  :*** Looking up your hostname...]
+3 <<< [:server] *** Looking up your hostname...
+1,2 $ message set to: [P   :]
 0:2 < :*.?.net NOTICE * :*** Found your hostname (baz.bar.foo)
-1,2 $$$ *** Found your hostname (baz.bar.foo)
+1,2 $ message set to: [N :server  :*** Found your hostname (baz.bar.foo)]
+3 <<< [:server] *** Found your hostname (baz.bar.foo)
+1,2 $ message set to: [P   :]
 0:2 < PING :?
 2 > PONG :?
 
 
 # handle bot query NOTICE
 0:2 < :SaslServ!SaslServ@services.bar.baz NOTICE foo1 :Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
-3 <<< [SaslServ] Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
+1,2 $ message set to: [N SaslServ  :Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.]
+4 <<< [SaslServ] Last login from ~foobarbaz@foo.bar.baz on Jan 1 22:00:00 2021 +0000.
+1,2 $ message set to: [P   :]
 
 # check difference in available commands when switching to client window
 > /join #test
 0 #   0) :start
 0 #   1) foo.bar.baz :DEBUG
 0 #   2) foo.bar.baz :RAW
-0 #   3) foo.bar.baz SaslServ
+0 #   3) foo.bar.baz :server
+0 #   4) foo.bar.baz SaslServ
 > /window 1
 > /help
 1 # commands available in this window:
 1,2 $ channels:#test:exits cleared
 0:2 < :foo.bar.baz 333 foo1 #test bar!~bar@bar.bar 1234567890
 1,2 $ channels:#test:topic set to: [Topic(what='foo bar baz', who=NickUserHost(nick='bar', user='~bar', host='bar.bar'))]
-4 $ bar!~bar@bar.bar set topic: foo bar baz
+5 $ bar!~bar@bar.bar set topic: foo bar baz
 0:2 < :foo.bar.baz 353 foo1 @ #test :foo1 @bar
 1,2 $ users:1:nick set to: [?]
 1,2 $ users:1:nick set to: [bar]
 1,2 $ channels:#test:user_ids set to:
 1,2 $   1
 1,2 $   me
-4 $ residents: bar, foo1
+5 $ residents: bar, foo1
 
 # deliver PRIVMSG to channel window, update sender's user+host from metadata
 0:2 < :bar!~bar@bar.bar PRIVMSG #test :hi there
 1,2 $ users:1:user set to: [~bar]
 1,2 $ users:1:host set to: [bar.bar]
-4 < [bar] hi there
+1,2 $ message set to: [P bar #test :hi there]
+5 < [bar] hi there
+1,2 $ message set to: [P   :]
 
 # check _changing_ TOPIC message is communicated to channel window
 0:2 < :bar!~bar@bar.bar TOPIC #test :foo bar baz
 0:2 < :bar!~bar@bar.bar TOPIC #test :abc def ghi
 1,2 $ channels:#test:topic set to: [Topic(what='abc def ghi', who=NickUserHost(nick='bar', user='~bar', host='bar.bar'))]
-4 $ bar!~bar@bar.bar set topic: abc def ghi
+5 $ bar!~bar@bar.bar set topic: abc def ghi
 
 # process non-self channel JOIN
 0:2 < :baz!~baz@baz.baz JOIN :#test
 1,2 $   1
 1,2 $   2
 1,2 $   me
-4 $ baz!~baz@baz.baz joins
+5 $ baz!~baz@baz.baz joins
 
 # join second channel with partial residents identity to compare distribution of resident-specific messages
 > /join #testtest
 1,2 $ channels:#testtest:user_ids set to:
 1,2 $   2
 1,2 $   me
-5 $ residents: baz, foo1
+6 $ residents: baz, foo1
 
 # handle query window with known user
 0:2 < :baz!~baz@baz.baz PRIVMSG foo1 :hi there
-6 < [baz] hi there
+1,2 $ message set to: [P baz  :hi there]
+7 < [baz] hi there
+1,2 $ message set to: [P   :]
 > /privmsg baz hello, how is it going
 2 > PRIVMSG baz :hello, how is it going
-6 > [foo1] hello, how is it going
+7 > [foo1] hello, how is it going
 0:2 < :baz!~baz@baz.baz PRIVMSG foo1 :fine!
-6 < [baz] fine!
+1,2 $ message set to: [P baz  :fine!]
+7 < [baz] fine!
+1,2 $ message set to: [P   :]
 
 # handle failure to query absent user
 > /privmsg barbar hello!
 2 > PRIVMSG barbar :hello!
-7 > [foo1] hello!
+8 > [foo1] hello!
 0:2 < :*.?.net 401 foo1 barbar :No such nick/channel
-7 !$ barbar not online
+8 !$ barbar not online
 
 # handle non-self renaming
 0:2 < :baz!~baz@baz.baz NICK :bazbaz
 1,2 $ users:2:nick set to: [bazbaz]
-4,5,6 $ baz!~baz@baz.baz renames bazbaz
+5,6,7 $ baz!~baz@baz.baz renames bazbaz
 
 # handle non-self PART in one of two inhabited channels, preserve identity into re-JOIN
 0:2 < :bazbaz!~baz@baz.baz PART :#test
 1,2 $ channels:#test:user_ids set to:
 1,2 $   1
 1,2 $   me
-4 $ bazbaz!~baz@baz.baz parts
+5 $ bazbaz!~baz@baz.baz parts
 1,2 $ channels:#test:exits:2 cleared
 0:2 < :bazbaz!~baz@baz.baz JOIN :#test
 1,2 $ channels:#test:user_ids set to:
 1,2 $   1
 1,2 $   2
 1,2 $   me
-4 $ bazbaz!~baz@baz.baz joins
+5 $ bazbaz!~baz@baz.baz joins
 
 # handle non-self PART in only inhabited channel, lose identity, re-join as new identity
 0:2 < :bar!~bar@bar.bar PART :#test
 1,2 $ channels:#test:user_ids set to:
 1,2 $   2
 1,2 $   me
-4 $ bar!~bar@bar.bar parts
+5 $ bar!~bar@bar.bar parts
 1,2 $ channels:#test:exits:1 cleared
 1,2 $ users:1 cleared
 0:2 < :bar!~bar@bar.bar JOIN :#test
 1,2 $   2
 1,2 $   3
 1,2 $   me
-4 $ bar!~bar@bar.bar joins
+5 $ bar!~bar@bar.bar joins
 
 # handle non-self QUIT
 0:2 < :bazbaz!~baz@baz.baz QUIT :Client Quit
 1,2 $ users:2:exit_msg set to: [QClient Quit]
-6 $ bazbaz!~baz@baz.baz quits: Client Quit
+7 $ bazbaz!~baz@baz.baz quits: Client Quit
 1,2 $ channels:#test:exits:2 set to: [QClient Quit]
 1,2 $ channels:#test:user_ids set to:
 1,2 $   3
 1,2 $   me
-4 $ bazbaz!~baz@baz.baz quits: Client Quit
+5 $ bazbaz!~baz@baz.baz quits: Client Quit
 1,2 $ channels:#test:exits:2 cleared
 1,2 $ channels:#testtest:exits:2 set to: [QClient Quit]
 1,2 $ channels:#testtest:user_ids set to:
 1,2 $   me
-5 $ bazbaz!~baz@baz.baz quits: Client Quit
+6 $ bazbaz!~baz@baz.baz quits: Client Quit
 1,2 $ channels:#testtest:exits:2 cleared
 1,2 $ users:2 cleared
 
 1,2 $ channels:#test:exits:me set to: [P]
 1,2 $ channels:#test:user_ids set to:
 1,2 $   3
-4 $ foo1!~foobarbaz@baz.bar.foo parts
+5 $ foo1!~foobarbaz@baz.bar.foo parts
 1,2 $ channels:#test:exits:me cleared
 1,2 $ channels:#test cleared
 1,2 $ users:3 cleared
 2 > QUIT :ircplom says bye
 0:2 < :foo1!~foobarbaz@baz.bar.foo QUIT :Client Quit
 1,2 $ users:me:exit_msg set to: [QClient Quit]
-3,6,7 $ foo1!~foobarbaz@baz.bar.foo quits: Client Quit
+3,4,7,8 $ foo1!~foobarbaz@baz.bar.foo quits: Client Quit
 1,2 $ channels:#testtest:exits:me set to: [QClient Quit]
 1,2 $ channels:#testtest:user_ids set to:
-5 $ foo1!~foobarbaz@baz.bar.foo quits: Client Quit
+6 $ foo1!~foobarbaz@baz.bar.foo quits: Client Quit
 1,2 $ channels:#testtest:exits:me cleared
 0:2 < ERROR :Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]
 1,2 $ connection_state set to: [Closing link: (~foobarbaz@baz.bar.foo) [Quit: ircplom says bye]]
 1,2 $ connection_state set to: []
-3,4,5,6,7 $ DISCONNECTED
+3,4,5,6,7,8 $ DISCONNECTED
 1,2 $ isupport cleared
 1,2 $ isupport:CHANTYPES set to: [#&]
 1,2 $ isupport:PREFIX set to: [(ov)@+]
 
 # test setting up second client, but 432 irrecoverably
 > /connect baz.bar.foo ?foo foo:foo
-8,9 $ isupport cleared
-8,9 $ isupport:CHANTYPES set to: [#&]
-8,9 $ isupport:PREFIX set to: [(ov)@+]
-8,9 $ isupport:USERLEN set to: [10]
-8,9 $ hostname set to: [baz.bar.foo]
-8,9 $ port set to: [-1]
-8,9 $ nick_wanted set to: [?foo]
-8,9 $ user_wanted set to: [foo]
-8,9 $ realname set to: [foo]
-8,9 $ port set to: [6697]
-8,9 $ connection_state set to: [connecting]
-8,9 $ connection_state set to: [connected]
+9,10 $ isupport cleared
+9,10 $ isupport:CHANTYPES set to: [#&]
+9,10 $ isupport:PREFIX set to: [(ov)@+]
+9,10 $ isupport:USERLEN set to: [10]
+9,10 $ hostname set to: [baz.bar.foo]
+9,10 $ port set to: [-1]
+9,10 $ nick_wanted set to: [?foo]
+9,10 $ user_wanted set to: [foo]
+9,10 $ realname set to: [foo]
+9,10 $ port set to: [6697]
+9,10 $ connection_state set to: [connecting]
+9,10 $ connection_state set to: [connected]
 , $ CONNECTED
-1:9 > CAP LS :302
-1:9 > USER foo 0 * :foo
-1:9 > NICK :?foo
-1:9 < :*.?.net 432 * ?foo :Erroneous nickname
-8,9 $ connection_state set to: []
+1:10 > CAP LS :302
+1:10 > USER foo 0 * :foo
+1:10 > NICK :?foo
+1:10 < :*.?.net 432 * ?foo :Erroneous nickname
+9,10 $ connection_state set to: []
 , $ DISCONNECTED
-8,9 $ isupport cleared
-8,9 $ isupport:CHANTYPES set to: [#&]
-8,9 $ isupport:PREFIX set to: [(ov)@+]
-8,9 $ isupport:USERLEN set to: [10]
-8,9 !$ nickname refused for bad format, giving up
+9,10 $ isupport cleared
+9,10 $ isupport:CHANTYPES set to: [#&]
+9,10 $ isupport:PREFIX set to: [(ov)@+]
+9,10 $ isupport:USERLEN set to: [10]
+9,10 !$ nickname refused for bad format, giving up
 
 # test failing third connection
 > /connect baz.baz.baz baz baz:baz
-10,11 $ isupport cleared
-10,11 $ isupport:CHANTYPES set to: [#&]
-10,11 $ isupport:PREFIX set to: [(ov)@+]
-10,11 $ isupport:USERLEN set to: [10]
-10,11 $ hostname set to: [baz.baz.baz]
-10,11 $ port set to: [-1]
-10,11 $ nick_wanted set to: [baz]
-10,11 $ user_wanted set to: [baz]
-10,11 $ realname set to: [baz]
-10,11 $ port set to: [6697]
-10,11 $ connection_state set to: [connecting]
-10,11 $ connection_state set to: [connected]
+11,12 $ isupport cleared
+11,12 $ isupport:CHANTYPES set to: [#&]
+11,12 $ isupport:PREFIX set to: [(ov)@+]
+11,12 $ isupport:USERLEN set to: [10]
+11,12 $ hostname set to: [baz.baz.baz]
+11,12 $ port set to: [-1]
+11,12 $ nick_wanted set to: [baz]
+11,12 $ user_wanted set to: [baz]
+11,12 $ realname set to: [baz]
+11,12 $ port set to: [6697]
+11,12 $ connection_state set to: [connecting]
+11,12 $ connection_state set to: [connected]
 , $ CONNECTED
-2:11 > CAP LS :302
-2:11 > USER baz 0 * :baz
-2:11 > NICK :baz
+2:12 > CAP LS :302
+2:12 > USER baz 0 * :baz
+2:12 > NICK :baz
 2: < FAKE_IRC_CONN_ABORT_EXCEPTION
-10,11 $ connection_state set to: [broken: FAKE_IRC_CONN_ABORT_EXCEPTION]
-10,11 $ connection_state set to: []
+11,12 $ connection_state set to: [broken: FAKE_IRC_CONN_ABORT_EXCEPTION]
+11,12 $ connection_state set to: []
 , $ DISCONNECTED
-10,11 $ isupport cleared
-10,11 $ isupport:CHANTYPES set to: [#&]
-10,11 $ isupport:PREFIX set to: [(ov)@+]
-10,11 $ isupport:USERLEN set to: [10]
+11,12 $ isupport cleared
+11,12 $ isupport:CHANTYPES set to: [#&]
+11,12 $ isupport:PREFIX set to: [(ov)@+]
+11,12 $ isupport:USERLEN set to: [10]
 
 # check that (save TUI tests assuming start on window 0, and no 4 yet) on reconnect, all the same effects can be expected
 > /reconnect
 repeat 63:65
-3,4,5,6,7 $ CONNECTED
-repeat 66:158
-repeat 168:368
+3,4,5,6,7,8 $ CONNECTED
+repeat 66:166
+repeat 177:383
 
 > /quit
 0 <