# ourselves
from ircplom.events import (
AffectiveEvent, CrashingException, ExceptionEvent, QueueMixin)
-from ircplom.irc_conn import (BaseIrcConnection, IrcConnAbortException,
- IrcMessage, PORT_SSL)
+from ircplom.irc_conn import (
+ BaseIrcConnection, IrcConnAbortException, IrcMessage, ILLEGAL_NICK_CHARS,
+ ILLEGAL_NICK_FIRSTCHARS, ISUPPORT_DEFAULTS, PORT_SSL)
ClientsDb = dict[str, 'Client']
_NAMES_DESIRED_SERVER_CAPS = ('sasl',)
-_ILLEGAL_NICK_FIRSTCHARS = '~&@+# '
class _MsgTok(Enum):
self._dict: dict[str, Any] = {}
def __getitem__(self, key: str):
- return self._dict[key]
+ return self._dict[key] if key in self._dict else None
def __setitem__(self, key: str, val: Any) -> None:
if isinstance(val, _NickUserHost):
return not isinstance(getattr(self, key), (bool, int, str, tuple,
_CompletableStringsList))
+ @property
+ def illegal_nick_firstchars(self) -> str:
+ 'Calculated from hardcoded constants and .isupports.'
+ return (ILLEGAL_NICK_CHARS + ILLEGAL_NICK_FIRSTCHARS
+ + self.chan_prefixes + self.membership_prefixes)
+
+ @property
+ def chan_prefixes(self) -> str:
+ 'Registered possible channel name prefixes.'
+ return self.isupports['CHANTYPES'] or ISUPPORT_DEFAULTS['CHANTYPES']
+
+ @property
+ def membership_prefixes(self) -> str:
+ 'Registered possible membership nickname prefixes.'
+ prefix = self.isupports['PREFIX'] or ISUPPORT_DEFAULTS['PREFIX']
+ toks = prefix.split(')', maxsplit=1)
+ assert len(toks) == 2
+ assert toks[0][0] == '('
+ return toks[1]
+
def user_id(self, query: str | _NickUserHost) -> str:
'Return user_id for nickname of entire NickUserHost, create if none.'
nick = query if isinstance(query, str) else query.nick
and not set('@!') & set(msg_tok)) else None
if ex_tok is _MsgTok.CHANNEL:
return {'id': msg_tok, 'db': self._db.chan(msg_tok)
- } if msg_tok[0] == '#' else None
+ } if msg_tok[0] in self._db.chan_prefixes else None
if ex_tok is _MsgTok.NICKNAME:
- return (msg_tok if msg_tok[0] not in _ILLEGAL_NICK_FIRSTCHARS
+ return (msg_tok
+ if msg_tok[0] not in self._db.illegal_nick_firstchars
else None)
if ex_tok is _MsgTok.NICK_USER_HOST:
try:
self._db.isupports.set_from_eq_str(str(item))
elif ret['verb'] == '353': # RPL_NAMREPLY
for user_id in [
- self._db.user_id(name.lstrip(_ILLEGAL_NICK_FIRSTCHARS))
+ self._db.user_id(name.lstrip(self._db.membership_prefixes))
for name in ret['names']]:
ret['channel']['db'].add_user(user_id)
elif ret['verb'] == '372': # RPL_MOTD
# ourselves
from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window,
CMD_SHORTCUTS)
-from ircplom.irc_conn import IrcMessage
+from ircplom.irc_conn import IrcMessage, ISUPPORT_DEFAULTS
from ircplom.client import (
Client, ClientQueueMixin, Db, IrcConnSetup, LogScope, NewClientEvent,
NickUserHost, ServerCapability, SharedChannelDbFields,
if not self.display:
self.display = str(self.value)
- @property
- def is_chan(self) -> bool:
- 'Return if .path points to a _ChannelDb.'
- return self.path[0] == '#'
-
class _Db(Db):
def set_and_check_for_change(self, update: _Update
) -> bool | dict[str, tuple[str, ...]]:
result: bool | dict[str, tuple[str, ...]] = False
- if update.is_chan:
+ if self.is_chan_name(update.path):
chan_name = update.path
if update.value is None and not update.arg:
del self._channels[chan_name]
result = super().set_and_check_for_change(update)
return result
+ def is_chan_name(self, name: str) -> bool:
+ 'Tests name to match CHANTYPES prefixes.'
+ return name[0] in self.isupports.get('CHANTYPES',
+ ISUPPORT_DEFAULTS['CHANTYPES'])
+
def chan(self, name: str) -> _ChannelDb:
'Produce DB for channel of name – pre-existing, or newly created.'
if name not in self._channels:
def update_db(self, update: _Update) -> bool:
'Apply update to .db, and if changing anything, log and trigger.'
- scope = (LogScope.CHAT if update.is_chan
+ is_chan_update = self.db.is_chan_name(update.path)
+ scope = (LogScope.CHAT if is_chan_update
else (LogScope.ALL if update.path == 'connection_state'
else LogScope.SERVER))
verb = 'cleared' if update.value is None else 'changed to:'
what = f'{update.path}:{update.arg}' if update.arg else update.path
- log_kwargs = {'target': update.path} if update.is_chan else {}
+ log_kwargs = {'target': update.path} if is_chan_update else {}
result = self.db.set_and_check_for_change(update)
if result is False:
return False
def _on_update(self, path: str, arg: str = '') -> None:
value: Optional[_DbType] = None
- is_chan = path[0] == '#'
+ is_chan = path[0] in self._db.chan_prefixes
display = ''
if arg:
if is_chan and path in self._db.chan_names: