def raw(irc, source, args): """<text> Sends raw text to the IRC server. This command is not officially supported on non-Clientbot networks, where it requires a separate permission.""" if irc.protoname == 'clientbot': # exec.raw is included for backwards compatibility with PyLink 1.x perms = ['raw.raw', 'exec.raw'] else: perms = ['raw.raw.unsupported_network'] permissions.check_permissions(irc, source, perms) args = ' '.join(args) if not args.strip(): irc.reply('No text entered!') return # Note: This is loglevel debug so that we don't risk leaking things like # NickServ passwords on Clientbot networks. log.debug('(%s) Sending raw text %r to IRC for %s', irc.name, args, irc.get_hostmask(source)) irc.send(args) irc.reply("Done.")
def nick(irc, source, args): """[<target>] <newnick> Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client.""" permissions.check_permissions(irc, source, ['bots.nick']) try: nick = args[0] newnick = args[1] except IndexError: try: nick = irc.pseudoclient.nick newnick = args[0] except IndexError: irc.error("Not enough arguments. Needs 1-2: nick (optional), newnick.") return u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client) if newnick in ('0', u): # Allow /nick 0 to work newnick = u elif not irc.is_nick(newnick): irc.error('Invalid nickname %r.' % newnick) return elif not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)): irc.error("Cannot force nick changes for a protected PyLink services client.") return irc.nick(u, newnick) irc.reply("Done.") # Signal the nick change to other plugins irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
def _exec(irc, source, args, locals_dict=None): """<code> Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work. \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02""" permissions.check_permissions(irc, source, ['exec.exec']) # Allow using \n in the code, while escaping backslashes correctly otherwise. args = bytes(' '.join(args), 'utf-8').decode("unicode_escape") if not args.strip(): irc.reply('No code entered!') return log.info('(%s) Executing %r for %s', irc.name, args, irc.get_hostmask(source)) if locals_dict is None: locals_dict = locals() else: # Add irc, source, and args to the given locals_dict, to allow basic things like irc.reply() # to still work. locals_dict['irc'] = irc locals_dict['source'] = source locals_dict['args'] = args exec(args, globals(), locals_dict) irc.reply("Done.")
def _check_automode_access(irc, uid, channel, command): """Checks the caller's access to Automode.""" # Automode defines the following permissions, where <command> is either "manage", "list", # "sync", "clear", "remotemanage", "remotelist", "remotesync", "remoteclear": # - automode.<command> OR automode.<command>.*: ability to <command> automode on all channels. # - automode.<command>.relay_owned: ability to <command> automode on channels owned via Relay. # If Relay isn't loaded, this permission check FAILS. # - automode.<command>.#channel: ability to <command> automode on the given channel. # - automode.savedb: ability to save the automode DB. log.debug('(%s) Automode: checking access for %s/%s for %s capability on %s', irc.name, uid, irc.get_hostmask(uid), command, channel) baseperm = 'automode.%s' % command try: # First, check the catch all and channel permissions. perms = [baseperm, baseperm+'.*', '%s.%s' % (baseperm, channel)] return permissions.check_permissions(irc, uid, perms) except utils.NotAuthorizedError: if not command.startswith('remote'): # Relay-based ACL checking only works with local calls. log.debug('(%s) Automode: falling back to automode.%s.relay_owned', irc.name, command) permissions.check_permissions(irc, uid, [baseperm+'.relay_owned'], also_show=perms) relay = world.plugins.get('relay') if relay is None: raise utils.NotAuthorizedError("You are not authorized to use Automode when Relay is " "disabled. You are missing one of the following " "permissions: %s or %s.%s" % (baseperm, baseperm, channel)) elif (irc.name, channel) not in relay.db: raise utils.NotAuthorizedError("The network you are on does not own the relay channel %s." % channel) return True raise
def reloadproto(irc, source, args): """<protocol module name> Reloads the given protocol module without restart. You will have to manually disconnect and reconnect any network using the module for changes to apply.""" permissions.check_permissions(irc, source, ['networks.reloadproto']) try: name = args[0] except IndexError: irc.error('Not enough arguments (needs 1: protocol module name)') return # Reload the dependency libraries first importlib.reload(pylinkirc.classes) log.debug('networks.reloadproto: reloading %s', pylinkirc.classes) for common_name in pylinkirc.protocols.common_modules: module = utils._get_protocol_module(common_name) log.debug('networks.reloadproto: reloading %s', module) importlib.reload(module) proto = utils._get_protocol_module(name) log.debug('networks.reloadproto: reloading %s', proto) importlib.reload(proto) irc.reply( "Done. You will have to manually disconnect and reconnect any network using the %r module for changes to apply." % name)
def disconnect(irc, source, args): """<network> Disconnects the network <network>. When all networks are disconnected, PyLink will automatically exit. To reconnect a network disconnected using this command, use REHASH to reload the networks list.""" permissions.check_permissions(irc, source, ['networks.disconnect']) try: netname = args[0] network = world.networkobjects[netname] except IndexError: # No argument given. irc.error( 'Not enough arguments (needs 1: network name (case sensitive)).') return except KeyError: # Unknown network. irc.error('No such network "%s" (case sensitive).' % netname) return if network.has_cap('virtual-server'): irc.error( '"%s" is a virtual server and cannot be directly disconnected.' % netname) return log.info('Disconnecting network %r per %s', netname, irc.get_hostmask(source)) control.remove_network(network) irc.reply( "Done. If you want to reconnect this network, use the 'rehash' command." )
def kick(irc, source, args): """<channel> <user> [<reason>] Kicks <user> from the specified channel.""" permissions.check_permissions(irc, source, ['opercmds.kick']) try: channel = args[0] target = args[1] reason = ' '.join(args[2:]) except IndexError: irc.error( "Not enough arguments. Needs 2-3: channel, target, reason (optional)." ) return if channel not in irc.channels: # KICK only works on channels that exist. irc.error("Unknown channel %r." % channel) return targetu = _try_find_target(irc, target) sender = irc.pseudoclient.uid irc.kick(sender, channel, targetu, reason) irc.reply("Done.") irc.call_hooks([ sender, 'OPERCMDS_KICK', { 'channel': channel, 'target': targetu, 'text': reason, 'parse_as': 'KICK' } ])
def topic(irc, source, args): """<channel> <topic> Changes the topic in a channel.""" permissions.check_permissions(irc, source, ['opercmds.topic']) try: channel = args[0] topic = ' '.join(args[1:]) except IndexError: irc.error("Not enough arguments. Needs 2: channel, topic.") return if channel not in irc.channels: irc.error("Unknown channel %r." % channel) return irc.topic(irc.pseudoclient.uid, channel, topic) irc.reply("Done.") irc.call_hooks([ irc.pseudoclient.uid, 'OPERCMDS_TOPIC', { 'channel': channel, 'text': topic, 'setter': source, 'parse_as': 'TOPIC' } ])
def quit(irc, source, args): """<target> [<reason>] Quits the PyLink client with nick <target>, if one exists.""" permissions.check_permissions(irc, source, ['bots.quit']) try: nick = args[0] except IndexError: irc.error("Not enough arguments. Needs 1-2: nick, reason (optional).") return 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' if not irc.is_manipulatable_client(u): irc.error("Cannot force quit a protected PyLink services client.") return irc.quit(u, quitmsg) irc.reply("Done.") irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}])
def save(irc, source, args): """takes no arguments. Saves the Automode database to disk.""" permissions.check_permissions(irc, source, ['automode.savedb']) datastore.save() reply(irc, 'Done.')
def jupe(irc, source, args): """<server> [<reason>] Jupes the given server.""" permissions.check_permissions(irc, source, ['opercmds.jupe']) try: servername = args[0] reason = ' '.join(args[1:]) or "No reason given" desc = "Juped by %s: [%s]" % (irc.get_hostmask(source), reason) except IndexError: irc.error( 'Not enough arguments. Needs 1-2: servername, reason (optional).') return if not irc.is_server_name(servername): irc.error("Invalid server name %r." % servername) return sid = irc.spawn_server(servername, desc=desc) irc.call_hooks([ irc.pseudoclient.uid, 'OPERCMDS_SPAWNSERVER', { 'name': servername, 'sid': sid, 'text': desc } ]) irc.reply("Done.")
def g(irc, source, args): """<message text> Sends out a Instance-wide notice. """ permissions.check_permissions(irc, source, ["global.global"]) message = " ".join(args).strip() if not message: irc.error("Refusing to send an empty message.") return global_conf = conf.conf.get('global') or {} template = string.Template(global_conf.get('format', DEFAULT_FORMAT)) exempt_channels = set(global_conf.get('exempt_channels', set())) netcount = 0 chancount = 0 for netname, ircd in world.networkobjects.items(): # Skip networks that aren't ready and dummy networks which don't have .pseudoclient set if ircd.connected.is_set() and ircd.pseudoclient: netcount += 1 for channel in ircd.pseudoclient.channels: local_exempt_channels = exempt_channels | set( ircd.serverdata.get('global_exempt_channels', set())) skip = False for exempt in local_exempt_channels: if ircd.match_text(exempt, str(channel)): log.debug( 'global: Skipping channel %s%s for exempt %r', netname, channel, exempt) skip = True break if skip: continue subst = { 'sender': irc.get_friendly_name(source), 'network': irc.name, 'fullnetwork': irc.get_full_network_name(), 'current_channel': channel, 'current_network': netname, 'current_fullnetwork': ircd.get_full_network_name(), 'text': message } # Disable relaying or other plugins handling the global message. ircd.msg(channel, template.safe_substitute(subst), loopback=False) chancount += 1 irc.reply('Done. Sent to %d channels across %d networks.' % (chancount, netcount))
def handle_stats(irc, source, command, args): """/STATS handler. Currently supports the following: c - link blocks o - oper blocks (accounts) u - shows uptime """ stats_type = args['stats_type'][0].lower( ) # stats_type shouldn't be more than 1 char anyways perms = ['stats.%s' % stats_type] if stats_type == 'u': perms.append('stats.uptime') # Consistency try: permissions.check_permissions(irc, source, perms) except utils.NotAuthorizedError as e: # Note, no irc.error() because this is not a command, but a handler irc.msg(source, 'Error: %s' % e, notice=True) return log.info('(%s) /STATS %s requested by %s', irc.name, stats_type, irc.get_hostmask(source)) def _num(num, text): irc.numeric(args['target'], num, source, text) if stats_type == 'c': # 213/RPL_STATSCLINE: "C <host> * <name> <port> <class>" for netname, serverdata in sorted(conf.conf['servers'].items()): # We're cramming as much as we can into the class field... _num( 213, "C %s * %s %s [%s:%s:%s]" % (serverdata.get('ip', '0.0.0.0'), netname, serverdata.get('port', 0), serverdata['protocol'], 'ssl' if serverdata.get('ssl') else 'no-ssl', serverdata.get('encoding', 'utf-8'))) elif stats_type == 'o': # 243/RPL_STATSOLINE: "O <hostmask> * <nick> [:<info>]" # New style accounts only! for accountname, accountdata in conf.conf['login'].get('accounts', {}).items(): networks = accountdata.get('networks', []) if irc.name in networks or not networks: hosts = ' '.join(accountdata.get('hosts', ['*@*'])) needoper = 'needoper' if accountdata.get( 'require_oper') else '' _num(243, "O %s * %s :%s" % (hosts, accountname, needoper)) elif stats_type == 'u': # 242/RPL_STATSUPTIME: ":Server Up <days> days <hours>:<minutes>:<seconds>" _num(242, ':Server Up %s' % timediff(world.start_ts, int(time.time()))) else: log.info('(%s) Unknown /STATS type %r requested by %s', irc.name, stats_type, irc.get_hostmask(source)) _num(219, "%s :End of /STATS report" % stats_type)
def msg(irc, source, args): """[<source>] <target> <text> Sends message <text> from <source>, where <source> is the nick of a PyLink client. If <source> is not given, it defaults to the main PyLink client.""" permissions.check_permissions(irc, source, ['bots.msg']) # Because we want the source nick to be optional, this argument parsing gets a bit tricky. try: msgsource = args[0] target = args[1] text = ' '.join(args[2:]) # 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, filterfunc=irc.is_internal_client) if sourceuid is None or not text: # First argument isn't one of our clients raise IndexError except IndexError: try: sourceuid = irc.pseudoclient.uid target = args[0] text = ' '.join(args[1:]) except IndexError: irc.error('Not enough arguments. Needs 2-3: source nick (optional), target, text.') return if not text: irc.error('No text given.') return 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 irc.message(sourceuid, real_target, text) irc.reply("Done.") irc.call_hooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
def echo(irc, source, args): """<text> Echoes the text given.""" permissions.check_permissions(irc, source, ['commands.echo']) if not args: irc.error('No text to send!') return irc.reply(' '.join(args))
def showchan(irc, source, args): """<channel> Shows information about <channel>.""" permissions.check_permissions(irc, source, ['commands.showchan']) try: channel = args[0] except IndexError: irc.error("Not enough arguments. Needs 1: channel.") return if channel not in irc.channels: irc.error('Unknown channel %r.' % channel) return f = lambda s: irc.reply(s, private=True) c = irc.channels[channel] # Only show verbose info if caller is oper or is in the target channel. verbose = source in c.users or irc.is_oper(source) secret = ('s', None) in c.modes if secret and not verbose: # Hide secret channels from normal users. irc.error('Unknown channel %r.' % channel) return nicks = [irc.users[u].nick for u in c.users] f('Information on channel \x02%s\x02:' % channel) if c.topic: f('\x02Channel topic\x02: %s' % c.topic) # Mark TS values as untrusted on Clientbot and others (where TS is read-only or not trackable) f('\x02Channel creation time\x02: %s (%s) [UTC]%s' % (time.asctime(time.gmtime(int( c.ts))), c.ts, ' [UNTRUSTED]' if not irc.has_cap('has-ts') else '')) # Show only modes that aren't list-style modes. modes = irc.join_modes( [m for m in c.modes if m[0] not in irc.cmodes['*A']], sort=True) f('\x02Channel modes\x02: %s' % modes) if verbose: nicklist = [] # Iterate over the user list, sorted by nick. for user, nick in sorted(zip(c.users, nicks), key=lambda userpair: userpair[1].lower()): # Note: reversed() is used here because we're adding prefixes onto the nick in reverse for pmode in reversed(c.get_prefix_modes(user)): # Show prefix modes in order from highest to lowest. nick = irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '') + nick nicklist.append(nick) while nicklist[:20]: # 20 nicks per line to prevent message cutoff. f('\x02User list\x02: %s' % ' '.join(nicklist[:20])) nicklist = nicklist[20:]
def status(irc, source, args): """takes no arguments. Returns your current PyLink login status.""" permissions.check_permissions(irc, source, ['commands.status']) identified = irc.users[source].account if identified: irc.reply('You are identified as \x02%s\x02.' % identified) else: irc.reply('You are not identified as anyone.') irc.reply('Operator access: \x02%s\x02' % bool(irc.is_oper(source)))
def part(irc, source, args): """[<target>] <channel1>,[<channel2>],... [<reason>] Parts <target>, the nick of a PyLink client, from a comma-separated list of channels. If <target> is not given, it defaults to the main PyLink client.""" permissions.check_permissions(irc, source, ['bots.part']) try: nick = args[0] clist = args[1] # For the part message, join all remaining arguments into one text string reason = ' '.join(args[2:]) # 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, 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. u = irc.pseudoclient.uid try: clist = args[0] except IndexError: irc.error( "Not enough arguments. Needs 1-2: nick (optional), comma separated list of channels." ) return reason = ' '.join(args[1:]) clist = clist.split(',') if not clist: irc.error("No valid channels given.") return if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)): irc.error("Cannot force part a protected PyLink services client.") return for channel in clist: if not irc.is_channel(channel): irc.error("Invalid channel name %r." % channel) return irc.part(u, channel, reason) irc.reply("Done.") irc.call_hooks([ u, 'PYLINK_BOTSPLUGIN_PART', { 'channels': clist, 'text': reason, 'parse_as': 'PART' } ])
def checkban(irc, source, args, use_regex=False): """<banmask> [<target nick or hostmask>] [--channel #channel] [--maxresults <num>] CHECKBAN provides a ban checker command based on nick!user@host masks, user@host masks, and PyLink extended targets. If a target nick or hostmask is given, this command returns whether the given banmask will match it. Otherwise, it will display a list of connected users matching the banmask. If the --channel argument is given without a target mask, the returned results will only include users in the given channel. The --maxresults option configures how many responses will be shown.""" permissions.check_permissions(irc, source, ['opercmds.checkban']) args = checkban_parser.parse_args(args) if not args.target: # No hostmask was given, return a list of matched users. results = 0 userlist_func = irc.match_all_re if use_regex else irc.match_all irc.reply("Checking for hosts that match \x02%s\x02:" % args.banmask, private=True) for uid in userlist_func(args.banmask, channel=args.channel): if results < args.maxresults: userobj = irc.users[uid] s = "\x02%s\x02 (%s@%s) [%s] {\x02%s\x02}" % ( userobj.nick, userobj.ident, userobj.host, userobj.realname, irc.get_friendly_name( irc.get_server(uid))) # Always reply in private to prevent information leaks. irc.reply(s, private=True) results += 1 else: if results: irc.reply("\x02%s\x02 out of \x02%s\x02 results shown." % (min([results, args.maxresults]), results), private=True) else: irc.reply("No results found.", private=True) else: # Target can be both a nick (of an online user) or a hostmask. irc.match_host() handles this # automatically. if irc.match_host(args.banmask, args.target): irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (args.target, args.banmask)) else: irc.reply('No, \x02%s\x02 does not match \x02%s\x02.' % (args.target, args.banmask))
def showuser(irc, source, args): """<user> Shows information about <user>.""" permissions.check_permissions(irc, source, ['commands.showuser']) target = ' '.join(args) if not target: irc.error("Not enough arguments. Needs 1: nick.") return users = irc.nick_to_uid(target, multi=True) or [target] for user in users: _do_showuser(irc, source, user)
def checkbanre(irc, source, args): """<regular expression> [<target nick or hostmask>] [--channel #channel] [--maxresults <num>] CHECKBANRE provides a ban checker command based on regular expressions matched against users' "nick!user@host [gecos]" mask. If a target nick or hostmask is given, this command returns whether the given banmask will match it. Otherwise, it will display a list of connected users matching the banmask. If the --channel argument is given without a target mask, the returned results will only include users in the given channel. The --maxresults option configures how many responses will be shown.""" permissions.check_permissions(irc, source, ['opercmds.checkban.re']) return checkban(irc, source, args, use_regex=True)
def inject(irc, source, args): """<text> Admin-only. Injects raw text into the running PyLink protocol module, replying with the hook data returned. \x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02""" permissions.check_permissions(irc, source, ['exec.inject']) args = ' '.join(args) if not args.strip(): irc.reply('No text entered!') return log.info('(%s) Injecting raw text %r into protocol module for %s', irc.name, args, irc.get_hostmask(source)) irc.reply(repr(irc.parse_irc_command(args)))
def massbanre(irc, source, args): """<channel> <regular expression> [<kick reason>] [--quiet/-q] [--include-opers/-o] Bans users on the specified channel whose "nick!user@host [gecos]" mask matches the given Python-style regular expression. (https://docs.python.org/3/library/re.html#regular-expression-syntax describes supported syntax) The --quiet option can also be given to mass-mute the given user on networks where this is supported (currently ts6, unreal, and inspircd). No kicks will be sent in this case. By default, this command will ignore opers. This behaviour can be suppressed using the --include-opers option. \x02Be careful when using this command, as it is easy to make mistakes with regex. Use 'checkbanre' to check your bans first!\x02 """ permissions.check_permissions(irc, source, ['opercmds.massban.re']) return massban(irc, source, args, use_regex=True)
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] new = args[1] except IndexError: irc.error("Not enough arguments. Needs 2: target, new %s." % human_field) return # Find the user targetu = _try_find_target(irc, target) internal_field = internal_field or human_field.upper() irc.update_client(targetu, internal_field, new) irc.reply("Done.")
def msg(irc, source, args): """[<source>] <target> <text> Sends message <text> from <source>, where <source> is the nick of a PyLink client. If <source> is not given, it defaults to the main PyLink client.""" permissions.check_permissions(irc, source, ['bots.msg']) # Because we want the source nick to be optional, this argument parsing gets a bit tricky. try: msgsource = args[0] target = args[1] text = ' '.join(args[2:]) # 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 raise IndexError if not text: raise IndexError except IndexError: try: sourceuid = irc.pseudoclient.uid target = args[0] text = ' '.join(args[1:]) except IndexError: irc.error('Not enough arguments. Needs 2-3: source nick (optional), target, text.') return if not text: 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 irc.error('Unknown user %r.' % target) return else: real_target = target irc.message(sourceuid, real_target, text) irc.reply("Done.") irc.call_hooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
def _check_logout_access(irc, source, target, perms): """ Checks whether the source UID has access to log out the target UID. This returns True if the source user has a permission specified, or if the source and target are both logged in and have the same account. """ assert source in irc.users, "Unknown source user" assert target in irc.users, "Unknown target user" try: permissions.check_permissions(irc, source, perms) except utils.NotAuthorizedError: if irc.users[source].account and (irc.users[source].account == irc.users[target].account): return True else: raise else: return True
def loglevel(irc, source, args): """<level> Sets the log level to the given <level>. <level> must be either DEBUG, INFO, WARNING, ERROR, or CRITICAL. If no log level is given, shows the current one.""" permissions.check_permissions(irc, source, ['commands.loglevel']) try: level = args[0].upper() try: loglevel = loglevels[level] except KeyError: irc.error('Unknown log level "%s".' % level) return else: world.console_handler.setLevel(loglevel) irc.reply("Done.") except IndexError: irc.reply(world.console_handler.level)
def threadinfo(irc, source, args): """takes no arguments. Lists all threads currently present in this PyLink instance.""" permissions.check_permissions(irc, source, ['exec.threadinfo']) for t in sorted(threading.enumerate(), key=lambda t: t.name.lower()): name = t.name # Unnamed threads are something we want to avoid throughout PyLink. if name.startswith('Thread-'): name = '\x0305%s\x03' % t.name # Also VERY bad: remaining threads for networks not in the networks index anymore! elif name.startswith(('Listener for', 'Ping timer loop for', 'Queue thread for')) and name.rsplit(" ", 1)[-1] not in world.networkobjects: name = '\x0304%s\x03' % t.name irc.reply('\x02%s\x02[%s]: daemon=%s; alive=%s' % (name, t.ident, t.daemon, t.is_alive()), private=True) irc.reply("Total of %s threads." % threading.active_count())
def spawnclient(irc, source, args): """<nick> <ident> <host> Spawns the specified client on the PyLink server. Note: this doesn't check the validity of any fields you give it!""" if not irc.has_cap('can-spawn-clients'): irc.error("This network does not support client spawning.") return permissions.check_permissions(irc, source, ['bots.spawnclient']) try: nick, ident, host = args[:3] except ValueError: irc.error("Not enough arguments. Needs 3: nick, user, host.") return irc.spawn_client(nick, ident, host, manipulatable=True) irc.reply("Done.")
def kill(irc, source, args): """<target> [<reason>] Kills the given target.""" permissions.check_permissions(irc, source, ['opercmds.kill']) try: target = args[0] reason = ' '.join(args[1:]) except IndexError: irc.error( "Not enough arguments. Needs 1-2: target, reason (optional).") return # 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: irc.error("Cannot kill the main PyLink client!") return # 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. if not irc.is_internal_client(targetu): reason = "Killed (%s (Requested by %s: %s))" % (irc.get_friendly_name( sender), irc.get_friendly_name(source), reason) irc.kill(sender, targetu, reason) irc.reply("Done.") irc.call_hooks([ source, 'OPERCMDS_KILL', { 'target': targetu, 'text': reason, 'userdata': userdata, 'parse_as': 'KILL' } ])