From 074548813f629e0d1030f133a1e1e92e9b42467e Mon Sep 17 00:00:00 2001 From: "Vgr E. Barry" Date: Thu, 17 Nov 2016 10:01:25 -0500 Subject: [PATCH] Delay some channel handling operations Specifically, the mode handling and the end of list modes are delayed until the end of the WHO reply from the server. When the end of the WHO reply is received, all queued operations on the channel, if any, are triggered at once in the form of relevant events. --- src/channels.py | 11 +++++++++-- src/hooks.py | 22 ++++++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/channels.py b/src/channels.py index 076bc00..a25db5f 100644 --- a/src/channels.py +++ b/src/channels.py @@ -67,6 +67,7 @@ class Channel(IRCContext): self.modes = {} self.timestamp = None self.state = _States.NotJoined + self._pending = [] def __del__(self): self.users.clear() @@ -81,6 +82,12 @@ class Channel(IRCContext): def __repr__(self): return "{self.__class__.__name__}({self.name!r})".format(self=self) + def queue(self, name, params, args): + if self._pending is None: + Event(name, params).dispatch(*args) + else: + self._pending.append((name, params, args)) + def join(self, key=""): if self.state in (_States.NotJoined, _States.Left): self.state = _States.PendingJoin @@ -151,7 +158,7 @@ class Channel(IRCContext): self.client.send("MODE", self.name, "".join(final)) - def update_modes(self, rawnick, mode, targets): + def update_modes(self, actor, mode, targets): """Update the channel's mode registry with the new modes. This is called whenever a MODE event is received. All of the @@ -184,7 +191,7 @@ class Channel(IRCContext): elif c in list_modes: # stuff like bans, quiets, and ban and invite exempts if c not in self.modes: self.modes[c] = {} - self.modes[c][targets[i]] = (rawnick, set_time) + self.modes[c][targets[i]] = (actor.rawnick, set_time) i += 1 else: diff --git a/src/hooks.py b/src/hooks.py index 0adc8d2..3a72a2a 100644 --- a/src/hooks.py +++ b/src/hooks.py @@ -6,7 +6,7 @@ further in the relevant hook functions. """ -from src.decorators import hook +from src.decorators import event_listener, hook from src.context import Features from src.events import Event from src.logger import plog @@ -144,6 +144,16 @@ def end_who(cli, bot_server, bot_nick, target, rest): """ + try: + chan = channels.get(target) + except KeyError: + pass + else: + if chan._pending is not None: + for name, params, args in chan._pending: + Event(name, params).dispatch(*args) + chan._pending = None + Event("who_end", {}).dispatch(var, target) ### Server PING handling @@ -278,9 +288,13 @@ def mode_change(cli, rawnick, chan, mode, *targets): return target = channels.add(chan, cli) - target.update_modes(rawnick, mode, targets) + target.queue("mode_change", {"mode": mode, "targets": targets}, (var, actor, target)) - Event("mode_change", {}).dispatch(var, actor, target) +@event_listener("mode_change", 0) # This should fire before anything else! +def apply_mode_changes(evt, var, actor, target): + """Apply all mode changes before any other event.""" + + target.update_modes(actor, evt.data.pop("mode"), evt.data.pop("targets")) ### List modes handling (bans, quiets, ban and invite exempts) @@ -369,7 +383,7 @@ def handle_endlistmode(cli, chan, mode): """Handle the end of a list mode listing.""" ch = channels.add(chan, cli) - Event("end_listmode", {}).dispatch(var, ch, mode) + ch.queue("end_listmode", {}, (var, ch, mode)) @hook("endofbanlist") def end_banlist(cli, server, bot_nick, chan, message):