From: Christian Heller Date: Sat, 26 Jul 2025 15:23:12 +0000 (+0200) Subject: Improve log messaging, differentiate target reach. X-Git-Url: https://plomlompom.com/repos/booking/blog?a=commitdiff_plain;h=05b8180fc58744badb04404a3637967d329dcc68;p=ircplom Improve log messaging, differentiate target reach. --- diff --git a/ircplom/irc_conn.py b/ircplom/irc_conn.py index 192c096..de4b33a 100644 --- a/ircplom/irc_conn.py +++ b/ircplom/irc_conn.py @@ -11,6 +11,10 @@ from ircplom.events import ( AffectiveEvent, ExceptionEvent, Loop, PayloadMixin, QueueMixin) +ClientsDb = dict[UUID, 'Client'] + +CHAT_GLOB = '*' + _TIMEOUT_RECV_LOOP = 0.1 _TIMEOUT_CONNECT = 5 _CONN_RECV_BUFSIZE = 1024 @@ -23,8 +27,6 @@ _IRCSPEC_TAG_ESCAPES = ((r'\:', ';'), (r'\r', '\r'), (r'\\', '\\')) -ClientsDb = dict[UUID, 'Client'] - class IrcMessage: 'Properly structured representation of IRC message as per IRCv3 spec.' @@ -149,6 +151,7 @@ class ClientEvent(AffectiveEvent, ClientIdMixin): class _ConnectedEvent(ClientEvent): def affect(self, target: 'Client') -> None: + target.log(msg='# connected to server', chat=CHAT_GLOB) target.send(IrcMessage(verb='USER', params=(getuser(), '0', '*', target.realname))) target.send(IrcMessage(verb='NICK', params=(target.nickname,))) @@ -159,8 +162,8 @@ class InitReconnectEvent(ClientEvent): def affect(self, target: 'Client') -> None: if target.assumed_open: - target.log('ALERT: Reconnect called, but still seem connected, ' - 'so nothing to do.') + target.log('# ALERT: reconnection called, but still seem ' + 'connected, so nothing to do.') else: target.start_connecting() @@ -169,8 +172,8 @@ class SendEvent(ClientEvent, PayloadMixin): 'To trigger sending of payload to server.' payload: IrcMessage - def affect(self, target: 'Client') -> None: - target.send(self.payload) + def affect(self, target: 'Client', chat: str = '') -> None: + target.send(msg=self.payload, chat=chat) class ClientQueueMixin(QueueMixin): @@ -184,15 +187,17 @@ class ClientQueueMixin(QueueMixin): class Client(ABC, ClientQueueMixin): 'Abstracts socket connection, loop over it, and handling messages from it.' + nick_confirmed: bool + nickname: str def __init__(self, hostname: str, nickname: str, realname: str, **kwargs ) -> None: super().__init__(**kwargs) - self.id_ = uuid4() self._hostname = hostname self._socket: Optional[socket] = None - self.assumed_open = False self._recv_loop: Optional[Loop] = None + self.id_ = uuid4() + self.assumed_open = False self.realname = realname self.update_login(nick_confirmed=False, nickname=nickname) self.start_connecting() @@ -203,12 +208,12 @@ class Client(ABC, ClientQueueMixin): def connect(self) -> None: try: self._socket = socket() - self.log(f'Connecting to {self._hostname} …') + self.log(f'# connecting to server {self._hostname} …') self._socket.settimeout(_TIMEOUT_CONNECT) try: self._socket.connect((self._hostname, _PORT)) except (TimeoutError, socket_gaierror) as e: - self.log(f'ALERT: {e}') + self.log(f'# ALERT: {e}') return self._socket.settimeout(_TIMEOUT_RECV_LOOP) self.assumed_open = True @@ -222,24 +227,37 @@ class Client(ABC, ClientQueueMixin): @abstractmethod def log(self, msg: str, chat: str = '') -> None: - 'Write msg into log, whatever shape that may have.' + 'Write msg into log of chat, whatever shape that may have. + + Messages to chat=CHAT_GLOB are meant to appear in all widgets mapped to + the client, those to chat="" only in the initial connection window. + ' - def send(self, msg: IrcMessage) -> None: + def send(self, msg: IrcMessage, chat: str = '') -> None: 'Send line-separator-delimited message over socket.' if not (self._socket and self.assumed_open): - self.log('ALERT: cannot send, assuming connection closed.') + self.log('# ALERT: cannot send, connection seems closed') return self._socket.sendall(msg.raw.encode('utf-8') + _IRCSPEC_LINE_SEPARATOR) - self.log(f'->: {msg.raw}') + self.log(msg=f'->: {msg.raw}', chat=chat) def update_login(self, nick_confirmed: bool, nickname: str = '') -> None: 'Manage .nickname, .nick_confirmed – useful for subclass extension.' - if nickname: + first_run = not hasattr(self, 'nickname') + prefix = '# nickname' + if first_run or (nickname and nickname != self.nickname): + verb = 'set' if first_run else f'changed from "{self.nickname}' self.nickname = nickname - self.nick_confirmed = nick_confirmed + self.log(msg=f'{prefix} {verb} to {nickname}', chat=CHAT_GLOB) + if first_run or nick_confirmed != self.nick_confirmed: + self.nick_confirmed = nick_confirmed + if not first_run: + self.log( + msg=f'{prefix} {"" if nick_confirmed else "un"}confirmed') def close(self) -> None: 'Close both recv Loop and socket.' + self.log(msg='# disconnected from server', chat=CHAT_GLOB) self.assumed_open = False self.update_login(nick_confirmed=False) if self._recv_loop: diff --git a/ircplom/tui.py b/ircplom/tui.py index 0e3df99..eb28de7 100644 --- a/ircplom/tui.py +++ b/ircplom/tui.py @@ -13,7 +13,7 @@ from blessed import Terminal as BlessedTerminal from ircplom.events import ( AffectiveEvent, Loop, PayloadMixin, QueueMixin, QuitEvent) from ircplom.irc_conn import ( - IrcMessage, Client, ClientIdMixin, ClientQueueMixin, + CHAT_GLOB, IrcMessage, Client, ClientIdMixin, ClientQueueMixin, InitReconnectEvent, NewClientEvent, SendEvent) _MIN_HEIGHT = 4 @@ -394,7 +394,7 @@ class _KeyboardEvent(TuiEvent, PayloadMixin): elif len(self.payload) == 1: target.window.prompt.insert(self.payload) else: - target.log(f'ALERT: unknown keyboard input: {self.payload}') + target.log(f'# ALERT: unknown keyboard input: {self.payload}') super().affect(target) @@ -430,29 +430,36 @@ class Tui(QueueMixin): 'Currently selected _Window.' return self.windows[self._window_idx] + def _new_client_window(self, client_id: UUID, chat: str = '' + ) -> '_ClientWindow': + new_idx = len(self.windows) + win = _ClientWindow(idx=new_idx, term=self.term, q_out=self._q_out, + client_id=client_id, chat=chat) + self.windows += [win] + self._switch_window(new_idx) + return win + def client_wins(self, client_id: UUID) -> list['_ClientWindow']: - 'All _ClientWindows matching client_id.' - return [win for win in self.windows + 'All _ClientWindows matching client_id; if none, create one.' + wins = [win for win in self.windows if isinstance(win, _ClientWindow) and win.client_id == client_id] # pylint: disable=no-member + if not wins: + wins = [self._new_client_window(client_id=client_id)] + return wins def client_win(self, client_id: UUID, chat: str = '') -> '_ClientWindow': '''That _ClientWindow matching client_id and chat; create if none. - In case of creation, also switch to window, and if not client's first - window, copy prompt prefix from client's first window. + In case of creation, copy prompt prefix from client's first window. ''' client_wins = self.client_wins(client_id) candidates = [win for win in client_wins if win.chat == chat] if candidates: return candidates[0] - new_idx = len(self.windows) - win = _ClientWindow(idx=new_idx, term=self.term, q_out=self._q_out, - client_id=client_id, chat=chat) + win = self._new_client_window(client_id=client_id, chat=chat) if client_wins: win.prompt.prefix = client_wins[0].prompt.prefix - self.windows += [win] - self._switch_window(new_idx) return win def log(self, msg: str) -> None: @@ -500,7 +507,7 @@ class Tui(QueueMixin): else: alert = 'not prefixed by /' if alert: - self.log(f'invalid prompt command: {alert}') + self.log(f'# ALERT: invalid prompt command: {alert}') def cmd__quit(self) -> None: 'Trigger program exit.' @@ -649,8 +656,8 @@ class _ClientWindow(_Window, ClientQueueMixin): def cmd__disconnect(self, quit_msg: str = 'ircplom says bye') -> None: 'Send QUIT command to server.' - self._cput(SendEvent, payload=IrcMessage(verb='QUIT', - params=(quit_msg,))) + self._cput(SendEvent, + payload=IrcMessage(verb='QUIT', params=(quit_msg,))) def cmd__reconnect(self) -> None: 'Attempt reconnection.' @@ -658,8 +665,8 @@ class _ClientWindow(_Window, ClientQueueMixin): def cmd__nick(self, new_nick: str) -> None: 'Attempt nickname change.' - self._cput(SendEvent, payload=IrcMessage(verb='NICK', - params=(new_nick,))) + self._cput(SendEvent, + payload=IrcMessage(verb='NICK', params=(new_nick,))) class _ClientWindowEvent(TuiEvent, ClientIdMixin): @@ -673,7 +680,12 @@ class _ClientLogEvent(_ClientWindowEvent, PayloadMixin): payload: str def affect(self, target: Tui) -> None: - target.client_win(self.client_id, self.chat).log.append(self.payload) + if self.chat == CHAT_GLOB: + for win in target.client_wins(self.client_id): + win.log.append(self.payload) + else: + target.client_win(self.client_id, self.chat + ).log.append(self.payload) super().affect(target)