From 59d542bb7066000dd0330408029fe3e4feb4d966 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Fri, 15 May 2015 19:24:24 +0200 Subject: [PATCH] NickCapture & core: Add support for MONITOR. Closes GH-842. --- plugins/NickCapture/plugin.py | 24 +++++++++++++++++++++++- src/irclib.py | 33 +++++++++++++++++++++++++++++++++ src/ircmsgs.py | 13 +++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/plugins/NickCapture/plugin.py b/plugins/NickCapture/plugin.py index 17b312204..974d50657 100644 --- a/plugins/NickCapture/plugin.py +++ b/plugins/NickCapture/plugin.py @@ -45,6 +45,12 @@ class NickCapture(callbacks.Plugin): self.__parent = super(NickCapture, self) self.__parent.__init__(irc) self.lastIson = 0 + self.monitoring = [] + + def die(self): + for irc in self.monitoring: + nick = self._getNick(irc.network) + irc.unmonitor(nick) def _getNick(self, network): network_nick = conf.supybot.networks.get(network).nick() @@ -60,10 +66,16 @@ class NickCapture(callbacks.Plugin): # We used to check this, but nicksToHostmasks is never cleared # except on reconnects, which can cause trouble. # if nick not in irc.state.nicksToHostmasks: - self._ison(irc, nick) + if 'monitor' in irc.state.supported: + if irc not in self.monitoring: + irc.monitor(nick) + self.monitoring.append(irc) + else: + self._ison(irc, nick) self.__parent.__call__(irc, msg) def _ison(self, irc, nick): + assert 'monitor' not in irc.state.supported if self.registryValue('ison'): now = time.time() if now - self.lastIson > self.registryValue('ison.period'): @@ -95,6 +107,16 @@ class NickCapture(callbacks.Plugin): nick = self._getNick(irc.network) if nick: self._sendNick(irc, nick) + + def do731(self, irc, msg): + """This is sent by the MONITOR when a nick goes offline.""" + nick = self._getNick(irc.network) + for target in msg.args[1].split(','): + if nick == target: + self._sendNick(irc, nick) + self.monitoring.remove(irc) + irc.unmonitor(nick) + break NickCapture = internationalizeDocstring(NickCapture) Class = NickCapture diff --git a/src/irclib.py b/src/irclib.py index 94380f9b2..1c743c058 100644 --- a/src/irclib.py +++ b/src/irclib.py @@ -666,6 +666,7 @@ class Irc(IrcCommandDispatcher): self._setNonResettingVariables() self._queueConnectMessages() self.startedSync = ircutils.IrcDict() + self.monitoring = ircutils.IrcDict() def isChannel(self, s): """Helper function to check whether a given string is a channel on @@ -1041,6 +1042,38 @@ class Irc(IrcCommandDispatcher): command='CAP', args=('END',))) + def monitor(self, targets): + """Increment a counter of how many callbacks monitor each target; + and send a MONITOR + to the server if the target is not yet + monitored.""" + if isinstance(targets, str): + targets = [targets] + not_yet_monitored = set() + for target in targets: + if target in self.monitoring: + self.monitoring[target] += 1 + else: + not_yet_monitored.add(target) + self.monitoring[target] = 1 + if not_yet_monitored: + self.queueMsg(ircmsgs.monitor('+', not_yet_monitored)) + return not_yet_monitored + + def unmonitor(self, targets): + """Decrements a counter of how many callbacks monitor each target; + and send a MONITOR - to the server if the counter drops to 0.""" + if isinstance(targets, str): + targets = [targets] + should_be_unmonitored = set() + for target in targets: + self.monitoring[target] -= 1 + if self.monitoring[target] == 0: + del self.monitoring[target] + should_be_unmonitored.add(target) + if should_be_unmonitored: + self.queueMsg(ircmsgs.monitor('-', should_be_unmonitored)) + return should_be_unmonitored + def do903(self, msg): log.info('%s: SASL authentication successful', self.network) self.queueMsg(ircmsgs.IrcMsg(command='CAP', args=('END',))) diff --git a/src/ircmsgs.py b/src/ircmsgs.py index 3a4453de4..45bf3104b 100644 --- a/src/ircmsgs.py +++ b/src/ircmsgs.py @@ -855,6 +855,19 @@ def ison(nick, prefix='', msg=None): prefix = msg.prefix return IrcMsg(prefix=prefix, command='ISON', args=(nick,), msg=msg) +def monitor(subcommand, nicks=None, prefix='', msg=None): + if conf.supybot.protocols.irc.strictRfc(): + assert isNick(nick), repr(nick) + assert subcommand in '+-CLS' + if subcommand in 'CLS': + assert nicks is None + if msg and not prefix: + prefix = msg.prefix + if not isinstance(nicks, str): + nicks = ','.join(nicks) + return IrcMsg(prefix=prefix, command='MONITOR', args=(subcommand, nicks), + msg=msg) + def error(s, msg=None): return IrcMsg(command='ERROR', args=(s,), msg=msg)