diff --git a/plugins/bots.py b/plugins/bots.py index b091e84..ff43db7 100644 --- a/plugins/bots.py +++ b/plugins/bots.py @@ -39,13 +39,16 @@ def quit(irc, source, args): irc.error("Not enough arguments. Needs 1-2: nick, reason (optional).") return - u = irc.nick_to_uid(nick) + u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client) + if u is None: + irc.error("Unknown user %r" % nick) + return if irc.pseudoclient.uid == u: irc.error("Cannot quit the main PyLink client!") return - quitmsg = ' '.join(args[1:]) or 'Client Quit' + quitmsg = ' '.join(args[1:]) or 'Client Quit' if not irc.is_manipulatable_client(u): irc.error("Cannot force quit a protected PyLink services client.") @@ -69,13 +72,13 @@ def joinclient(irc, source, args): try: # Check if the first argument is an existing PyLink client. If it is not, # then assume that the first argument was actually the channels being joined. - u = irc.nick_to_uid(args[0]) + u = irc.nick_to_uid(args[0], filterfunc=irc.is_internal_client) - if not irc.is_internal_client(u): # First argument isn't one of our clients + if u is None: # First argument isn't one of our clients raise IndexError clist = args[1] - except IndexError: # No nick was given; shift arguments one to the left. + except IndexError: # No valid nick was given; shift arguments one to the left. u = irc.pseudoclient.uid try: clist = args[0] @@ -114,7 +117,7 @@ def joinclient(irc, source, args): except KeyError: modes = [] - # Call a join hook manually so other plugins like relay can understand it. + # Signal the join to other plugins irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': real_channel, 'users': [u], 'modes': modes, 'parse_as': 'JOIN'}]) irc.reply("Done.") @@ -138,7 +141,7 @@ def nick(irc, source, args): except IndexError: irc.error("Not enough arguments. Needs 1-2: nick (optional), newnick.") return - u = irc.nick_to_uid(nick) + u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client) if newnick in ('0', u): # Allow /nick 0 to work newnick = u @@ -153,7 +156,7 @@ def nick(irc, source, args): irc.nick(u, newnick) irc.reply("Done.") - # Ditto above: manually send a NICK change hook payload to other plugins. + # Signal the nick change to other plugins irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}]) @utils.add_cmd @@ -171,8 +174,8 @@ def part(irc, source, args): # First, check if the first argument is an existing PyLink client. If it is not, # then assume that the first argument was actually the channels being parted. - u = irc.nick_to_uid(nick) - if not irc.is_internal_client(u): # First argument isn't one of our clients + u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client) + if u is None: # First argument isn't one of our clients raise IndexError except IndexError: # No nick was given; shift arguments one to the left. @@ -217,12 +220,11 @@ def msg(irc, source, args): # First, check if the first argument is an existing PyLink client. If it is not, # then assume that the first argument was actually the message TARGET. - sourceuid = irc.nick_to_uid(msgsource) - if not irc.is_internal_client(sourceuid): # First argument isn't one of our clients + sourceuid = irc.nick_to_uid(msgsource, filterfunc=irc.is_internal_client) + + if sourceuid is None or not text: # First argument isn't one of our clients raise IndexError - if not text: - raise IndexError except IndexError: try: sourceuid = irc.pseudoclient.uid @@ -236,12 +238,26 @@ def msg(irc, source, args): irc.error('No text given.') return - if not irc.is_channel(target): - # Convert nick of the message target to a UID, if the target isn't a channel - real_target = irc.nick_to_uid(target) - if real_target is None: # Unknown target user, if target isn't a valid channel name + try: + int_u = int(target) + except: + int_u = None + + if int_u and int_u in irc.users: + real_target = int_u # Some protocols use numeric UIDs + elif target in irc.users: + real_target = target + elif not irc.is_channel(target): + # Convert nick of the message target to a UID, if the target isn't a channel or UID + potential_targets = irc.nick_to_uid(target, multi=True) + if not potential_targets: # Unknown target user, if target isn't a valid channel name irc.error('Unknown user %r.' % target) return + elif len(potential_targets) > 1: + irc.error('Multiple users with the nick %r found: please select the right UID: %s' % (target, str(potential_targets))) + return + else: + real_target = potential_targets[0] else: real_target = target diff --git a/plugins/opercmds.py b/plugins/opercmds.py index a7dc645..4ede14c 100644 --- a/plugins/opercmds.py +++ b/plugins/opercmds.py @@ -333,6 +333,29 @@ def jupe(irc, source, args): irc.reply("Done.") +def _try_find_target(irc, nick): + """ + Tries to find the target UID for the given nick, raising LookupError if it doesn't exist or is ambiguous. + """ + try: + int_u = int(nick) + except: + int_u = None + + if int_u and int_u in irc.users: + return int_u # Some protocols use numeric UIDs + elif nick in irc.users: + return nick + + potential_targets = irc.nick_to_uid(nick, multi=True) + if not potential_targets: + # Whatever we were told to kick doesn't exist! + raise LookupError("No such target %r." % nick) + elif len(potential_targets) > 1: + raise LookupError("Multiple users with the nick %r found: please select the right UID: %s" % (nick, str(potential_targets))) + else: + return potential_targets[0] + @utils.add_cmd def kick(irc, source, args): """ [] @@ -347,16 +370,11 @@ def kick(irc, source, args): irc.error("Not enough arguments. Needs 2-3: channel, target, reason (optional).") return - targetu = irc.nick_to_uid(target) - if channel not in irc.channels: # KICK only works on channels that exist. irc.error("Unknown channel %r." % channel) return - if not targetu: - # Whatever we were told to kick doesn't exist! - irc.error("No such target nick %r." % target) - return + targetu = _try_find_target(irc, target) sender = irc.pseudoclient.uid irc.kick(sender, channel, targetu, reason) @@ -379,17 +397,15 @@ def kill(irc, source, args): # Convert the source and target nicks to UIDs. sender = irc.pseudoclient.uid - targetu = irc.nick_to_uid(target) - userdata = irc.users.get(targetu) - if targetu not in irc.users: - # Whatever we were told to kick doesn't exist! - irc.error("No such nick %r." % target) - return - elif irc.pseudoclient.uid == targetu: + targetu = _try_find_target(irc, target) + + if irc.pseudoclient.uid == targetu: irc.error("Cannot kill the main PyLink client!") return + userdata = irc.users.get(targetu) + # Deliver a more complete kill reason if our target is a non-PyLink client. # We skip this for PyLink clients so that relayed kills don't get # "Killed (abc (...))" tacked on both here and by the receiving IRCd. @@ -473,23 +489,24 @@ def chghost(irc, source, args): """ Changes the visible host of the target user.""" - chgfield(irc, source, args, 'host') + _chgfield(irc, source, args, 'host') @utils.add_cmd def chgident(irc, source, args): """ Changes the ident of the target user.""" - chgfield(irc, source, args, 'ident') + _chgfield(irc, source, args, 'ident') @utils.add_cmd def chgname(irc, source, args): """ Changes the GECOS (realname) of the target user.""" - chgfield(irc, source, args, 'name', 'GECOS') + _chgfield(irc, source, args, 'name', 'GECOS') -def chgfield(irc, source, args, human_field, internal_field=None): +def _chgfield(irc, source, args, human_field, internal_field=None): + """Helper function for chghost/chgident/chgname.""" permissions.check_permissions(irc, source, ['opercmds.chg' + human_field]) try: target = args[0] @@ -499,10 +516,7 @@ def chgfield(irc, source, args, human_field, internal_field=None): return # Find the user - targetu = irc.nick_to_uid(target) - if targetu not in irc.users: - irc.error("No such nick %r." % target) - return + targetu = _try_find_target(irc, target) internal_field = internal_field or human_field.upper() irc.update_client(targetu, internal_field, new)