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'),)),
)
'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.'
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)
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
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']: