from ircplom.irc_conn import (
BaseIrcConnection, IrcConnAbortException, IrcMessage,
ILLEGAL_NICK_CHARS, ILLEGAL_NICK_FIRSTCHARS, ISUPPORT_DEFAULTS, PORT_SSL)
-from ircplom.msg_parse_expectations import MsgTok, MSG_EXPECTATIONS
+from ircplom.msg_parse_expectations import MSG_EXPECTATIONS
_NAMES_DESIRED_SERVER_CAPS = ('sasl',)
else:
super().__setattr__(key, value)
+ @staticmethod
+ def possible_from(value: str) -> bool:
+ 'If class instance could be parsed from value.'
+ toks = value.split('!')
+ if not len(toks) == 2:
+ return False
+ toks = toks[1].split('@')
+ if not len(toks) == 2:
+ return False
+ return True
+
@classmethod
def from_str(cls, value: str) -> Self:
'Produce from string assumed to fit _!_@_ pattern.'
+ assert cls.possible_from(value)
toks = value.split('!')
- assert len(toks) == 2
toks = toks[0:1] + toks[1].split('@')
- assert len(toks) == 3
return cls(*toks)
@property
if id_ not in to_keep]:
del self.users[user_id]
- @property
- def illegal_nick_firstchars(self) -> str:
- 'Calculated from hardcoded constants and .isupport.'
- return (ILLEGAL_NICK_CHARS + ILLEGAL_NICK_FIRSTCHARS
- + self.isupport['CHANTYPES'] + self._get_membership_prefixes())
+ def is_nick(self, nick: str) -> bool:
+ 'Tests name to match rules for nicknames.'
+ if len(nick) == 0:
+ return False
+ if nick[0] in (ILLEGAL_NICK_FIRSTCHARS
+ + self.isupport['CHANTYPES']
+ + self._get_membership_prefixes()):
+ return False
+ for c in [c for c in nick if c in ILLEGAL_NICK_CHARS]:
+ return False
+ return True
def _get_membership_prefixes(self) -> str:
'Registered possible membership nickname prefixes.'
self._log(to_log, scope=log_target)
self._log(msg.raw, scope=LogScope.RAW, out=True)
- def _match_msg(self, msg: IrcMessage) -> dict[str, Any]:
- 'Test .source, .verb, .params.'
- tok_type = str | _NickUserHost | tuple[str, ...]
-
- 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 isinstance(ex_tok, str):
- return msg_tok if msg_tok == ex_tok else None
- 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 msg_tok if self.db.is_chan_name(msg_tok) else None
- if ex_tok is MsgTok.NICKNAME:
- 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:
- return _NickUserHost.from_str(msg_tok)
- except AssertionError:
- return None
- if ex_tok is MsgTok.LIST:
- return tuple(msg_tok.split())
- return msg_tok
-
- def into_tasks_and_key(code: str, to_ret: dict[str, Any]) -> str:
- tasks = to_ret['_tasks']
- cmds_str, key = code.split(':', maxsplit=1) if code else ('', '')
- for command in [t for t in cmds_str.split(',') if t]:
- tasks[command] = tasks.get(command, []) + [key]
- return key
-
- for ex in [ex for ex in MSG_EXPECTATIONS if ex.verb == msg.verb]:
- to_return: dict[str, Any] = {'verb': ex.verb, '_tasks': {}}
- ex_tok_fields = tuple([ex.source] + list(ex.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
- for idx, ex_tok in enumerate(ex_tok_fields):
- ex_tok, code = ((ex_tok[0], ex_tok[1])
- if isinstance(ex_tok, tuple) else (ex_tok, ''))
- key = into_tasks_and_key(code, to_return)
- to_return[key] = param_match(ex_tok, msg_tok_fields[idx])
- if to_return[key] is None:
- passing = False
- break
- if passing:
- for code in ex.bonus_tasks:
- into_tasks_and_key(code, to_return)
- return to_return
- 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)
- ret = self._match_msg(msg)
- if 'verb' not in ret:
+ ret = {}
+ for ex in [ex for ex in MSG_EXPECTATIONS if ex.verb == msg.verb]:
+ result = ex.parse_msg(
+ msg=msg,
+ is_chan_name=self.db.is_chan_name,
+ is_nick=self.db.is_nick,
+ possible_nickuserhost=_NickUserHost.possible_from,
+ into_nickuserhost=_NickUserHost.from_str)
+ if result is not None:
+ ret = result
+ break
+ if '_verb' not in ret:
self._log(f'PLEASE IMPLEMENT HANDLER FOR: {msg.raw}')
return
for task, tok_names in ret['_tasks'].items():
- task_verb, target_name = task.split('_', maxsplit=1)
- if task_verb == 'set' and target_name == 'user':
+ if task.verb == 'set' and task.path == ('user',):
for tok_name in tok_names:
self.db.user_id(ret[tok_name])
continue
- path_toks = target_name.split('.')
node = self
- for step in [t for t in path_toks if t]:
+ for step in task.path:
node = (node[ret[step] if step.isupper() else step]
if isinstance(node, Dict)
else getattr(node, step))
# FIXME: alphabetical sorting of tok_names merely hack to parse
# TOPIC messages, to ensure any setattr_topic:what be processed
# before any setattr_topic:who, i.e. properly completing .topic
- if task_verb == 'setattr':
+ if task.verb == 'setattr':
setattr(node, tok_name, ret[tok_name])
- elif task_verb == 'do':
+ elif task.verb == 'do':
getattr(node, tok_name)()
- if ret['verb'] == '005': # RPL_ISUPPORT
+ if ret['_verb'] == '005': # RPL_ISUPPORT
for item in ret['isupport']:
if item[0] == '-':
del self.db.isupport[item[1:]]
else:
key, data = _Dict.key_val_from_eq_str(item)
self.db.isupport[key] = data
- elif ret['verb'] == '353': # RPL_NAMREPLY
+ elif ret['_verb'] == '353': # RPL_NAMREPLY
self.db.channels[ret['channel']].add_from_namreply(ret['names'])
- elif ret['verb'] == '372': # RPL_MOTD
+ elif ret['_verb'] == '372': # RPL_MOTD
self.db.motd.append(ret['line'])
- elif ret['verb'] == '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['verb'] == '432': # ERR_ERRONEOUSNICKNAME
+ elif ret['_verb'] == '432': # ERR_ERRONEOUSNICKNAME
alert = 'nickname refused for bad format'
if 'nick' not in ret:
alert += ', giving up'
self.close()
self._log(alert, alert=True)
- elif ret['verb'] == '433': # ERR_NICKNAMEINUSE
+ elif ret['_verb'] == '433': # ERR_NICKNAMEINUSE
self._log('nickname already in use, trying increment', alert=True)
self.send(IrcMessage(
'NICK', (_NickUserHost(nick=ret['used']).incremented,)))
- elif ret['verb'] == '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['verb'] == 'CAP':
+ elif ret['_verb'] == 'CAP':
if (self.caps.process_msg(verb=ret['subverb'], items=ret['items'],
complete='tbc' not in ret)
and 'sasl' in self.db.caps.keys()
self.send(IrcMessage('AUTHENTICATE', ('PLAIN',)))
else:
self.caps.end_negotiation()
- elif ret['verb'] == 'JOIN'\
+ elif ret['_verb'] == 'JOIN'\
and ret['joiner'].nick != self.db.users['me'].nick:
self.db.channels[ret['channel']].append_nick(ret['joiner'])
- elif ret['verb'] == 'NICK':
+ elif ret['_verb'] == 'NICK':
user_id = self.db.user_id(ret['named'])
self.db.users[user_id].nick = ret['nick']
if user_id == 'me':
self.db.nick_wanted = ret['nick']
- elif ret['verb'] in {'NOTICE', 'PRIVMSG'}:
+ 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
'target': (ret['sender'].nick if 'nick' in ret
else ret['channel'])}
self._log(ret['message'], out=False, **kw)
- elif ret['verb'] == 'PART':
+ elif ret['_verb'] == 'PART':
self.db.channels[ret['channel']].remove_nick(ret['parter'])
if 'message' in ret:
self._log(f'{ret["parter"]} parts: {ret["message"]}',
LogScope.CHAT, target=ret['channel'])
if ret['parter'] == self.db.users['me']:
del self.db.channels[ret['channel']]
- elif ret['verb'] == 'PING':
+ elif ret['_verb'] == 'PING':
self.send(IrcMessage(verb='PONG', params=(ret['reply'],)))
- elif ret['verb'] == 'QUIT':
+ elif ret['_verb'] == 'QUIT':
for ch_name, ch in self.db.chans_of_user(ret['quitter']).items():
ch.remove_nick(ret['quitter'])
self._log(f'{ret["quitter"]} quits: {ret["message"]}',
'Structured expectations and processing hints for server messages.'
from enum import Enum, auto
-from typing import NamedTuple
+from typing import Any, Callable, NamedTuple, Optional, Self
+from ircplom.irc_conn import IrcMessage
-class MsgTok(Enum):
+class _MsgTok(Enum):
'Server message token classifications.'
ANY = auto()
CHANNEL = auto()
NICK_USER_HOST = auto()
-_MsgTokGuide = str | MsgTok | tuple[str | MsgTok, str]
-
-
-class _MsgParseExpectation(NamedTuple):
- verb: str
- source: _MsgTokGuide
- params: tuple[_MsgTokGuide, ...] = tuple()
- idx_into_list: int = -1
- bonus_tasks: tuple[str, ...] = tuple()
+_MsgTokGuide = str | _MsgTok | tuple[str | _MsgTok, str]
+
+
+class _Command:
+
+ def __init__(self, input_: str) -> None:
+ self.verb, path_str = input_.split('_', maxsplit=1)
+ self.path = tuple(path_str.split('.'))
+
+ def __str__(self) -> str:
+ return f'{self.verb}_{".".join(self.path)}'
+
+ def __hash__(self) -> int:
+ return hash(str(self))
+
+ def __eq__(self, other) -> bool:
+ return hash(self) == hash(other)
+
+
+class _MsgParseExpectation:
+
+ def __init__(self,
+ verb: str,
+ source: _MsgTokGuide,
+ params: tuple[_MsgTokGuide, ...] = tuple(),
+ idx_into_list: int = -1,
+ bonus_tasks: tuple[str, ...] = tuple()
+ ) -> None:
+
+ class _Code(NamedTuple):
+ title: str
+ commands: tuple[_Command, ...]
+
+ @classmethod
+ def from_(cls, input_: str) -> Self:
+ 'Split by ":" into commands (further split by ","), title.'
+ toks = input_.split(':', maxsplit=1)
+ title = toks[1]
+ commands = [_Command(t) for t in toks[0].split(',') if t]
+ return cls(title, tuple(commands))
+
+ class _TokExpectation(NamedTuple):
+ type_: _MsgTok | str
+ code: Optional[_Code]
+
+ @classmethod
+ def from_(cls, value: _MsgTokGuide) -> Self:
+ 'Standardize value into .type_, (potentially empty) code.'
+ type_, code = ((value[0], _Code.from_(value[1]))
+ if isinstance(value, tuple)
+ else (value, None))
+ return cls(type_, code)
+
+ self.verb = verb
+ self.source = _TokExpectation.from_(source)
+ self.params = tuple(_TokExpectation.from_(param) for param in params)
+ self.idx_into_list = idx_into_list
+ self.bonus_tasks = tuple(_Code.from_(item) for item in bonus_tasks)
+
+ def parse_msg(self,
+ msg: IrcMessage,
+ is_chan_name: Callable,
+ is_nick: Callable,
+ possible_nickuserhost: Callable,
+ into_nickuserhost: Callable
+ ) -> Optional[dict[str, Any]]:
+ 'Try parsing msg into informative result dictionary, or None on fail.'
+ cmp_params: list[str | tuple[str, ...]]
+ if self.idx_into_list < 0:
+ cmp_params = list(msg.params)
+ else:
+ idx_after = len(msg.params) + 1 - (len(self.params)
+ - self.idx_into_list)
+ cmp_params = (list(msg.params[:self.idx_into_list]) +
+ [msg.params[self.idx_into_list:idx_after]] +
+ list(msg.params[idx_after:]))
+ cmp_fields = tuple([msg.source] + cmp_params)
+ ex_fields = tuple([self.source] + list(self.params))
+ if len(ex_fields) != len(cmp_fields):
+ return None
+ validators: dict[_MsgTok, Callable[[Any], bool]] = {
+ _MsgTok.NONE: lambda tok: tok == '',
+ _MsgTok.CHANNEL: is_chan_name,
+ _MsgTok.NICKNAME: is_nick,
+ _MsgTok.NICK_USER_HOST: possible_nickuserhost,
+ _MsgTok.SERVER: lambda tok: '.' in tok and not set('@!') & set(tok)
+ }
+ parsers: dict[_MsgTok, Callable[[Any], Any]] = {
+ _MsgTok.LIST: lambda tok: tuple(tok.split()),
+ _MsgTok.NICK_USER_HOST: into_nickuserhost
+ }
+ parsed: dict[str, str | tuple[str, ...]] = {}
+ singled_tasks: list[tuple[_Command, str]] = []
+ for ex_tok, cmp_tok in [(ex_tok, cmp_fields[idx])
+ for idx, ex_tok in enumerate(ex_fields)]:
+ if (not isinstance(ex_tok.type_, str))\
+ and ex_tok.type_ in validators\
+ and (not validators[ex_tok.type_](cmp_tok)):
+ return None
+ if ex_tok.code:
+ parsed[ex_tok.code.title] = (
+ cmp_tok if (isinstance(ex_tok.type_, str)
+ or ex_tok.type_ not in parsers)
+ else parsers[ex_tok.type_](cmp_tok))
+ singled_tasks += [(cmd, ex_tok.code.title)
+ for cmd in ex_tok.code.commands]
+ for code in self.bonus_tasks:
+ singled_tasks += [(cmd, code.title) for cmd in code.commands]
+ tasks: dict[_Command, list[str]] = {}
+ for cmd, title in singled_tasks:
+ if cmd not in tasks:
+ tasks[cmd] = []
+ tasks[cmd] += [title]
+ return parsed | {'_verb': self.verb, '_tasks': tasks}
MSG_EXPECTATIONS: list[_MsgParseExpectation] = [
_MsgParseExpectation(
'001', # RPL_WELCOME
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'002', # RPL_YOURHOST
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'003', # RPL_CREATED
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'004', # RPL_MYINFO
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY,
- MsgTok.ANY,
- MsgTok.ANY,
- MsgTok.ANY,
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY,
+ _MsgTok.ANY,
+ _MsgTok.ANY,
+ _MsgTok.ANY,
+ _MsgTok.ANY)),
_MsgParseExpectation(
'250', # RPL_STATSDLINE / RPL_STATSCONN
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'251', # RPL_LUSERCLIENT
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'252', # RPL_LUSEROP
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY,
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY,
+ _MsgTok.ANY)),
_MsgParseExpectation(
'253', # RPL_LUSERUNKNOWN
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY,
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY,
+ _MsgTok.ANY)),
_MsgParseExpectation(
'254', # RPL_LUSERCHANNELS
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY,
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY,
+ _MsgTok.ANY)),
_MsgParseExpectation(
'255', # RPL_LUSERME
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'265', # RPL_LOCALUSERS
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'265', # RPL_LOCALUSERS
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY,
- MsgTok.ANY,
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY,
+ _MsgTok.ANY,
+ _MsgTok.ANY)),
_MsgParseExpectation(
'266', # RPL_GLOBALUSERS
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
_MsgParseExpectation(
'266', # RPL_GLOBALUSERS
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY,
- MsgTok.ANY,
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY,
+ _MsgTok.ANY,
+ _MsgTok.ANY)),
_MsgParseExpectation(
'375', # RPL_MOTDSTART already implied by 1st 372
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY)),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY)),
# various login stuff
_MsgParseExpectation(
'005', # RPL_ISUPPORT
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, ':isupport'),
- MsgTok.ANY), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, ':isupport'),
+ _MsgTok.ANY), # comment
idx_into_list=1),
_MsgParseExpectation(
'372', # RPL_MOTD
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, ':line'))),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, ':line'))),
_MsgParseExpectation(
'376', # RPL_ENDOFMOTD
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.ANY), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.ANY), # comment
bonus_tasks=('do_db.motd:complete',)),
_MsgParseExpectation(
'396', # RPL_VISIBLEHOST
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.SERVER, 'setattr_db.users.me:host'),
- MsgTok.ANY)), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.SERVER, 'setattr_db.users.me:host'),
+ _MsgTok.ANY)), # comment
# SASL
_MsgParseExpectation(
'900', # RPL_LOGGEDIN
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'),
- (MsgTok.ANY, 'setattr_db:sasl_account'),
- MsgTok.ANY)), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'),
+ (_MsgTok.ANY, 'setattr_db:sasl_account'),
+ _MsgTok.ANY)), # comment
_MsgParseExpectation(
'903', # RPL_SASLSUCCESS
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, 'setattr_db:sasl_auth_state')),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, 'setattr_db:sasl_auth_state')),
bonus_tasks=('do_caps:end_negotiation',)),
_MsgParseExpectation(
'904', # ERR_SASLFAIL
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, 'setattr_db:sasl_auth_state')),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, 'setattr_db:sasl_auth_state')),
bonus_tasks=('do_caps:end_negotiation',)),
_MsgParseExpectation(
'AUTHENTICATE',
- MsgTok.NONE,
+ _MsgTok.NONE,
('+',)),
# capability negotation
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('NEW', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('DEL', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
('ACK', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('ACK', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
('NAK', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('NAK', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
('LS', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
('LS', ':subverb'),
('*', ':tbc'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('LS', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('LS', ':subverb'),
('*', ':tbc'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
('LIST', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
('LIST', ':subverb'),
('*', ':tbc'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('LIST', ':subverb'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
('LIST', ':subverb'),
('*', ':tbc'),
- (MsgTok.LIST, ':items'))),
+ (_MsgTok.LIST, ':items'))),
# nickname management
_MsgParseExpectation(
'432', # ERR_ERRONEOUSNICKNAME
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
- MsgTok.NICKNAME, # no need to re-use the bad one
- MsgTok.ANY)), # comment
+ _MsgTok.NICKNAME, # no need to re-use the bad one
+ _MsgTok.ANY)), # comment
_MsgParseExpectation(
'432', # ERR_ERRONEOUSNICKNAME
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- MsgTok.NICKNAME, # no need to re-use the bad one
- MsgTok.ANY)), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.NICKNAME, # no need to re-use the bad one
+ _MsgTok.ANY)), # comment
_MsgParseExpectation(
'433', # ERR_NICKNAMEINUSE
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
- (MsgTok.NICKNAME, ':used'),
- MsgTok.ANY)), # comment
+ (_MsgTok.NICKNAME, ':used'),
+ _MsgTok.ANY)), # comment
_MsgParseExpectation(
'433', # ERR_NICKNAMEINUSE
- MsgTok.SERVER,
- (MsgTok.NICKNAME, # we rather go for incrementation
- (MsgTok.NICKNAME, ':used'),
- MsgTok.ANY)), # comment
+ _MsgTok.SERVER,
+ (_MsgTok.NICKNAME, # we rather go for incrementation
+ (_MsgTok.NICKNAME, ':used'),
+ _MsgTok.ANY)), # comment
_MsgParseExpectation(
'NICK',
- (MsgTok.NICK_USER_HOST, ':named'),
- ((MsgTok.NICKNAME, ':nick'),)),
+ (_MsgTok.NICK_USER_HOST, ':named'),
+ ((_MsgTok.NICKNAME, ':nick'),)),
# joining/leaving
_MsgParseExpectation(
'332', # RPL_TOPIC
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.CHANNEL, ':CHAN'),
- (MsgTok.ANY, 'setattr_db.channels.CHAN.topic:what'))),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.CHANNEL, ':CHAN'),
+ (_MsgTok.ANY, 'setattr_db.channels.CHAN.topic:what'))),
_MsgParseExpectation(
'333', # RPL_TOPICWHOTIME
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.CHANNEL, ':CHAN'),
- (MsgTok.NICK_USER_HOST, 'setattr_db.channels.CHAN.topic:who'),
- (MsgTok.ANY, ':timestamp'))),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.CHANNEL, ':CHAN'),
+ (_MsgTok.NICK_USER_HOST, 'setattr_db.channels.CHAN.topic:who'),
+ (_MsgTok.ANY, ':timestamp'))),
_MsgParseExpectation(
'353', # RPL_NAMREPLY
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
'@',
- (MsgTok.CHANNEL, ':channel'),
- (MsgTok.LIST, ':names'))),
+ (_MsgTok.CHANNEL, ':channel'),
+ (_MsgTok.LIST, ':names'))),
_MsgParseExpectation(
'353', # RPL_NAMREPLY
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
'=',
- (MsgTok.CHANNEL, ':channel'),
- (MsgTok.LIST, ':names'))),
+ (_MsgTok.CHANNEL, ':channel'),
+ (_MsgTok.LIST, ':names'))),
_MsgParseExpectation(
'366', # RPL_ENDOFNAMES
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.CHANNEL, ':CHAN'),
- MsgTok.ANY), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.CHANNEL, ':CHAN'),
+ _MsgTok.ANY), # comment
bonus_tasks=('do_db.channels.CHAN.user_ids:complete',)),
_MsgParseExpectation(
'JOIN',
- (MsgTok.NICK_USER_HOST, ':joiner'),
- ((MsgTok.CHANNEL, ':channel'),)),
+ (_MsgTok.NICK_USER_HOST, ':joiner'),
+ ((_MsgTok.CHANNEL, ':channel'),)),
_MsgParseExpectation(
'PART',
- (MsgTok.NICK_USER_HOST, ':parter'),
- ((MsgTok.CHANNEL, ':channel'),)),
+ (_MsgTok.NICK_USER_HOST, ':parter'),
+ ((_MsgTok.CHANNEL, ':channel'),)),
_MsgParseExpectation(
'PART',
- (MsgTok.NICK_USER_HOST, ':parter'),
- ((MsgTok.CHANNEL, ':channel'),
- (MsgTok.ANY, ':message'))),
-
- _MsgParseExpectation(
- 'TOPIC',
- (MsgTok.NICK_USER_HOST, 'setattr_db.channels.CHAN.topic:who'),
- ((MsgTok.CHANNEL, ':CHAN'),
- (MsgTok.ANY, 'setattr_db.channels.CHAN.topic:what'))),
+ (_MsgTok.NICK_USER_HOST, ':parter'),
+ ((_MsgTok.CHANNEL, ':channel'),
+ (_MsgTok.ANY, ':message'))),
# messaging
_MsgParseExpectation(
'401', # ERR_NOSUCKNICK
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.NICKNAME, ':target'),
- MsgTok.ANY)), # comment
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.NICKNAME, ':target'),
+ _MsgTok.ANY)), # comment
_MsgParseExpectation(
'NOTICE',
- MsgTok.SERVER,
+ _MsgTok.SERVER,
('*',
- (MsgTok.ANY, ':message'))),
+ (_MsgTok.ANY, ':message'))),
_MsgParseExpectation(
'NOTICE',
- MsgTok.SERVER,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, ':message'))),
+ _MsgTok.SERVER,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, ':message'))),
_MsgParseExpectation(
'NOTICE',
- (MsgTok.NICK_USER_HOST, 'set_user:sender'),
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, ':message'))),
+ (_MsgTok.NICK_USER_HOST, 'set_user:sender'),
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, ':message'))),
_MsgParseExpectation(
'NOTICE',
- (MsgTok.NICK_USER_HOST, 'set_user:sender'),
- ((MsgTok.CHANNEL, ':channel'),
- (MsgTok.ANY, ':message'))),
+ (_MsgTok.NICK_USER_HOST, 'set_user:sender'),
+ ((_MsgTok.CHANNEL, ':channel'),
+ (_MsgTok.ANY, ':message'))),
_MsgParseExpectation(
'PRIVMSG',
- (MsgTok.NICK_USER_HOST, 'set_user:sender'),
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, ':message'))),
+ (_MsgTok.NICK_USER_HOST, 'set_user:sender'),
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, ':message'))),
_MsgParseExpectation(
'PRIVMSG',
- (MsgTok.NICK_USER_HOST, 'set_user:sender'),
- ((MsgTok.CHANNEL, ':channel'),
- (MsgTok.ANY, ':message'))),
+ (_MsgTok.NICK_USER_HOST, 'set_user:sender'),
+ ((_MsgTok.CHANNEL, ':channel'),
+ (_MsgTok.ANY, ':message'))),
# misc.
_MsgParseExpectation(
'ERROR',
- MsgTok.NONE,
- ((MsgTok.ANY, 'setattr_db:connection_state'),),
+ _MsgTok.NONE,
+ ((_MsgTok.ANY, 'setattr_db:connection_state'),),
bonus_tasks=('do_:close',)),
_MsgParseExpectation(
'MODE',
- (MsgTok.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'),
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, 'setattr_db:user_modes'))),
+ (_MsgTok.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'),
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, 'setattr_db:user_modes'))),
_MsgParseExpectation(
'MODE',
- MsgTok.NICKNAME,
- ((MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
- (MsgTok.ANY, 'setattr_db:user_modes'))),
+ _MsgTok.NICKNAME,
+ ((_MsgTok.NICKNAME, 'setattr_db.users.me:nick'),
+ (_MsgTok.ANY, 'setattr_db:user_modes'))),
_MsgParseExpectation(
'PING',
- MsgTok.NONE,
- ((MsgTok.ANY, ':reply'),)),
+ _MsgTok.NONE,
+ ((_MsgTok.ANY, ':reply'),)),
+
+ _MsgParseExpectation(
+ 'TOPIC',
+ (_MsgTok.NICK_USER_HOST, 'setattr_db.channels.CHAN.topic:who'),
+ ((_MsgTok.CHANNEL, ':CHAN'),
+ (_MsgTok.ANY, 'setattr_db.channels.CHAN.topic:what'))),
_MsgParseExpectation(
'QUIT',
- (MsgTok.NICK_USER_HOST, ':quitter'),
- ((MsgTok.ANY, ':message'),)),
+ (_MsgTok.NICK_USER_HOST, ':quitter'),
+ ((_MsgTok.ANY, ':message'),)),
]