From dd12e37fe6fd1a9d8ac809af136dc84665f7896f Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Fri, 19 Sep 2025 21:22:56 +0200 Subject: [PATCH] Fix /part for one channel appearing in windows for all. --- ircplom/client.py | 25 ++++++++++++++----------- ircplom/client_tui.py | 28 +++++++++++++++++++++------- ircplom/testing.py | 1 + test.txt | 16 ++++++++++++---- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/ircplom/client.py b/ircplom/client.py index 38db44e..7df5eaa 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -314,6 +314,7 @@ class Channel: 'Collects .topic, and in .user_ids inhabitant IDs.' topic: Topic user_ids: Iterable[str] + exits: Dict[str] class LogScope(Enum): @@ -323,6 +324,7 @@ class LogScope(Enum): RAW = auto() CHAT = auto() USER = auto() + USER_NO_CHANNELS = auto() SAME = auto() @@ -374,6 +376,7 @@ class _CompletableTopic(_Completable, Topic): class _Channel(Channel): user_ids: _CompletableStringsSet topic: _CompletableTopic + exits: _Dict[str] def __init__(self, userid_for_nickuserhost: Callable, @@ -399,9 +402,11 @@ class _Channel(Channel): 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() @@ -454,21 +459,18 @@ class _User(_SetNickuserhostMixin, User): 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: @@ -491,6 +493,7 @@ class _UpdatingCompletableTopic(_UpdatingCompletable, _CompletableTopic): class _UpdatingChannel(_UpdatingAttrsMixin, _Channel): user_ids: _UpdatingCompletableStringsSet topic: _UpdatingCompletableTopic + exits: _UpdatingDict[str] class _UpdatingUser(_UpdatingAttrsMixin, _User): @@ -567,13 +570,13 @@ class _UpdatingChannelsDict(_UpdatingDict[_UpdatingChannel]): '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]): diff --git a/ircplom/client_tui.py b/ircplom/client_tui.py index 92a1429..b708689 100644 --- a/ircplom/client_tui.py +++ b/ircplom/client_tui.py @@ -214,6 +214,7 @@ class _QueryWindow(_ChatWindow): 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) @@ -230,10 +231,19 @@ class _UpdatingChannel(_UpdatingNode, Channel): 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: @@ -248,10 +258,9 @@ class _UpdatingUser(_UpdatingNode, User): 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 @@ -307,14 +316,16 @@ class _ClientWindowsManager: 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 { @@ -344,7 +355,8 @@ class _ClientWindowsManager: 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}', @@ -397,6 +409,8 @@ class ClientTui(BaseTui): 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) diff --git a/ircplom/testing.py b/ircplom/testing.py index 497eab1..eddd5ff 100644 --- a/ircplom/testing.py +++ b/ircplom/testing.py @@ -119,6 +119,7 @@ class TestingClientTui(ClientTui): 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: diff --git a/test.txt b/test.txt index 5dacdb0..3570853 100644 --- a/test.txt +++ b/test.txt @@ -188,6 +188,7 @@ 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 @@ -222,7 +223,9 @@ # 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 @@ -235,12 +238,17 @@ # 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 @@ -269,7 +277,7 @@ 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 < -- 2.30.2