'TUI adaptions to Client.'
# built-ins
+from enum import Enum, auto
from getpass import getuser
from typing import Any, Callable, Optional, Sequence
# ourselves
from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window,
CMD_SHORTCUTS)
from ircplom.client import (
- AutoAttrMixin, Channel, ChatMessage, Client, ClientQueueMixin, Dict,
- DictItem, ImplementationFail, IrcConnSetup, LogScope, NewClientEvent,
- NickUserHost, SendFail, ServerCapability, SharedClientDbFields, User)
+ AutoAttrMixin, Channel, ChatMessage, Client, ClientQueueMixin, Dict,
+ DictItem, ImplementationFail, IrcConnSetup, NewClientEvent, NickUserHost,
+ SendFail, ServerCapability, SharedClientDbFields, TargetUserOffline, User)
from ircplom.irc_conn import IrcMessage
CMD_SHORTCUTS['disconnect'] = 'window.disconnect'
_LOG_PREFIX_IN = '<'
+class _LogScope(Enum):
+ 'Where log messages should go.'
+ ALL = auto()
+ DEBUG = auto()
+ RAW = auto()
+ CHAT = auto()
+ USER = auto()
+ USER_NO_CHANNELS = auto()
+
+
class _ClientWindow(Window, ClientQueueMixin):
- def __init__(self, scope: LogScope, log: Callable, **kwargs) -> None:
+ def __init__(self, scope: _LogScope, log: Callable, **kwargs) -> None:
self.scope = scope
self._log = log
super().__init__(**kwargs)
self._title = f'{self.client_id} '\
- + f':{"DEBUG" if self.scope == LogScope.DEBUG else "RAW"}'
+ + f':{"DEBUG" if self.scope == _LogScope.DEBUG else "RAW"}'
def _send_msg(self, verb: str, params: tuple[str, ...]) -> None:
self._client_trigger('send_w_params_tuple', verb=verb, params=params)
def cmd__disconnect(self, quit_msg: str = 'ircplom says bye') -> None:
'Send QUIT command to server.'
- self._log('requesting disconnect …', scope=LogScope.DEBUG)
+ self._log('requesting disconnect …', scope=_LogScope.DEBUG)
self._send_msg('QUIT', (quit_msg,))
def cmd__reconnect(self) -> None:
class _Update:
old_value: Any
- results: list[tuple[LogScope, Any]]
+ results: list[tuple[_LogScope, Any]]
def __init__(self, path: tuple[str, ...], value: Any) -> None:
self.full_path = path
self._set(update.key, update.value)
do_report |= True
if do_report:
- update.results += [(LogScope.DEBUG,
+ update.results += [(_LogScope.DEBUG,
tuple(sorted(update.value))
if isinstance(update.value, set)
else update.value)]
super().recursive_set_and_report_change(update)
if update.key == 'topic':
msg = f':{self.topic.who} set topic: {self.topic.what}'
- update.results += [(LogScope.CHAT, [msg])]
+ update.results += [(_LogScope.CHAT, [msg])]
elif update.key == 'user_ids':
if not update.old_value:
nicks = []
for id_ in sorted(update.value):
nicks += [f'NICK:{id_}', ':, ']
nicks.pop()
- update.results += [(LogScope.CHAT, [':residents: '] + nicks)]
+ update.results += [(_LogScope.CHAT, [':residents: '] + nicks)]
else:
for id_ in (id_ for id_ in update.value
if id_ not in update.old_value):
- update.results += [(LogScope.CHAT,
+ update.results += [(_LogScope.CHAT,
[f'NUH:{id_}', ': joins'])]
for id_ in (id_ for id_ in update.old_value
if id_ not in update.value):
- update.results += [(LogScope.CHAT,
+ update.results += [(_LogScope.CHAT,
_UpdatingUser.exit_msg_toks(
f'NUH:{id_}', self.exits[id_]))]
self.prev_nick = update.old_value
if update.old_value != '?':
update.results += [
- (LogScope.USER,
+ (_LogScope.USER,
[f':{self.prev} renames {update.value}'])]
elif update.key == 'exit_msg':
if update.value:
- update.results += [(LogScope.USER_NO_CHANNELS,
+ update.results += [(_LogScope.USER_NO_CHANNELS,
self.exit_msg_toks(
f':{self}', update.value))]
super().recursive_set_and_report_change(update)
if update.key == 'connection_state':
if update.value == 'connected':
- update.results += [(LogScope.ALL, [':CONNECTED'])]
+ update.results += [(_LogScope.ALL, [':CONNECTED'])]
elif not update.value:
- update.results += [(LogScope.ALL, [':DISCONNECTED'])]
+ update.results += [(_LogScope.ALL, [':DISCONNECTED'])]
elif update.key == 'message' and update.value:
assert isinstance(update.value, ChatMessage)
- update.results += [(LogScope.CHAT, [':' + update.value.content])]
+ update.results += [(_LogScope.CHAT, [':' + update.value.content])]
class _ClientWindowsManager:
self._tui_new_window = tui_new_window
self.db = _TuiClientDb()
self.windows: list[_ClientWindow] = []
- for scope in (LogScope.DEBUG, LogScope.RAW):
+ for scope in (_LogScope.DEBUG, _LogScope.RAW):
self._new_win(scope)
- def _new_win(self, scope: LogScope, chatname: str = '') -> _ClientWindow:
+ def _new_win(self, scope: _LogScope, chatname: str = '') -> _ClientWindow:
kwargs = {'scope': scope, 'log': self.log, 'win_cls': _ClientWindow}
- if scope == LogScope.CHAT:
+ if scope == _LogScope.CHAT:
kwargs['win_cls'] = (
_ChannelWindow if self.db.is_chan_name(chatname)
else _QueryWindow)
self.windows += [win]
return win
- def window(self, scope: LogScope, chatname: str = '') -> _ClientWindow:
+ def window(self, scope: _LogScope, chatname: str = '') -> _ClientWindow:
'Return client window of scope.'
for win in [w for w in self.windows if w.scope == scope]:
- if scope == LogScope.CHAT:
+ if scope == _LogScope.CHAT:
if isinstance(win, _ChatWindow) and win.chatname == chatname:
return win
continue
self.db.users[user_id].nick,
self.db.users[user_id].prev_nick})))]
- def log(self, msg: str, scope: LogScope, **kwargs) -> None:
+ def log(self, msg: str, scope: _LogScope, **kwargs) -> None:
'From parsing scope, kwargs, build prefix before sending to logger.'
first_char = '$'
sender_label = ''
receiving: Optional[bool] = None
- if scope == LogScope.RAW:
+ if scope == _LogScope.RAW:
receiving = not kwargs.get('out', False)
- if scope == LogScope.CHAT and 'sender' in kwargs:
+ if scope == _LogScope.CHAT and 'sender' in kwargs:
receiving = bool(kwargs['sender'])
sender_label = ' [' + (kwargs['sender'] if receiving
else self.db.users['me'].nick) + ']'
for scope, result in update.results:
log_kwargs: dict[str, Any] = {'scope': scope}
- if scope in {LogScope.CHAT, LogScope.USER,
- LogScope.USER_NO_CHANNELS}:
+ if scope in {_LogScope.CHAT, _LogScope.USER,
+ _LogScope.USER_NO_CHANNELS}:
if update.full_path == ('message',):
log_kwargs['log_target'] = (update.value.target
or update.value.sender)
def _log_target_wins(self, **kwargs) -> Sequence[Window]:
if (scope := kwargs.get('scope', None)):
m = self._client_mngrs[kwargs['client_id']]
- if scope == LogScope.ALL:
+ if scope == _LogScope.ALL:
return [w for w in m.windows
- if w.scope not in {LogScope.DEBUG, LogScope.RAW}]
- if scope == LogScope.DEBUG:
- return [m.window(LogScope.DEBUG), m.window(LogScope.RAW)]
- if scope == LogScope.CHAT:
- return [m.window(LogScope.CHAT, chatname=kwargs['log_target'])]
- if scope == LogScope.USER:
+ if w.scope not in {_LogScope.DEBUG, _LogScope.RAW}]
+ if scope == _LogScope.DEBUG:
+ return [m.window(_LogScope.DEBUG), m.window(_LogScope.RAW)]
+ if scope == _LogScope.CHAT:
+ 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:
+ if scope == _LogScope.USER_NO_CHANNELS:
return m.windows_for_userid(kwargs['log_target'],
w_channels=False)
return [m.window(scope)]
def send(self, verb: str, *args) -> IrcMessage:
msg = super().send(verb, *args)
- self._log(msg.raw, scope=LogScope.RAW, out=True)
+ self._log(msg.raw, scope=_LogScope.RAW, out=True)
return msg
def handle_msg(self, msg: IrcMessage) -> None:
- self._log(msg.raw, scope=LogScope.RAW, out=False)
+ self._log(msg.raw, scope=_LogScope.RAW, out=False)
try:
super().handle_msg(msg)
except ImplementationFail as e:
self._log(str(e), alert=True)
+ except TargetUserOffline as e:
+ name = f'{e}'
+ self._log(f'{name} not online', scope=_LogScope.CHAT,
+ log_target=name, alert=True)
- def _log(self, msg: str, scope: Optional[LogScope] = None, **kwargs
+ def _log(self, msg: str, scope: Optional[_LogScope] = None, **kwargs
) -> None:
if not scope:
- scope = LogScope.DEBUG
+ scope = _LogScope.DEBUG
self._client_tui_trigger('log', scope=scope, msg=msg, **kwargs)
- if scope == LogScope.RAW:
+ if scope == _LogScope.RAW:
with open(f'{self.client_id}.log', 'a', encoding='utf8') as f:
f.write(('>' if kwargs['out'] else '<') + f' {msg}\n')