from ircplom.db_primitives import (
Clearable, Completable, CompletableStringsSet, DbLinked, DbLinking,
Dict, DictItem, UpdatingAttrsMixin, UpdatingCompletable,
- UpdatingCompletableStringsOrdered, UpdatingCompletableStringsSet,
- UpdatingDict, UpdatingMixin)
+ UpdatingCompletableDict, UpdatingCompletableStringsOrdered,
+ UpdatingCompletableStringsSet, UpdatingDict, UpdatingMixin)
from ircplom.events import (
AffectiveEvent, CrashingException, ExceptionEvent, QueueMixin)
from ircplom.irc_conn import (
exits: Dict[str]
modes_listy: Dict[tuple[str, ...]]
modes_valued: Dict[str]
- modes_toggled: str = ''
+ modes_toggled: Iterable[str]
@dataclass
user_ids: UpdatingCompletableStringsSet
topic: _UpdatingCompletableTopic
exits: UpdatingDict[str]
- modes_listy: UpdatingDict[tuple[str, ...]]
- modes_valued: UpdatingDict[str]
+ modes_listy: UpdatingCompletableDict[tuple[str, ...]]
+ modes_valued: UpdatingCompletableDict[str]
+ modes_toggled: UpdatingCompletableStringsSet
def _id_from_nick(self, nick: str, create_if_none: bool) -> str:
user_id = self._db.users.id_for_nickuserhost(NickUserHost(nick),
self.exits[user.id_] = msg
self.user_ids.completable_remove(user.id_, on_complete=True)
del self.exits[user.id_]
+ self.modes_listy.completed = None
for c in [c for c in self._db.get_membership_modes().keys()
if user.id_ in self.modes_listy.get(c, tuple())]:
self.modes_listy[c] = tuple(uid for uid in self.modes_listy[c]
if uid != user.id_)
+ self.modes_listy.complete()
self._db.users.purge()
def set_modes(self, modeset: str, args_str='') -> None:
except AssertionError:
raise ImplementationFail( # pylint: disable=raise-missing-from
f'channel mode setting {modeset} on args: {args_str}')
+ modes_attrs = self.modes_listy, self.modes_valued, self.modes_toggled
+ for attr in modes_attrs:
+ attr.completed = None
for do_add, char, arg in todos:
if char in modes['A'] + prefix_modes:
char_modes = list(self.modes_listy.get(char, tuple()))
elif char in self.modes_valued.keys():
del self.modes_valued[char]
else: # char in chanmodes['D']
- self.modes_toggled\
- = ''.join(sorted(set((self.modes_toggled + char) if do_add
- else (c for c in self.modes_toggled
- if c != char))))
+ getattr(self.modes_toggled,
+ f'completable_{"add" if do_add else "remove"}'
+ )(char, False)
+ for attr in modes_attrs:
+ attr.complete()
class _SetNickuserhostMixin:
if path not in self._updates_cache\
or val != self._updates_cache[path]:
self._updates_cache[path] = val
+ if val in (None, Dict()):
+ for cache_path in [p for p in self._updates_cache
+ if len(p) > len(path)
+ and p[:len(path)] == path]:
+ del self._updates_cache[cache_path]
return [(path, val)]
return []
else None) if isinstance(parent, Dict)
else getattr(parent, last_step))
- # treat anything without UpdatingMixin as single endnode to return
+ # ignore Completables if not .completed
+ if isinstance(val_at_path, Completable):
+ if val_at_path.completed is None:
+ return []
+ val_at_path = val_at_path.completed
+
+ # treat any non-Dict without UpdatingMixin as single endnode to return
# (NB: this would include None, as a deletion signal)
- if not isinstance(val_at_path, UpdatingMixin):
+ if not isinstance(val_at_path, (Dict, UpdatingMixin)):
return update_unless_cached(path, val_at_path)
- # for completable, only return its .completed
- if isinstance(val_at_path, Completable):
- if val_at_path.completed is not None:
- return update_unless_cached(path, val_at_path.completed)
- return []
-
# for empty Dict, return one as clearing signal
if isinstance(val_at_path, Dict) and not val_at_path.keys():
- for cache_path in [p for p in self._updates_cache
- if len(p) > len(path)
- and p[:len(path)] == path]:
- del self._updates_cache[cache_path]
return update_unless_cached(path, Dict())
# if node at path has children, (only) return _their_ endnode updates
for key in sub_items:
p = path + (key,)
updates += self.into_endnode_updates(p)
+
+ # signal absence of any _former_ sub_items siblings
+ for cache_path in [p for p in self._updates_cache
+ if p[:-1] == path
+ and p[-1] not in sub_items]:
+ updates += update_unless_cached(cache_path, None)
return updates
def clear(self) -> None:
for task, args in [t for t in ret['_tasks'].items()
if t[0].verb == verb]:
path = list(task.path)
- node: Any = (ret[path.pop(0)]
- if task.path and path[0].isupper() else self)
- for step in path:
- key = ret[step] if step.isupper() else step
- node = (node[key] if isinstance(node, Dict)
- else (node(key) if callable(node)
- else getattr(node, key)))
+ node: Any = (ret[path.pop(0)[1]]
+ if task.path and path[0][1].isupper() else self)
+ for is_dict_key, step_name in path:
+ if is_dict_key:
+ if step_name.isupper():
+ step_name = ret[step_name]
+ node = node[step_name]
+ else:
+ node = (node(step_name) if callable(node)
+ else getattr(node, step_name))
for arg in args:
if task.verb == 'setattr':
setattr(node, arg, ret[arg])
exits: _UpdatingDict[str]
modes_listy: _UpdatingDict[tuple[str, ...]]
modes_valued: _UpdatingDict[str]
+ modes_toggled: set[str]
def prefix_for(self, user_id: str) -> str:
'Construct prefixes string for user of user_id.'
return tuple((val for val in self._dict.values()))
def get(self, key: str, default: DictItem) -> DictItem:
- 'Wrapper around dict.get(key, default), excluding None returns.'
+ 'Wrapper around dict.get(key, default), sans implicit None defaults.'
return self._dict.get(key, default)
def _on_set(self, m_name: str, item: str, on_complete: bool, exists: bool
) -> None:
- method = getattr(self._collected, m_name)
+ assert (self.completed is not None) == on_complete
+ if (item in self._collected) == exists:
+ getattr(self._collected, m_name)(item)
if on_complete:
- assert self.completed is not None
- assert (item in self.completed) == exists
- assert (item in self._collected) == exists
- method(item)
self.complete()
- else:
- assert self.completed is None
- assert (item in self._collected) == exists
- method(item)
def completable_add(self, item: str, on_complete: bool) -> None:
'Put item into collection.'
self.complete()
+class _CompletableDict(Completable, Dict[DictItem]):
+ completed: Optional[Dict[DictItem]] = None
+
+ def complete(self) -> None:
+ self.completed: Dict[DictItem] = Dict()
+ for k, v in self._dict.items():
+ self.completed[k] = v
+
+
class UpdatingMixin:
'Ensures update trigger subclasses can call on any data changes.'
'Clearable, updating, completable tuple of strings.'
-_DbType = TypeVar('_DbType')
+class UpdatingCompletableDict(UpdatingCompletable, _CompletableDict[DictItem]):
+ 'Updating, completable Dict.'
+
+
+_DbType = TypeVar('_DbType', bound='DbLinking')
_Same = TypeVar('_Same')
class _Command(NamedTuple):
verb: str
- path: tuple[str, ...]
+ path: tuple[tuple[bool, str], ...]
@classmethod
def from_(cls, input_: str) -> Self:
- 'Split by first "_" into verb, path (split into steps tuple by ".").'
+ 'Split by first "_" into verb, path (split into steps tuple).'
verb, path_str = input_.split('_', maxsplit=1)
- return cls(verb, tuple(step for step in path_str.split('.')
- if path_str))
+ is_dict_key = False
+ cur_tok = ''
+ steps = []
+ for c in path_str + '.':
+ if c in '.[]':
+ if cur_tok:
+ steps += [(is_dict_key, cur_tok)]
+ cur_tok = ''
+ is_dict_key = c == '['
+ continue
+ cur_tok += c
+ return cls(verb, tuple(steps))
class _Code(NamedTuple):
_MsgParseExpectation(
'001', # RPL_WELCOME
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY),
bonus_tasks=('do_autojoin:',)),
_MsgParseExpectation(
'002', # RPL_YOURHOST
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'003', # RPL_CREATED
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'004', # RPL_MYINFO
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY,
_MsgToken.ANY,
_MsgToken.ANY,
_MsgParseExpectation(
'250', # RPL_STATSDLINE / RPL_STATSCONN
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'251', # RPL_LUSERCLIENT
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'252', # RPL_LUSEROP
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY,
_MsgToken.ANY)),
_MsgParseExpectation(
'253', # RPL_LUSERUNKNOWN
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY,
_MsgToken.ANY)),
_MsgParseExpectation(
'254', # RPL_LUSERCHANNELS
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY,
_MsgToken.ANY)),
_MsgParseExpectation(
'255', # RPL_LUSERME
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'265', # RPL_LOCALUSERS
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'265', # RPL_LOCALUSERS
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY,
_MsgToken.ANY,
_MsgToken.ANY)),
_MsgParseExpectation(
'266', # RPL_GLOBALUSERS
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
_MsgParseExpectation(
'266', # RPL_GLOBALUSERS
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY,
_MsgToken.ANY,
_MsgToken.ANY)),
_MsgParseExpectation(
'375', # RPL_MOTDSTART already implied by 1st 372
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY)),
# various login stuff
_MsgParseExpectation(
'005', # RPL_ISUPPORT
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.LIST, 'do_db.set_isupport_from_rpl:isupport'),
_MsgToken.ANY)), # comment
_MsgParseExpectation(
'372', # RPL_MOTD
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.ANY, 'do_db.motd.append:line'))),
_MsgParseExpectation(
'376', # RPL_ENDOFMOTD
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY), # comment
bonus_tasks=('do_db.motd.complete:',)),
_MsgParseExpectation(
'396', # RPL_VISIBLEHOST
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
- (_MsgToken.SERVER, 'setattr_db.users.me:host'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
+ (_MsgToken.SERVER, 'setattr_db.users[me]:host'),
_MsgToken.ANY)), # comment
# SASL
_MsgParseExpectation(
'900', # RPL_LOGGEDIN
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
- (_MsgToken.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
+ (_MsgToken.NICK_USER_HOST, 'setattr_db.users[me]:nickuserhost'),
(_MsgToken.ANY, 'setattr_db:sasl_account'),
_MsgToken.ANY)), # comment
_MsgParseExpectation(
'903', # RPL_SASLSUCCESS
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.ANY, 'setattr_db:sasl_auth_state')),
bonus_tasks=('do_caps.end_negotiation:',)),
_MsgParseExpectation(
'904', # ERR_SASLFAIL
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.ANY, 'setattr_db:sasl_auth_state')),
bonus_tasks=('do_caps.end_negotiation:',)),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('NEW', ':subverb'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('DEL', ':subverb'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('ACK', ':subverb'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('NAK', ':subverb'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('LS', ':subverb'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('LS', ':subverb'),
('*', ':tbc'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('LIST', ':subverb'),
('*', ':tbc'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'CAP',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
('LIST', ':subverb'),
(_MsgToken.LIST, ':items'))),
_MsgParseExpectation(
'432', # ERR_ERRONEOUSNICKNAME
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
_MsgToken.ANY, # bad one probably fails our NICKNAME tests
_MsgToken.ANY)), # comment
_MsgParseExpectation(
'332', # RPL_TOPIC
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.CHANNEL, ':CHAN'),
- (_MsgToken.ANY, 'setattr_db.channels.CHAN.topic:what'))),
+ (_MsgToken.ANY, 'setattr_db.channels[CHAN].topic:what'))),
_MsgParseExpectation(
'333', # RPL_TOPICWHOTIME
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.CHANNEL, ':CHAN'),
(_MsgToken.NICK_USER_HOST,
- f'{_TOK_SKIPNUH},setattr_db.channels.CHAN.topic:who'),
+ f'{_TOK_SKIPNUH},setattr_db.channels[CHAN].topic:who'),
(_MsgToken.ANY, ':timestamp')),
- bonus_tasks=('doafter_db.channels.CHAN.topic.complete:',)),
+ bonus_tasks=('doafter_db.channels[CHAN].topic.complete:',)),
_MsgParseExpectation(
'353', # RPL_NAMREPLY
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
'@',
(_MsgToken.CHANNEL, ':CHANNEL'),
- (_MsgToken.LIST, 'do_db.channels.CHANNEL.add_from_namreply:names'))),
+ (_MsgToken.LIST, 'do_db.channels[CHANNEL].add_from_namreply:names'))),
_MsgParseExpectation(
'353', # RPL_NAMREPLY
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
'=',
(_MsgToken.CHANNEL, ':CHANNEL'),
- (_MsgToken.LIST, 'do_db.channels.CHANNEL.add_from_namreply:names'))),
+ (_MsgToken.LIST, 'do_db.channels[CHANNEL].add_from_namreply:names'))),
_MsgParseExpectation(
'366', # RPL_ENDOFNAMES
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.CHANNEL, ':CHAN'),
_MsgToken.ANY), # comment
- bonus_tasks=('doafter_db.channels.CHAN.user_ids.complete:',)),
+ bonus_tasks=('doafter_db.channels[CHAN].modes_listy.complete:',
+ 'doafter_db.channels[CHAN].user_ids.complete:')),
_MsgParseExpectation(
'JOIN',
- (_MsgToken.NICK_USER_HOST, 'do_db.channels.CHANNEL.join_user:user'),
+ (_MsgToken.NICK_USER_HOST, 'do_db.channels[CHANNEL].join_user:user'),
((_MsgToken.CHANNEL, ':CHANNEL'),)),
_MsgParseExpectation(
_MsgParseExpectation(
'401', # ERR_NOSUCKNICK
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.NICKNAME, ':missing'),
_MsgToken.ANY)), # comment
_MsgParseExpectation(
'NOTICE',
_MsgToken.SERVER,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
(_MsgToken.ANY, ':content')),
bonus_tasks=('do_db.notice:content',)),
_MsgParseExpectation(
'NOTICE',
(_MsgToken.NICK_USER_HOST, ':sender'),
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:target'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:target'),
(_MsgToken.ANY, ':content')),
bonus_tasks=('do_db.notice:content,sender',)),
_MsgParseExpectation(
'PRIVMSG',
(_MsgToken.NICK_USER_HOST, ':sender'),
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:target'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:target'),
(_MsgToken.ANY, ':content')),
bonus_tasks=('do_db.privmsg:content,sender',)),
_MsgParseExpectation(
_MsgParseExpectation(
'MODE',
- (_MsgToken.NICK_USER_HOST, 'setattr_db.users.me:nickuserhost'),
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
- (_MsgToken.ANY, 'setattr_db.users.me:modes'))),
+ (_MsgToken.NICK_USER_HOST, 'setattr_db.users[me]:nickuserhost'),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
+ (_MsgToken.ANY, 'setattr_db.users[me]:modes'))),
_MsgParseExpectation(
'MODE',
_MsgToken.NICKNAME,
- ((_MsgToken.NICKNAME, 'setattr_db.users.me:nick'),
- (_MsgToken.ANY, 'setattr_db.users.me:modes'))),
+ ((_MsgToken.NICKNAME, 'setattr_db.users[me]:nick'),
+ (_MsgToken.ANY, 'setattr_db.users[me]:modes'))),
_MsgParseExpectation(
'MODE',
_MsgToken.SERVER,
((_MsgToken.CHANNEL, ':CHANNEL'),
- (_MsgToken.ANY, 'do_db.channels.CHANNEL.set_modes:modeset'))),
+ (_MsgToken.ANY, 'do_db.channels[CHANNEL].set_modes:modeset'))),
_MsgParseExpectation(
'MODE',
_MsgToken.SERVER,
((_MsgToken.CHANNEL, ':CHANNEL'),
(_MsgToken.ANY, ':modeset'),
(_MsgToken.ANY, ':args')),
- bonus_tasks=('do_db.channels.CHANNEL.set_modes:modeset,args',)),
+ bonus_tasks=('do_db.channels[CHANNEL].set_modes:modeset,args',)),
_MsgParseExpectation(
'TOPIC',
- (_MsgToken.NICK_USER_HOST, 'setattr_db.channels.CHAN.topic:who'),
+ (_MsgToken.NICK_USER_HOST, 'setattr_db.channels[CHAN].topic:who'),
((_MsgToken.CHANNEL, ':CHAN'),
- (_MsgToken.ANY, 'setattr_db.channels.CHAN.topic:what')),
- bonus_tasks=('doafter_db.channels.CHAN.topic.complete:',)),
+ (_MsgToken.ANY, 'setattr_db.channels[CHAN].topic:what')),
+ bonus_tasks=('doafter_db.channels[CHAN].topic.complete:',)),
_MsgParseExpectation(
'QUIT',
× servermsg-mode
insert servermsglogged [% (MSG)=:foo.bar.baz%MODE%#ch_win3%(ARGS)]
-× prefix-log
-log 1 $ channels:#ch_win3:modes_listy:(PREFIX) (NEW_IDS)
-log 3 $ (NICK) (VERB) (PREFIX)
-
-× set-prefix
-insert servermsg-mode [% (ARGS)=(MODESET)%(NICK)]
-insert prefix-log
-
-× prefix+
-insert set-prefix [(MODESET)=+(CHAR) (VERB)=gains]
-
-× prefix-
-insert set-prefix [(MODESET)=-(CHAR) (VERB)=loses]
-
× msg-prefixed-long
insert servermsglogged [% (MSG)=:(NICK)!~(NICK)(NICK)@(NICK).(NICK)%PRIVMSG%#ch_win3%:(NICK)%(NICK)%(NICK)]
insert user-set-to range=1: [(USER_USER)=~(NICK)(NICK) (USER_HOST)=(NICK).(NICK)]
# check /join into channel with many other users, with multi-line 353, and membership prefixes
insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=3 (RESIDENT_NAMES)=foo%@baz%+oof]
insert user-set-to range=:1 [(USER_ID)=1 (USER_NICK)=baz]
-log 1 $ channels:#ch_win3:modes_listy:o set to: [1]
insert user-set-to range=:1 [(USER_ID)=2 (USER_NICK)=oof]
-log 1 $ channels:#ch_win3:modes_listy:v set to: [2]
insert servermsglogged [% (MSG)=:foo.bar.baz%353%foo%=%#ch_win3%:+rab%zab]
insert user-set-to range=:1 [(USER_ID)=3 (USER_NICK)=rab]
-log 1 $ channels:#ch_win3:modes_listy:v set to: [2], [3]
insert user-set-to range=:1 [(USER_ID)=4 (USER_NICK)=zab]
-insert join-channel-1-end-of-names [% (WIN_ID)=3 (RESIDENT_IDS)=[1],%[2],%[3],%[4],%[me] (RESIDENT_NICKS)=@baz,%+oof,%+rab,%zab,%foo]
+insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=3]
+log 1 $ channels:#ch_win3:modes_listy:o set to: [1]
+log 1 $ channels:#ch_win3:modes_listy:v set to: [2], [3]
+insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=3 (RESIDENT_IDS)=[1],%[2],%[3],%[4],%[me] (RESIDENT_NICKS)=@baz,%+oof,%+rab,%zab,%foo]
# check (presence/absence of) membership prefixes mirrored in message display
insert privmsg-win [% (WIN_ID)=3 (TXT)=foo%foo%foo]
insert msg-prefixed-long [(USER_ID)=4 (NICK)=zab (PREFIX)=]
# check server giving and taking membership prefixes
-insert prefix- [(CHAR)=o (PREFIX)=o (NICK)=baz (NEW_IDS)=emptied]
-insert prefix- [% (CHAR)=v (PREFIX)=v (NICK)=rab (NEW_IDS)=set%to:%[2]]
-insert servermsg-mode [% (ARGS)=-v+oo%:oof%zab%baz]
-insert prefix-log [% (PREFIX)=v (VERB)=loses (NICK)=oof (NEW_IDS)=emptied]
-insert prefix-log [% (PREFIX)=o (VERB)=gains (NICK)=zab (NEW_IDS)=set%to:%[4]]
-insert prefix-log [% (PREFIX)=o (VERB)=gains (NICK)=baz (NEW_IDS)=set%to:%[1],%[4]]
-insert prefix+ [% (CHAR)=v (PREFIX)=v (NICK)=oof (NEW_IDS)=set%to:%[2]]
-insert prefix+ [% (CHAR)=o (PREFIX)=o (NICK)=foo (NEW_IDS)=set%to:%[1],%[4],%[me]]
+insert servermsg-mode [% (ARGS)=-o%baz]
+log 1 $ channels:#ch_win3:modes_listy:o emptied
+log 3 $ baz loses o
+insert servermsg-mode [% (ARGS)=-v%rab]
+log 1 $ channels:#ch_win3:modes_listy:v set to: [2]
+log 3 $ rab loses v
+insert servermsg-mode [% (ARGS)=-v+oov%:oof%zab%baz%foo]
+log 1 $ channels:#ch_win3:modes_listy:o set to: [1], [4]
+log 3 $ baz gains o
+log 3 $ zab gains o
+log 1 $ channels:#ch_win3:modes_listy:v set to: [me]
+log 3 $ foo gains v
+log 3 $ oof loses v
# re-check membership prefix mirorring in message display after re-distribution
-insert privmsg-win [% (WIN_ID)=3 (TXT)=foo%foo%foo [foo]=[@foo]]
+insert privmsg-win [% (WIN_ID)=3 (TXT)=foo%foo%foo [foo]=[+foo]]
insert msg-prefixed-short [(USER_ID)=1 (NICK)=baz (PREFIX)=@]
-insert msg-prefixed-short [(USER_ID)=2 (NICK)=oof (PREFIX)=+]
insert msg-prefixed-short [(USER_ID)=3 (NICK)=rab (PREFIX)= PRIVMSG=NOTICE [rab]=(rab)]
insert msg-prefixed-short [(USER_ID)=4 (NICK)=zab (PREFIX)=@]
insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=4 (RESIDENT_NAMES)=foo%baz]
insert join-channel-1-end-of-names [% (WIN_ID)=4 (RESIDENT_IDS)=[1],%[me] (RESIDENT_NICKS)=baz,%foo]
insert part-no-msg-other [% (USER_ID)=1 (NICK)=baz (REMAINING_IDS)=[2],%[3],%[4],%[me]]
-log 1 $ channels:#ch_win3:modes_listy:o set to: [4], [me]
+log 1 $ channels:#ch_win3:modes_listy:o set to: [4]
insert servermsglogged [% (MSG)=:baz!~bazbaz@baz.baz%JOIN%:#ch_win3]
log 1 $ channels:#ch_win3:user_ids set to: [1], [2], [3], [4], [me]
log 3 $ baz!~bazbaz@baz.baz joins
insert servermsg-mode [% (ARGS)=+l%:bar]
log 1 $ channels:#ch_win3:modes_listy:l set to: [bar], [foo]
insert servermsg-mode [% (ARGS)=+l-l%:baz%foo]
-log 1 $ channels:#ch_win3:modes_listy:l set to: [bar], [baz], [foo]
log 1 $ channels:#ch_win3:modes_listy:l set to: [bar], [baz]
# check server setting type-B modes
insert servermsg-mode [% (ARGS)=+k%:password]
log 1 $ channels:#ch_win3:modes_valued:k set to: [password]
insert servermsg-mode [% (ARGS)=-k%:*]
-log 1 $ channels:#ch_win3:modes_valued:k deleted
+log 1 $ channels:#ch_win3:modes_valued cleared
# check server setting type-C modes
insert servermsg-mode [% (ARGS)=+x%:foo]
log 1 $ channels:#ch_win3:modes_valued:x set to: [foo]
-insert servermsg-mode [% (ARGS)=+x%:bar]
+insert servermsg-mode [% (ARGS)=+xy%:bar%zab]
log 1 $ channels:#ch_win3:modes_valued:x set to: [bar]
-insert servermsg-mode [% (ARGS)=+y%:zab]
log 1 $ channels:#ch_win3:modes_valued:y set to: [zab]
insert servermsg-mode [(ARGS)=-x]
log 1 $ channels:#ch_win3:modes_valued:x deleted
# check server setting type-D modes
insert servermsg-mode [(ARGS)=+aBc]
-log 1 $ channels:#ch_win3:modes_toggled set to: [a]
-log 1 $ channels:#ch_win3:modes_toggled set to: [Ba]
-log 1 $ channels:#ch_win3:modes_toggled set to: [Bac]
+log 1 $ channels:#ch_win3:modes_toggled set to: [B], [a], [c]
insert servermsg-mode [(ARGS)=-cba]
-log 1 $ channels:#ch_win3:modes_toggled set to: [Ba]
log 1 $ channels:#ch_win3:modes_toggled set to: [B]
insert servermsg-mode [(ARGS)=-B+cb]
-log 1 $ channels:#ch_win3:modes_toggled set to: []
-log 1 $ channels:#ch_win3:modes_toggled set to: [c]
-log 1 $ channels:#ch_win3:modes_toggled set to: [bc]
+log 1 $ channels:#ch_win3:modes_toggled set to: [b], [c]
insert servermsglogged [% (MSG)=:foo.bar.baz%333%foo%#ch_win4%baz!~bazbaz@OLD.baz.baz%1234567890]
insert topic-set-to [% baz.baz=OLD.baz.baz (WIN_ID)=4 (TOPIC)=foo%bar%baz]
insert join-channel-0-1-list-residents [% (WIN_ID)=4 (RESIDENT_NAMES)=foo%baz]
-insert join-channel-1-end-of-names [% (WIN_ID)=4 (RESIDENT_IDS)=[2],%[me] (RESIDENT_NICKS)=baz,%foo]
+insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=4]
+insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=4 (RESIDENT_IDS)=[2],%[me] (RESIDENT_NICKS)=baz,%foo]
# check _observed_ topic change _does_ affect users database, and …
insert servermsglogged [% (MSG)=:baz!~bazbaz@baz.baz%TOPIC%#ch_win4%:foo%bar%baz]
# test effect of PREFIX
insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=7 (RESIDENT_NAMES)=foo%@bar%+baz%=quux]
insert user-set-to range=:1 [(USER_ID)=1 (USER_NICK)=bar]
-log 1 $ channels:#ch_win7:modes_listy:o set to: [1]
insert user-set-to range=:1 [(USER_ID)=2 (USER_NICK)=baz]
-log 1 $ channels:#ch_win7:modes_listy:v set to: [2]
insert user-set-to range=:1 [(USER_ID)=3 (USER_NICK)==quux]
-insert join-channel-1-end-of-names [% (WIN_ID)=7 (RESIDENT_IDS)=[1],%[2],%[3],%[me] (RESIDENT_NICKS)=@bar,%+baz,%=quux,%foo]
+insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=7]
+log 1 $ channels:#ch_win7:modes_listy:o set to: [1]
+log 1 $ channels:#ch_win7:modes_listy:v set to: [2]
+insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=7 (RESIDENT_IDS)=[1],%[2],%[3],%[me] (RESIDENT_NICKS)=@bar,%+baz,%=quux,%foo]
insert isupport-set [(KEY)=PREFIX (VALUE)=(vE)+=]
insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=8 (RESIDENT_NAMES)=foo%@bar%+baz%=quux]
insert user-set-to range=:1 [(USER_ID)=4 (USER_NICK)=@bar]
-log 1 $ channels:#ch_win8:modes_listy:v set to: [2]
insert user-set-to range=:1 [(USER_ID)=5 (USER_NICK)=quux]
+insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=8]
+log 1 $ channels:#ch_win8:modes_listy:v set to: [2]
log 1 $ channels:#ch_win8:modes_listy:E set to: [5]
-insert join-channel-1-end-of-names [% (WIN_ID)=8 (RESIDENT_IDS)=[2],%[4],%[5],%[me] (RESIDENT_NICKS)=+baz,%@bar,%=quux,%foo]
+insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=8 (RESIDENT_IDS)=[2],%[4],%[5],%[me] (RESIDENT_NICKS)=+baz,%@bar,%=quux,%foo]
# test effect of USERLEN
insert join-empty [(WIN_ID)=9]
× join-channel-1-end-of-names
insert servermsglogged [% (MSG)=:foo.bar.baz%366%foo%#ch_win(WIN_ID)%:End%of%/NAMES%list.]
+log 1 $ channels:#ch_win(WIN_ID):modes_listy cleared
log 1 $ channels:#ch_win(WIN_ID):user_ids set to: (RESIDENT_IDS)
log (WIN_ID) $ residents: (RESIDENT_NICKS)
# join channel with other user
insert join-channel-0-cmd-to-list-residents [% (WIN_ID)=4 (RESIDENT_NAMES)=foo%@baz]
insert user-set-to range=:1 [(USER_ID)=1 (USER_NICK)=baz]
+insert join-channel-1-end-of-names range=:-3 [(WIN_ID)=4]
log 1 $ channels:#ch_win4:modes_listy:o set to: [1]
-insert join-channel-1-end-of-names [% (WIN_ID)=4 (RESIDENT_IDS)=[1],%[me] (RESIDENT_NICKS)=@baz,%foo]
+insert join-channel-1-end-of-names range=-2: [% (WIN_ID)=4 (RESIDENT_IDS)=[1],%[me] (RESIDENT_NICKS)=@baz,%foo]
# process non-self channel JOIN
insert servermsglogged [% (MSG)=:bar!~barbar@bar.bar%JOIN%:#ch_win4]