From 8f56b28d11dea961010327976150d84d9018af1f Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Tue, 19 Aug 2025 08:22:24 +0200 Subject: [PATCH] Start moving params parsing out of .handle_msg. --- ircplom/client.py | 78 +++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/ircplom/client.py b/ircplom/client.py index df36cac..7736ddf 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -45,11 +45,16 @@ class _MsgSource(Enum): SERVER = auto() +class _MsgParam(Enum): + ANY = auto() + CHANNEL = auto() + + class _MsgParseExpectation(NamedTuple): verb: str len_min_params: int = 0 len_max_params: int = 0 - params: tuple[str, ...] = tuple() + params: tuple[str | _MsgParam, ...] = tuple() source: Optional[_MsgSource] = None @@ -68,17 +73,24 @@ _EXPECTATIONS: tuple[_MsgParseExpectation, ...] = ( _MsgParseExpectation('904', 2, source=_MsgSource.SERVER), _MsgParseExpectation('AUTHENTICATE', params=('+',), source=_MsgSource.NONE), _MsgParseExpectation('CAP', 3, 15, source=_MsgSource.SERVER), - _MsgParseExpectation('ERROR', 1, source=_MsgSource.NONE), - _MsgParseExpectation('JOIN', 1, source=_MsgSource.USER_ADDRESS), + _MsgParseExpectation('ERROR', params=(_MsgParam.ANY,), + source=_MsgSource.NONE), + _MsgParseExpectation('JOIN', params=(_MsgParam.CHANNEL,), + source=_MsgSource.USER_ADDRESS), _MsgParseExpectation('MODE', 2, source=_MsgSource.USER_ADDRESS), _MsgParseExpectation('NICK', 1, source=_MsgSource.USER_ADDRESS), _MsgParseExpectation('NOTICE', 2, source=_MsgSource.USER_ADDRESS), _MsgParseExpectation('NOTICE', 2, source=_MsgSource.SERVER), - _MsgParseExpectation('PART', 1, 2, source=_MsgSource.USER_ADDRESS), - _MsgParseExpectation('PING', 1, source=_MsgSource.NONE), + _MsgParseExpectation('PART', params=(_MsgParam.CHANNEL,), + source=_MsgSource.USER_ADDRESS), + _MsgParseExpectation('PART', params=(_MsgParam.CHANNEL, _MsgParam.ANY), + source=_MsgSource.USER_ADDRESS), + _MsgParseExpectation('PING', params=(_MsgParam.ANY,), + source=_MsgSource.NONE), _MsgParseExpectation('PRIVMSG', 2, source=_MsgSource.USER_ADDRESS), _MsgParseExpectation('PRIVMSG', 2, source=_MsgSource.SERVER), - _MsgParseExpectation('QUIT', 1, source=_MsgSource.USER_ADDRESS), + _MsgParseExpectation('QUIT', params=(_MsgParam.ANY,), + source=_MsgSource.USER_ADDRESS), ) @@ -564,10 +576,8 @@ class Client(ABC, ClientQueueMixin): def _match_msg(self, msg: _IrcMsg, verb: str): 'Test .source, .verb, .params.' - to_return: dict[str, Optional[str]] = {'_': None} - if not msg.verb == verb: - return False - for expect in [x for x in _EXPECTATIONS if x.verb == msg.verb]: + for expect in [x for x in _EXPECTATIONS if verb == x.verb == msg.verb]: + to_return: dict[str, Any] = {'params': []} if expect.source is _MsgSource.NONE: if msg.source != '': continue @@ -586,10 +596,22 @@ class Client(ABC, ClientQueueMixin): if len(toks) != 3: continue to_return['nickname'] = toks[0] - if expect.params and msg.params != expect.params: - continue n_len_params = len(msg.params) - if expect.len_max_params: + if expect.params: + if n_len_params != len(expect.params): + continue + for idx, exp_param in enumerate(expect.params): + param = msg.params[idx] + if isinstance(exp_param, str) and exp_param != param: + continue + if exp_param is _MsgParam.CHANNEL: + if param[0] != '#': + continue + to_return['ch_name'] = param + to_return['channel'] = self._db.chan(param) + if exp_param is _MsgParam.ANY: + to_return['params'] += [param] + elif expect.len_max_params: if not (expect.len_min_params <= n_len_params <= expect.len_max_params): continue @@ -662,12 +684,11 @@ class Client(ABC, ClientQueueMixin): elif self._match_msg(msg, 'ERROR'): self.close() elif (ret := self._match_msg(msg, 'JOIN')): - channel = msg.params[0] - log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {channel}' - self._log(log_msg, scope=LogScope.CHAT, target=channel) + log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {ret["ch_name"]}' + self._log(log_msg, scope=LogScope.CHAT, target=ret['ch_name']) if ret['nickname'] != self._db.nickname: - self._db.chan(channel).append_completable( - 'users', ret['nickname'], stay_complete=True) + ret['channel'].append_completable('users', ret['nickname'], + stay_complete=True) elif self._match_msg(msg, 'MODE')\ and msg.params[0] == self._db.nickname: self._db.user_modes = msg.params[1] @@ -685,25 +706,24 @@ class Client(ABC, ClientQueueMixin): } if 'nickname' in ret else {} self._log(msg.params[-1], out=False, target=msg.params[0], **kw) elif (ret := self._match_msg(msg, 'PART')): - channel = msg.params[0] - log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {channel}' - if len(msg.params) == 2: - log_msg += f': {msg.params[1]}' - self._log(log_msg, scope=LogScope.CHAT, target=channel) + log_msg = f'{ret["nickname"]} {msg.verb.lower()}s {ret["ch_name"]}' + if len(ret['params']): + log_msg += f': {ret["params"][0]}' + self._log(log_msg, scope=LogScope.CHAT, target=ret['ch_name']) if ret['nickname'] == self._db.nickname: - self._db.del_chan(channel) + self._db.del_chan(ret['ch_name']) else: - self._db.chan(channel).remove_completable( - 'users', ret['nickname'], stay_complete=True) - elif self._match_msg(msg, 'PING'): - self.send(IrcMessage(verb='PONG', params=(msg.params[0],))) + ret['channel'].remove_completable('users', ret['nickname'], + stay_complete=True) + elif (ret := self._match_msg(msg, 'PING')): + self.send(IrcMessage(verb='PONG', params=tuple(ret['params']))) elif (ret := self._match_msg(msg, 'QUIT')): for chan_name in self._db.chan_names: chan = self._db.chan(chan_name) if ret['nickname'] in chan.users: chan.remove_completable('users', ret['nickname'], stay_complete=True) - self._log(f'{ret["nickname"]} quits: {msg.params[0]}', + self._log(f'{ret["nickname"]} quits: {ret["params"][0]}', scope=LogScope.CHAT, target=chan_name) else: self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}') -- 2.30.2