From a7f977aa3b67dfe15b1471e214d73c5056c287a8 Mon Sep 17 00:00:00 2001 From: James Lu Date: Thu, 23 Jul 2015 00:01:51 -0700 Subject: [PATCH] Add whois handlers for channel lists, user modes, and signon time, and relay user information New API: utils.whois_handlers allows one to add functions taking (irc, target) and returning (irc numeric, reply text) Closes #72. --- coreplugin.py | 36 ++++++++++++++++++++++++++++++++++-- plugins/relay.py | 9 +++++++++ utils.py | 1 + 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/coreplugin.py b/coreplugin.py index d06d4b8..e565533 100644 --- a/coreplugin.py +++ b/coreplugin.py @@ -55,6 +55,38 @@ def handle_whois(irc, source, command, args): # we'll only send it if the user has umode +o. if ('o', None) in user.modes: f(irc, server, 313, source, "%s :is an IRC Operator" % nick) - # 318: End of WHOIS. - f(irc, server, 318, source, "%s :End of WHOIS" % nick.lower()) + # 379: RPL_WHOISMODES, used by UnrealIRCd and InspIRCd + f(irc, server, 379, source, '%s :is using modes %s' % (nick, utils.joinModes(user.modes))) + # 319: RPL_WHOISCHANNELS, shows channel list + public_chans = [] + for chan in user.channels: + # Here, we'll want to hide secret/private channels from non-opers + # who are not in them. + c = irc.channels[chan] + if ((irc.cmodes.get('secret'), None) in c.modes or \ + (irc.cmodes.get('private'), None) in c.modes) \ + and not (('o', None) in irc.users[source].modes or \ + source in c.users): + continue + # TODO: show prefix modes like a regular IRCd does. + public_chans.append(chan) + if public_chans: + f(irc, server, 319, source, '%s :%s' % (nick, ' '.join(public_chans))) + # 317: shows idle and signon time. Though we don't track the user's real + # idle time; we just return 0. + # 317 GL GL 15 1437632859 :seconds idle, signon time + f(irc, server, 317, source, "%s 0 %s :seconds idle, signon time" % (nick, user.ts)) + try: + # Iterate over plugin-created WHOIS handlers. They return a tuple + # or list with two arguments: the numeric, and the text to send. + for func in utils.whois_handlers: + num, text = func(irc, target) + f(irc, server, num, source, text) + except Exception as e: + # Again, we wouldn't want this to crash our service, in case + # something goes wrong! + log.exception('Error caught in WHOIS handler: %s', e) + finally: + # 318: End of WHOIS. + f(irc, server, 318, source, "%s :End of WHOIS" % nick) utils.add_hook(handle_whois, 'WHOIS') diff --git a/plugins/relay.py b/plugins/relay.py index 56eaac7..c82e28d 100644 --- a/plugins/relay.py +++ b/plugins/relay.py @@ -15,6 +15,15 @@ from log import log dbname = "pylinkrelay.db" relayusers = defaultdict(dict) +def relayWhoisHandlers(irc, target): + user = irc.users[target] + network, remoteuid = getLocalUser(irc, target) + remotenick = utils.networkobjects[network].users[remoteuid].nick + return [320, "%s :is a remote user connected via PyLink Relay. Home " + "network: %s; Home nick: %s" % (user.nick, network, + remotenick)] +utils.whois_handlers.append(relayWhoisHandlers) + def normalizeNick(irc, netname, nick, separator=None): # Block until we know the IRC network's nick length (after capabilities # are sent) diff --git a/utils.py b/utils.py index 61baf89..1b16bb1 100644 --- a/utils.py +++ b/utils.py @@ -12,6 +12,7 @@ command_hooks = defaultdict(list) networkobjects = {} schedulers = {} plugins = [] +whois_handlers = [] started = threading.Event() class TS6UIDGenerator():