From 8bf5cf42b90ea68fc48b0df38b76a1520522226f Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Thu, 21 Aug 2025 01:17:20 +0200 Subject: [PATCH] Re-organize message matching away from previous Numerics collections. --- ircplom/client.py | 442 ++++++++++++++++++++++++++-------------------- 1 file changed, 247 insertions(+), 195 deletions(-) diff --git a/ircplom/client.py b/ircplom/client.py index 2d5bd3d..44c7b0a 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -16,28 +16,6 @@ from ircplom.irc_conn import (BaseIrcConnection, IrcConnAbortException, ClientsDb = dict[str, 'Client'] _NAMES_DESIRED_SERVER_CAPS = ('sasl',) -# NB: in below numerics accounting, tuples define inclusive ranges -_NUMERICS_TO_CONFIRM_NICKNAME = ( - (1, 5), - (251, 255), - (265, 266), - 353, - 366, - 372, - (375, 376), - 396, - 401, - 900, - 903, - 904 -) -_NUMERICS_TO_IGNORE = ( - (1, 4), # nothing in this login info we're interested in - (250, 255), # same - (265, 266), # same - 375, # RPL_MOTDSTART already implied to us by first appearance of 372 -) - class _MsgTok(Enum): ANY = auto() @@ -59,189 +37,306 @@ class _MsgParseExpectation(NamedTuple): idx_into_list: int = -1 -_EXPECTATIONS: tuple[_MsgParseExpectation, ...] = ( +_EXPECTATIONS: list[_MsgParseExpectation] = [] +# these we ignore except for confirming/collecting the nickname +_EXPECTATIONS += [ _MsgParseExpectation(_MsgTok.SERVER, - '005', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'isupports'), - _MsgTok.ANY), - idx_into_list=1), - + '001', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), _MsgParseExpectation(_MsgTok.SERVER, - '353', - (_MsgTok.NICKNAME, - '=', - (_MsgTok.CHANNEL, 'channel'), - (_MsgTok.LIST, 'names'))), + '002', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), _MsgParseExpectation(_MsgTok.SERVER, - '366', - (_MsgTok.NICKNAME, - (_MsgTok.CHANNEL, 'channel'), + '003', + ((_MsgTok.NICKNAME, 'my_nickname'), _MsgTok.ANY)), - _MsgParseExpectation(_MsgTok.SERVER, - '372', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'line'))), + '004', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY, + _MsgTok.ANY, + _MsgTok.ANY, + _MsgTok.ANY, + _MsgTok.ANY)), _MsgParseExpectation(_MsgTok.SERVER, - '376', - (_MsgTok.NICKNAME, + '250', + ((_MsgTok.NICKNAME, 'my_nickname'), _MsgTok.ANY)), - - _MsgParseExpectation(_MsgTok.SERVER, '396', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'host_maybe_w_user'), + _MsgParseExpectation(_MsgTok.SERVER, + '251', + ((_MsgTok.NICKNAME, 'my_nickname'), _MsgTok.ANY)), - _MsgParseExpectation(_MsgTok.SERVER, - '401', - (_MsgTok.NICKNAME, - (_MsgTok.NICKNAME, 'target'), + '252', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY, _MsgTok.ANY)), - _MsgParseExpectation(_MsgTok.SERVER, - '432', - ('*', - _MsgTok.NICKNAME, + '253', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY, _MsgTok.ANY)), _MsgParseExpectation(_MsgTok.SERVER, - '432', - ((_MsgTok.NICKNAME, 'fallback'), - _MsgTok.NICKNAME, + '254', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY, + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '255', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '265', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '265', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY, + _MsgTok.ANY, + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '266', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '266', + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY, + _MsgTok.ANY, _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '375', # RPL_MOTDSTART already implied by 1st 372 + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), +] +# various login stuff +_EXPECTATIONS += [ _MsgParseExpectation(_MsgTok.SERVER, - '433', - (_MsgTok.NICKNAME, - _MsgTok.NICKNAME, + '005', # RPL_ISUPPORT + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.ANY, 'isupports'), + _MsgTok.ANY), + idx_into_list=1), + _MsgParseExpectation(_MsgTok.SERVER, + '372', # RPL_MOTD + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.ANY, 'line'))), + _MsgParseExpectation(_MsgTok.SERVER, + '376', # RPL_ENDOFMOTD + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '396', # RPL_VISIBLEHOST + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.ANY, 'host_maybe_w_user'), _MsgTok.ANY)), +] +# SASL +_EXPECTATIONS += [ _MsgParseExpectation(_MsgTok.SERVER, - '900', - (_MsgTok.NICKNAME, + '900', # RPL_LOGGEDIN + ((_MsgTok.NICKNAME, 'my_nickname'), (_MsgTok.USER_ADDRESS, 'full_address'), (_MsgTok.ANY, 'account'), _MsgTok.ANY)), _MsgParseExpectation(_MsgTok.SERVER, - '903', - (_MsgTok.NICKNAME, + '903', # RPL_SASLSUCCESS + ((_MsgTok.NICKNAME, 'my_nickname'), (_MsgTok.ANY, 'result'))), _MsgParseExpectation(_MsgTok.SERVER, - '904', - (_MsgTok.NICKNAME, + '904', # ERR_SASLFAIL + ((_MsgTok.NICKNAME, 'my_nickname'), (_MsgTok.ANY, 'result'))), - _MsgParseExpectation(_MsgTok.NONE, 'AUTHENTICATE', ('+',)), +] +# capability negotation +_EXPECTATIONS += [ _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, - ('NEW', 'verb'), + ((_MsgTok.NICKNAME, 'my_nickname'), + ('NEW', 'subverb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, - ('DEL', 'verb'), + ((_MsgTok.NICKNAME, 'my_nickname'), + ('DEL', 'subverb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, - ('ACK', 'verb'), + ('*', + ('ACK', 'subverb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, - ('NAK', 'verb'), + ((_MsgTok.NICKNAME, 'my_nickname'), + ('ACK', 'subverb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', ('*', - ('LS', 'verb'), - ('*', 'tbc'), + ('NAK', 'subverb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, - ('LS', 'verb'), + ((_MsgTok.NICKNAME, 'my_nickname'), + ('NAK', 'subverb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, + ('*', + ('LS', 'subverb'), + (_MsgTok.LIST, 'items'))), + _MsgParseExpectation(_MsgTok.SERVER, + 'CAP', + ('*', + ('LS', 'subverb'), ('*', 'tbc'), - ('LIST', 'verb'), (_MsgTok.LIST, 'items'))), _MsgParseExpectation(_MsgTok.SERVER, 'CAP', - (_MsgTok.NICKNAME, - ('LIST', 'verb'), + ((_MsgTok.NICKNAME, 'my_nickname'), + ('LS', 'subverb'), + (_MsgTok.LIST, 'items'))), + _MsgParseExpectation(_MsgTok.SERVER, + 'CAP', + ((_MsgTok.NICKNAME, 'my_nickname'), + ('LS', 'subverb'), + ('*', 'tbc'), (_MsgTok.LIST, 'items'))), - _MsgParseExpectation(_MsgTok.NONE, - 'ERROR', - ((_MsgTok.ANY, 'reason'),)), - - _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'joiner'), - 'JOIN', - ((_MsgTok.CHANNEL, 'channel'),)), + _MsgParseExpectation(_MsgTok.SERVER, + 'CAP', + ('*', + ('LIST', 'subverb'), + (_MsgTok.LIST, 'items'))), + _MsgParseExpectation(_MsgTok.SERVER, + 'CAP', + ('*', + ('LIST', 'subverb'), + ('*', 'tbc'), + (_MsgTok.LIST, 'items'))), + _MsgParseExpectation(_MsgTok.SERVER, + 'CAP', + ((_MsgTok.NICKNAME, 'my_nickname'), + ('LIST', 'subverb'), + (_MsgTok.LIST, 'items'))), + _MsgParseExpectation(_MsgTok.SERVER, + 'CAP', + ((_MsgTok.NICKNAME, 'my_nickname'), + ('LIST', 'subverb'), + ('*', 'tbc'), + (_MsgTok.LIST, 'items'))), +] - _MsgParseExpectation(_MsgTok.NICKNAME, - 'MODE', - (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'mode'))), - _MsgParseExpectation(_MsgTok.USER_ADDRESS, - 'MODE', +# nickname management +_EXPECTATIONS += [ + _MsgParseExpectation(_MsgTok.SERVER, + '432', # ERR_ERRONEOUSNICKNAME + ('*', + _MsgTok.NICKNAME, + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '432', # ERR_ERRONEOUSNICKNAME + ((_MsgTok.NICKNAME, 'my_nickname'), + _MsgTok.NICKNAME, + _MsgTok.ANY)), + _MsgParseExpectation(_MsgTok.SERVER, + '433', # ERR_NICKNAMEINUSE (_MsgTok.NICKNAME, - (_MsgTok.ANY, 'mode'))), - + _MsgTok.NICKNAME, + _MsgTok.ANY)), _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'named'), 'NICK', ((_MsgTok.NICKNAME, 'nickname'),)), +] +# joining/leaving +_EXPECTATIONS += [ + _MsgParseExpectation(_MsgTok.SERVER, + '353', # RPL_NAMREPLY + ((_MsgTok.NICKNAME, 'my_nickname'), + '=', + (_MsgTok.CHANNEL, 'channel'), + (_MsgTok.LIST, 'names'))), + _MsgParseExpectation(_MsgTok.SERVER, + '366', # RPL_ENDOFNAMES + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.CHANNEL, 'channel'), + _MsgTok.ANY)), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'joiner'), + 'JOIN', + ((_MsgTok.CHANNEL, 'channel'),)), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'), + 'PART', + ((_MsgTok.CHANNEL, 'channel'),)), + _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'parter'), + 'PART', + ((_MsgTok.CHANNEL, 'channel'), + (_MsgTok.ANY, 'reason'))), +] + +# messaging +_EXPECTATIONS += [ + _MsgParseExpectation(_MsgTok.SERVER, + '401', # ERR_NOSUCKNICK + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.NICKNAME, 'target'), + _MsgTok.ANY)), _MsgParseExpectation(_MsgTok.SERVER, 'NOTICE', ('*', (_MsgTok.ANY, 'message'))), _MsgParseExpectation(_MsgTok.SERVER, 'NOTICE', - ((_MsgTok.NICKNAME, 'nickname'), + ((_MsgTok.NICKNAME, 'my_nickname'), (_MsgTok.ANY, 'message'))), _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), 'NOTICE', - ((_MsgTok.NICKNAME, 'nickname'), + ((_MsgTok.NICKNAME, 'my_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.NICKNAME, 'my_nickname'), (_MsgTok.ANY, 'message'))), _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'sender'), 'PRIVMSG', ((_MsgTok.CHANNEL, 'channel'), (_MsgTok.ANY, 'message'))), +] +# misc. +_EXPECTATIONS += [ + _MsgParseExpectation(_MsgTok.NONE, + 'ERROR', + ((_MsgTok.ANY, 'reason'),)), + _MsgParseExpectation(_MsgTok.NICKNAME, + 'MODE', + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.ANY, 'mode'))), + _MsgParseExpectation(_MsgTok.USER_ADDRESS, + 'MODE', + ((_MsgTok.NICKNAME, 'my_nickname'), + (_MsgTok.ANY, 'mode'))), + _MsgParseExpectation(_MsgTok.NONE, + 'PING', + ((_MsgTok.ANY, 'reply'),)), _MsgParseExpectation((_MsgTok.USER_ADDRESS, 'quitter'), 'QUIT', ((_MsgTok.ANY, 'message'),)), -) +] class LogScope(Enum): @@ -253,24 +348,6 @@ class LogScope(Enum): SAME = auto() -class _Numerics: - - def __init__(self, numerics: tuple[int | tuple[int, int], ...]) -> None: - as_ints = [] - for item in numerics: - as_ints += ([item] if isinstance(item, int) - else list(range(item[0], item[1] + 1))) - self._numerics = tuple(as_ints) - - def contain(self, msg_verb: str) -> bool: - 'Is int(msg_verb) within the listed numbers?' - return msg_verb.isdigit() and int(msg_verb) in self._numerics - - -_NumericsToIgnore = _Numerics(_NUMERICS_TO_IGNORE) -_NumericsToConfirmNickname = _Numerics(_NUMERICS_TO_CONFIRM_NICKNAME) - - @dataclass class ClientIdMixin: 'Collects a Client\'s ID at .client_id.' @@ -709,7 +786,7 @@ class Client(ABC, ClientQueueMixin): self._log(f'connection broken: {e}', alert=True) self.close() - def _match_msg(self, msg: IrcMessage, verb: str): + def _match_msg(self, msg: IrcMessage) -> dict[str, Any]: 'Test .source, .verb, .params.' tok_type = str | tuple[str, ...] | dict[str, str | _ChannelDb] @@ -743,8 +820,8 @@ class Client(ABC, ClientQueueMixin): return tuple(msg_tok.split()) return msg_tok - for ex in [ex for ex in _EXPECTATIONS if verb == ex.verb == msg.verb]: - to_return: dict[str, Any] = {'': ''} # non-emtpy so boolish True + for ex in [ex for ex in _EXPECTATIONS if ex.verb == msg.verb]: + to_return: dict[str, Any] = {'verb': ex.verb} ex_tok_fields = tuple([ex.source] + list(ex.params)) msg_params: list[str | list[str]] if ex.idx_into_list < 0: @@ -758,27 +835,27 @@ class Client(ABC, ClientQueueMixin): msg_tok_fields = tuple([msg.source] + msg_params) if ex.params and len(ex_tok_fields) != len(msg_tok_fields): continue - passing = False + passing = True for idx, ex_tok in enumerate(ex_tok_fields): ex_tok, key = ((ex_tok[0], ex_tok[1]) if isinstance(ex_tok, tuple) else (ex_tok, '')) to_return[key] = param_match(ex_tok, msg_tok_fields[idx]) if to_return[key] is None: + passing = False break - passing = True if passing: return to_return - return False + return {} def handle_msg(self, msg: IrcMessage) -> None: 'Log msg.raw, then process incoming msg into appropriate client steps.' self._log(msg.raw, scope=LogScope.RAW, out=False) - if _NumericsToConfirmNickname.contain(msg.verb): - self.set_nick(msg.params[0], confirmed=True) - if _NumericsToIgnore.contain(msg.verb): - return - - if (ret := self._match_msg(msg, '005')): # RPL_ISUPPORT + ret = self._match_msg(msg) + if 'my_nickname' in ret: + self.set_nick(ret['my_nickname'], confirmed=True) + if 'verb' not in ret: + self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}') + elif ret['verb'] == '005': # RPL_ISUPPORT for item in ret['isupports']: toks = item.split('=', maxsplit=1) if toks[0][0] == '-': @@ -786,59 +863,48 @@ class Client(ABC, ClientQueueMixin): else: self._db.isupports[toks[0]] = (toks[1] if len(toks) > 1 else '') - - elif (ret := self._match_msg(msg, '353')): # RPL_NAMREPLY + elif ret['verb'] == '353': # RPL_NAMREPLY for name in ret['names']: ret['channel']['db'].append_completable('users', name.lstrip('~&@%+')) - elif (ret := self._match_msg(msg, '366')): # RPL_ENDOFNAMES + elif ret['verb'] == '366': # RPL_ENDOFNAMES ret['channel']['db'].declare_complete('users') - - elif (ret := self._match_msg(msg, '372')): # RPL_MOTD + elif ret['verb'] == '372': # RPL_MOTD self._db.append_completable('motd', ret['line']) - elif self._match_msg(msg, '376'): # RPL_ENDOFMOTD + elif ret['verb'] == '376': # RPL_ENDOFMOTD self._db.declare_complete('motd') - - elif (ret := self._match_msg(msg, '396')): # RPL_VISIBLEHOST + elif ret['verb'] == '396': # RPL_VISIBLEHOST # '@'-split because # claims: " can also be in the form " self._db.client_host = ret['host_maybe_w_user'].split('@')[-1] - - elif (ret := self._match_msg(msg, '401')): # ERR_NOSUCHNICK + elif ret['verb'] == '401': # ERR_NOSUCHNICK self._log(f'{ret["target"]} not online', scope=LogScope.CHAT, target=ret['target'], alert=True) - - elif (ret := self._match_msg(msg, '432')): # ERR_ERRONEOUSNICKNAME + elif ret['verb'] == '432': # ERR_ERRONEOUSNICKNAME alert = 'nickname refused for bad format' - if 'fallback' in ret: - self.set_nick(ret['fallback'], confirmed=True) - else: + if 'my_nickname' not in ret: alert += ', giving up' self.close() self._log(alert, alert=True) - - elif self._match_msg(msg, '433'): # ERR_NICKNAMEINUSE + elif ret['verb'] == '433': # ERR_NICKNAMEINUSE self._log('nickname already in use, trying increment', alert=True) self.set_nick(self._db.nick_incremented) - - elif (ret := self._match_msg(msg, '900')): # RPL_LOGGEDIN + elif ret['verb'] == '900': # RPL_LOGGEDIN self.set_nick(ret['full_address'][0], True) self._db.username, self._db.client_host = ret['full_address'][1:] self._db.sasl_account = ret['account'] - elif ((ret := self._match_msg(msg, '903')) # RPL_SASLSUCCESS - or (ret := self._match_msg(msg, '904'))): # ERR_SASLFAIL + elif ret['verb'] in {'903', '904'}: # RPL_SASLSUCCESS, ERR_SASLFAIL self._db.sasl_auth_state = ret['result'] self._caps.end_negotiation() - - elif self._match_msg(msg, 'AUTHENTICATE'): + elif ret['verb'] == 'AUTHENTICATE': auth = b64encode((self._db.nick_wanted + '\0' + self._db.nick_wanted + '\0' + self._db.password ).encode('utf-8')).decode('utf-8') self.send(IrcMessage('AUTHENTICATE', (auth,))) - - elif (ret := self._match_msg(msg, 'CAP')): - if (self._caps.process_msg(ret['verb'], ret['items'], 'tbc' in ret) + elif ret['verb'] == 'CAP': + if (self._caps.process_msg(verb=ret['subverb'], items=ret['items'], + incomplete='tbc' in ret) and self._db.caps.has('sasl') and 'PLAIN' in self._db.caps['sasl'].data.split(',')): if self._db.password: @@ -846,23 +912,19 @@ class Client(ABC, ClientQueueMixin): self.send(IrcMessage('AUTHENTICATE', ('PLAIN',))) else: self._caps.end_negotiation() - - elif (ret := self._match_msg(msg, 'ERROR')): + elif ret['verb'] == 'ERROR': self._db.connection_state = ret['reason'] self.close() - - elif (ret := self._match_msg(msg, 'JOIN')): + elif ret['verb'] == 'JOIN': self._log(f'{ret["joiner"][0]} {msg.verb.lower()}s ' + f'{ret["channel"]["id"]}', scope=LogScope.CHAT, target=ret['channel']['id']) if ret['joiner'][0] != self._db.nickname: ret['channel']['db'].append_completable('users', ret['joiner'][0], True) - - elif (ret := self._match_msg(msg, 'MODE')): + elif ret['verb'] == 'MODE': self._db.user_modes = ret['mode'] - - elif (ret := self._match_msg(msg, 'NICK')): + elif ret['verb'] == 'NICK': if ret['named'][0] == self._db.nickname: self.set_nick(ret['nickname'], confirmed=True) else: @@ -871,20 +933,15 @@ class Client(ABC, ClientQueueMixin): ch.append_completable('users', ret['nickname'], True) self._log(f'{ret["named"][0]} becomes {ret["nickname"]}', scope=LogScope.CHAT, target=id_) - - elif (ret := self._match_msg(msg, 'NOTICE'))\ - or (ret := self._match_msg(msg, 'PRIVMSG')): - if 'nickname' in ret: - self.set_nick(ret['nickname'], confirmed=True) + elif ret['verb'] in {'NOTICE', 'PRIVMSG'}: kw: dict[str, bool | str | LogScope] = { 'as_notice': msg.verb == 'NOTICE'} if 'sender' in ret: # not just server message kw |= {'sender': ret['sender'][0], 'scope': LogScope.CHAT, - 'target': (ret['sender'][0] if 'nickname' in ret + 'target': (ret['sender'][0] if 'my_nickname' in ret else ret['channel']['id'])} self._log(ret['message'], out=False, **kw) - - elif (ret := self._match_msg(msg, 'PART')): + elif ret['verb'] == 'PART': reason = f': {ret["reason"]}' if 'reason' in ret else '' self._log(f'{ret["parter"][0]} {msg.verb.lower()}s ' + f'{ret["channel"]["id"]}{reason}', @@ -894,19 +951,14 @@ class Client(ABC, ClientQueueMixin): else: ret['channel']['db'].remove_completable('users', ret['parter'][0], True) - - elif (ret := self._match_msg(msg, 'PING')): + elif ret['verb'] == 'PING': self.send(IrcMessage(verb='PONG', params=(ret['reply'],))) - - elif (ret := self._match_msg(msg, 'QUIT')): + elif ret['verb'] == 'QUIT': for id_, chan in self._db.chans_of_user(ret['quitter'][0]).items(): chan.remove_completable('users', ret['quitter'][0], True) self._log(f'{ret["quitter"][0]} quits: {ret["message"]}', LogScope.CHAT, target=id_) - else: - self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}') - def set_nick(self, nickname: str, confirmed=False) -> None: 'Set ClientDb\'s .nick wanted, send NICK if != .nickname.' if confirmed: -- 2.30.2