'Collects .topic, and in .user_ids inhabitant IDs.'
topic: Topic
user_ids: Iterable[str]
+ exits: Dict[str]
class LogScope(Enum):
RAW = auto()
CHAT = auto()
USER = auto()
+ USER_NO_CHANNELS = auto()
SAME = auto()
class _Channel(Channel):
user_ids: _CompletableStringsSet
topic: _CompletableTopic
+ exits: _Dict[str]
def __init__(self,
userid_for_nickuserhost: Callable,
updating=True)
self.user_ids.completable_add(user_id, on_complete=True)
- def remove_user(self, user: '_User') -> None:
+ def remove_user(self, user: '_User', msg: str) -> None:
'From .user_ids remove .nickname, keep .user_ids declared complete.'
+ self.exits[user.id_] = msg
self.user_ids.completable_remove(user.id_, on_complete=True)
+ del self.exits[user.id_]
self.purge_users()
remove_from_channels: Callable,
**kwargs) -> None:
self.names_channels = lambda: names_channels_of_user(self)
- self._remove_from_channels = lambda name='': remove_from_channels(self,
- name)
+ self._remove_from_channels = lambda target, msg: remove_from_channels(
+ self, target, msg)
super().__init__(**kwargs)
def part(self, channel_name: str, exit_msg: str) -> None:
'First set .exit_msg, then remove from channel of channel_name.'
- self.exit_msg = f'P{exit_msg}'
- self.exit_msg = ''
- self._remove_from_channels(channel_name)
+ self._remove_from_channels(channel_name, f'P{exit_msg}')
def quit(self, exit_msg: str) -> None:
'First set .exit_msg, then remove from any channels.'
self.exit_msg = f'Q{exit_msg}'
- self.exit_msg = ''
- self._remove_from_channels()
+ self._remove_from_channels('', self.exit_msg)
@property
def id_(self) -> str:
class _UpdatingChannel(_UpdatingAttrsMixin, _Channel):
user_ids: _UpdatingCompletableStringsSet
topic: _UpdatingCompletableTopic
+ exits: _UpdatingDict[str]
class _UpdatingUser(_UpdatingAttrsMixin, _User):
'Return names of channels listing user as member.'
return tuple(self._of_user(user).keys())
- def remove_user(self, user: _User, target: str) -> None:
+ def remove_user(self, user: _User, target: str, msg: str) -> None:
'Remove user from channel named "target", or all with user if empty.'
if target:
- self[target].remove_user(user)
+ self[target].remove_user(user, msg)
else:
for channel in self._of_user(user).values():
- channel.remove_user(user)
+ channel.remove_user(user, msg)
class _UpdatingIsupportDict(_UpdatingDict[str]):
class _UpdatingChannel(_UpdatingNode, Channel):
log_scopes = {'': LogScope.CHAT}
user_ids: set[str]
+ exits: _UpdatingDict[str]
def recursive_set_and_report_change(self, update: _Update) -> None:
super().recursive_set_and_report_change(update)
for id_ in (id_ for id_ in update.value
if id_ not in update.old_value):
update.results += [(scope, [':joining: ', f'NUH:{id_}'])]
+ for id_ in (id_ for id_ in update.old_value
+ if id_ not in update.value):
+ quits = self.exits[id_][0] == 'Q'
+ part_msg = self.exits[id_][1:]
+ exit_msg = [':' + ('quits' if quits else 'parts') + ': ',
+ f'NUH:{id_}']
+ if part_msg:
+ exit_msg += [f':: {part_msg}']
+ update.results += [(scope, exit_msg)]
class _UpdatingUser(_UpdatingNode, User):
- log_scopes = {'exit_msg': LogScope.USER}
+ log_scopes = {'exit_msg': LogScope.USER_NO_CHANNELS}
prev_nick = '?'
def recursive_set_and_report_change(self, update: _Update) -> None:
elif update.key == 'exit_msg':
update.results.clear()
if update.value:
- msg = f':{self} '
- msg += 'quits' if update.value[0] == 'Q' else 'parts'
+ msg = f':{self} quits'
if len(update.value) > 1:
- msg += ': ' + update.value[1:]
+ msg += f': {update.value[1:]}'
update.results += [(self._scope(update.key), [msg])]
@property
return win
return self._new_win(scope=scope, chatname=chatname)
- def windows_for_userid(self, user_id: str) -> list[_ClientWindow]:
+ def windows_for_userid(self, user_id: str, w_channels = True
+ ) -> list[_ClientWindow]:
'Return windows interacting with userid (all if "me").'
if user_id == 'me':
return self.windows[:]
chan_names = [c for c, v in self.db.channels.items()
if user_id in v.user_ids]
return [w for w in self.windows
- if ((isinstance(w, _ChannelWindow)
+ if ((w_channels
+ and isinstance(w, _ChannelWindow)
and w.chatname in chan_names)
or (isinstance(w, _QueryWindow)
and w.chatname in {
return False
for scope, result in update.results:
log_kwargs: dict[str, Any] = {'scope': scope}
- if scope in {LogScope.CHAT, LogScope.USER}:
+ if scope in {LogScope.CHAT, LogScope.USER,
+ LogScope.USER_NO_CHANNELS}:
log_kwargs |= {'target': update.full_path[1]}
if isinstance(result, Topic):
self.log(f'{result.who} set topic: {result.what}',
return [m.window(LogScope.CHAT, chatname=chatname)]
if scope == LogScope.USER:
return m.windows_for_userid(kwargs['target'])
+ if scope == LogScope.USER_NO_CHANNELS:
+ return m.windows_for_userid(kwargs['target'], w_channels=False)
return [m.window(scope)]
return super()._log_target_wins(**kwargs)
assert expected_msg == msg_sans_time, info
assert expected_win_ids == win_ids, info
self._play_till_next_log()
+ print(win_ids, msg)
return win_ids, logged_msg
def _play_till_next_log(self) -> None:
2 < :foo!~foobarbaz@baz.bar.foo JOIN #test
1,2 $ users:me:user set to: [~foobarbaz]
2 < :foo.bar.baz 332 foo #test :foo bar baz
+4 $ channels:#test:exits cleared
2 < :foo.bar.baz 333 foo #test bar!~bar@bar.bar 1234567890
4 $ bar!~bar@bar.bar set topic: foo bar baz
2 < :foo.bar.baz 353 foo @ #test :foo @bar
# handle non-self PART
2 < :bazbaz!~baz@baz.baz PART :#test
-4 $ bazbaz!~baz@baz.baz parts
+1,2 $ channels:#test:exits:2 set to: [P]
+4 $ parts: bazbaz!~baz@baz.baz
+1,2 $ channels:#test:exits:2 cleared
1,2 $ users:2 cleared
# handle re-join, treat as new user for lack of identity continuity reliability
# handle non-self QUIT
2 < :bazbaz!~baz@baz.baz QUIT :Client Quit
-4 $ bazbaz!~baz@baz.baz quits: Client Quit
+, $ bazbaz!~baz@baz.baz quits: Client Quit
+1,2 $ channels:#test:exits:3 set to: [QClient Quit]
+4 $ quits: bazbaz!~baz@baz.baz: Client Quit
+1,2 $ channels:#test:exits:3 cleared
1,2 $ users:3 cleared
# handle self-PART: clear channel, and its squatters
2 < :foo!~foobarbaz@baz.bar.foo PART :#test
-1,2,3,4 $ foo!~foobarbaz@baz.bar.foo parts
+1,2 $ channels:#test:exits:me set to: [P]
+4 $ parts: foo!~foobarbaz@baz.bar.foo
+1,2 $ channels:#test:exits:me cleared
1,2 $ channels:#test cleared
1,2 $ users:1 cleared
1,2,3,4 $ connection_state set to: [connecting]
1,2,3,4 $ connection_state set to: [connected]
repeat 64:147
-repeat 158:265
+repeat 158:273
> /quit
0 <