From: Christian Heller Date: Wed, 20 Aug 2025 21:38:54 +0000 (+0200) Subject: Enable collection of multi-param items, to integrate 005 into new system. X-Git-Url: https://plomlompom.com/repos/blog?a=commitdiff_plain;h=74a25b5e0aa43be546ba8c03622f010db9ae841c;p=ircplom Enable collection of multi-param items, to integrate 005 into new system. --- diff --git a/ircplom/client.py b/ircplom/client.py index cd10e6a..7d180f8 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -58,143 +58,150 @@ class _MsgParseExpectation(NamedTuple): params: tuple[_MsgTokGuide, ...] = tuple() len_min_params: int = 0 len_max_params: int = 0 + idx_into_list: int = -1 _EXPECTATIONS: tuple[_MsgParseExpectation, ...] = ( - _MsgParseExpectation(_MsgTok.SERVER, '005', tuple(), 3, 15), - - _MsgParseExpectation(_MsgTok.SERVER, - '353', - (_MsgTok.NICKNAME, - '=', - (_MsgTok.CHANNEL, 'channel'), - (_MsgTok.LIST, 'names'))), - _MsgParseExpectation(_MsgTok.SERVER, - '366', - (_MsgTok.NICKNAME, - (_MsgTok.CHANNEL, 'channel'), - _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, - '372', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'line'))), - _MsgParseExpectation(_MsgTok.SERVER, - '376', - (_MsgTok.NICKNAME, - _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, '396', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'host_maybe_w_user'), - _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, - '401', - (_MsgTok.NICKNAME, - (_MsgTok.NICKNAME, 'target'), - _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, - '432', - ('*', - _MsgTok.NICKNAME, - _MsgTok.ANY)), - _MsgParseExpectation(_MsgTok.SERVER, - '432', - ((_MsgTok.NICKNAME, 'fallback'), - _MsgTok.NICKNAME, - _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, - '433', - (_MsgTok.NICKNAME, - _MsgTok.NICKNAME, - _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, - '900', - (_MsgTok.NICKNAME, - (_MsgTok.USER_ADDRESS, 'full_address'), - (_MsgTok.ANY, 'account'), - _MsgTok.ANY)), - _MsgParseExpectation(_MsgTok.SERVER, - '903', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'result'))), - _MsgParseExpectation(_MsgTok.SERVER, - '904', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'result'))), - - _MsgParseExpectation(_MsgTok.NONE, - 'AUTHENTICATE', - ('+',)), - - _MsgParseExpectation(_MsgTok.SERVER, 'CAP', tuple(), 3, 15), - - _MsgParseExpectation(_MsgTok.NONE, - 'ERROR', - ((_MsgTok.ANY, 'reason'),)), - - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'joiner'), - 'JOIN', - ((_MsgTok.CHANNEL, 'channel'),)), - - _MsgParseExpectation(_MsgTok.NICKNAME, - 'MODE', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'mode'))), - _MsgParseExpectation(_MsgTok.USER_ADDRESS, - 'MODE', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'mode'))), - - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'named'), - 'NICK', - ((_MsgTok.NICKNAME, 'nickname'),)), - - _MsgParseExpectation(_MsgTok.SERVER, - 'NOTICE', - ('*', - (_MsgTok.ANY, 'message'))), - _MsgParseExpectation(_MsgTok.SERVER, - 'NOTICE', - ((_MsgTok.NICKNAME, 'nickname'), - (_MsgTok.ANY, 'message'))), - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), - 'NOTICE', - ((_MsgTok.NICKNAME, 'nickname'), - (_MsgTok.ANY, 'message'))), - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), - 'NOTICE', - ((_MsgTok.CHANNEL, 'channel'), - (_MsgTok.ANY, 'message'))), - - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'), - 'PART', - ((_MsgTok.CHANNEL, 'channel'),)), - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'), - 'PART', - ((_MsgTok.CHANNEL, 'channel'), - (_MsgTok.ANY, 'reason'))), - - _MsgParseExpectation(_MsgTok.NONE, - 'PING', - ((_MsgTok.ANY, 'reply'),)), - - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), - 'PRIVMSG', - ((_MsgTok.NICKNAME, 'nickname'), - (_MsgTok.ANY, 'message'))), - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), - 'PRIVMSG', - ((_MsgTok.CHANNEL, 'channel'), - (_MsgTok.ANY, 'message'))), - - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'quitter'), - 'QUIT', - ((_MsgTok.ANY, 'message'),)), + # _MsgParseExpectation(_MsgTok.SERVER, '005', tuple(), 3, 15), + _MsgParseExpectation(_MsgTok.SERVER, + '005', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'isupports'), + _MsgTok.ANY), + idx_into_list=1), + + _MsgParseExpectation(_MsgTok.SERVER, + '353', + (_MsgTok.NICKNAME, + '=', + (_MsgTok.CHANNEL, 'channel'), + (_MsgTok.LIST, 'names'))), + _MsgParseExpectation(_MsgTok.SERVER, + '366', + (_MsgTok.NICKNAME, + (_MsgTok.CHANNEL, 'channel'), + _MsgTok.ANY)), + + _MsgParseExpectation(_MsgTok.SERVER, + '372', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'line'))), + _MsgParseExpectation(_MsgTok.SERVER, + '376', + (_MsgTok.NICKNAME, + _MsgTok.ANY)), + + _MsgParseExpectation(_MsgTok.SERVER, '396', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'host_maybe_w_user'), + _MsgTok.ANY)), + + _MsgParseExpectation(_MsgTok.SERVER, + '401', + (_MsgTok.NICKNAME, + (_MsgTok.NICKNAME, 'target'), + _MsgTok.ANY)), + + _MsgParseExpectation(_MsgTok.SERVER, + '432', + ('*', + _MsgTok.NICKNAME, + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '432', + ((_MsgTok.NICKNAME, 'fallback'), + _MsgTok.NICKNAME, + _MsgTok.ANY)), + + _MsgParseExpectation(_MsgTok.SERVER, + '433', + (_MsgTok.NICKNAME, + _MsgTok.NICKNAME, + _MsgTok.ANY)), + + _MsgParseExpectation(_MsgTok.SERVER, + '900', + (_MsgTok.NICKNAME, + (_MsgTok.USER_ADDRESS, 'full_address'), + (_MsgTok.ANY, 'account'), + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '903', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'result'))), + _MsgParseExpectation(_MsgTok.SERVER, + '904', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'result'))), + + _MsgParseExpectation(_MsgTok.NONE, + 'AUTHENTICATE', + ('+',)), + + _MsgParseExpectation(_MsgTok.SERVER, 'CAP', tuple(), 3, 15), + + _MsgParseExpectation(_MsgTok.NONE, + 'ERROR', + ((_MsgTok.ANY, 'reason'),)), + + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'joiner'), + 'JOIN', + ((_MsgTok.CHANNEL, 'channel'),)), + + _MsgParseExpectation(_MsgTok.NICKNAME, + 'MODE', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'mode'))), + _MsgParseExpectation(_MsgTok.USER_ADDRESS, + 'MODE', + (_MsgTok.NICKNAME, + (_MsgTok.ANY, 'mode'))), + + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'named'), + 'NICK', + ((_MsgTok.NICKNAME, 'nickname'),)), + + _MsgParseExpectation(_MsgTok.SERVER, + 'NOTICE', + ('*', + (_MsgTok.ANY, 'message'))), + _MsgParseExpectation(_MsgTok.SERVER, + 'NOTICE', + ((_MsgTok.NICKNAME, 'nickname'), + (_MsgTok.ANY, 'message'))), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), + 'NOTICE', + ((_MsgTok.NICKNAME, 'nickname'), + (_MsgTok.ANY, 'message'))), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), + 'NOTICE', + ((_MsgTok.CHANNEL, 'channel'), + (_MsgTok.ANY, 'message'))), + + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'), + 'PART', + ((_MsgTok.CHANNEL, 'channel'),)), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'), + 'PART', + ((_MsgTok.CHANNEL, 'channel'), + (_MsgTok.ANY, 'reason'))), + + _MsgParseExpectation(_MsgTok.NONE, + 'PING', + ((_MsgTok.ANY, 'reply'),)), + + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), + 'PRIVMSG', + ((_MsgTok.NICKNAME, 'nickname'), + (_MsgTok.ANY, 'message'))), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), + 'PRIVMSG', + ((_MsgTok.CHANNEL, 'channel'), + (_MsgTok.ANY, 'message'))), + + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'quitter'), + 'QUIT', + ((_MsgTok.ANY, 'message'),)), ) @@ -565,15 +572,6 @@ class _ClientDb(_Db, SharedClientDbFields): 'Return part of channels dictionary for channels user is currently in.' return {k: v for k, v in self._channels.items() if user in v.users} - def process_isupport(self, params: tuple[str, ...]) -> None: - 'Process 005 RPL_ISUPPORT params into dictionary updates.' - for param in params[1:-1]: - toks = param.split('=', maxsplit=1) - if toks[0][0] == '-': - del self.isupports[toks[0][1:]] - else: - self.isupports[toks[0]] = toks[1] if len(toks) > 1 else '' - @property def nick_incremented(self) -> str: 'Return .nick_wanted with number suffix incremented, or "0" if none.' @@ -675,6 +673,38 @@ class Client(ABC, ClientQueueMixin): def _match_msg(self, msg: IrcMessage, verb: str): 'Test .source, .verb, .params.' + tok_type = str | tuple[str, ...] | dict[str, str | _ChannelDb] + + def param_match(ex_tok: str | _MsgTok, msg_tok: str | list[str] + ) -> Optional[tok_type | tuple[tok_type, ...]]: + if isinstance(msg_tok, list): + to_return = [] + for item in msg_tok: + result = param_match(ex_tok, item) + if not isinstance(result, tok_type): + return None + to_return += [result] + return tuple(to_return) + if ex_tok is _MsgTok.NONE: + return msg_tok if msg_tok == '' else None + if ex_tok is _MsgTok.SERVER: + return msg_tok if ('.' in msg_tok + 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 ex_tok is _MsgTok.NICKNAME: + return msg_tok if msg_tok[0] not in '~&@%+# ' else None + if ex_tok is _MsgTok.USER_ADDRESS: + toks = msg_tok.split('!') + if len(toks) != 2: + return None + toks = toks[0:1] + toks[1].split('@') + return tuple(toks) if len(toks) == 3 else None + if ex_tok is _MsgTok.LIST: + return tuple(msg_tok.split()) + return msg_tok + for ex in [ex for ex in _EXPECTATIONS if verb == ex.verb == msg.verb]: if not ex.params: len_p = len(msg.params) @@ -686,40 +716,25 @@ class Client(ABC, ClientQueueMixin): continue to_return: dict[str, Any] = {'': ''} # non-emtpy so boolish True ex_tok_fields = tuple([ex.source] + list(ex.params)) - msg_tok_fields = tuple([msg.source] + list(msg.params)) + msg_params: list[str | list[str]] + if ex.idx_into_list < 0: + msg_params = list(msg.params) + else: + idx_remainders = len(msg.params) + 1 - (len(ex.params) + - ex.idx_into_list) + msg_params = list(msg.params[:ex.idx_into_list])\ + + [list(msg.params[ex.idx_into_list:idx_remainders])]\ + + list(msg.params[idx_remainders:]) + msg_tok_fields = tuple([msg.source] + msg_params) if ex.params and len(ex_tok_fields) != len(msg_tok_fields): continue - passing = True + passing = False for idx, ex_tok in enumerate(ex_tok_fields): - passing = False ex_tok, key = ((ex_tok[0], ex_tok[1]) if isinstance(ex_tok, tuple) else (ex_tok, '')) - val_to_ret: str | tuple[str, ...] | dict[str, str | _ChannelDb] - val_to_ret = msg_tok = msg_tok_fields[idx] - if ex_tok is _MsgTok.NONE and msg_tok != '': + to_return[key] = param_match(ex_tok, msg_tok_fields[idx]) + if to_return[key] is None: break - if ex_tok is _MsgTok.SERVER\ - and ('.' not in msg_tok or set('@!') & set(msg_tok)): - break - if ex_tok is _MsgTok.CHANNEL: - if msg_tok[0] != '#': - break - val_to_ret = {'id': msg_tok, 'db': self._db.chan(msg_tok)} - elif ex_tok is _MsgTok.NICKNAME: - if msg_tok[0] in '~&@%+# *': - break - elif ex_tok is _MsgTok.USER_ADDRESS: - toks = msg_tok.split('!') - if len(toks) != 2: - break - toks = toks[0:1] + toks[1].split('@') - if len(toks) != 3: - break - val_to_ret = tuple(toks) - elif ex_tok is _MsgTok.LIST: - val_to_ret = tuple(msg_tok.split()) - if key: - to_return[key] = val_to_ret passing = True if passing: return to_return @@ -732,8 +747,15 @@ class Client(ABC, ClientQueueMixin): self.set_nick(msg.params[0], confirmed=True) if _NumericsToIgnore.contain(msg.verb): return - if self._match_msg(msg, '005'): # RPL_ISUPPORT - self._db.process_isupport(msg.params[1:-1]) + + if (ret := self._match_msg(msg, '005')): # RPL_ISUPPORT + for item in ret['isupports']: + toks = item.split('=', maxsplit=1) + if toks[0][0] == '-': + del self._db.isupports[toks[0][1:]] + else: + self._db.isupports[toks[0]] = (toks[1] if len(toks) > 1 + else '') elif (ret := self._match_msg(msg, '353')): # RPL_NAMREPLY for name in ret['names']: