Ejemplo n.º 1
0
def msg(irc, source, args):
    """<source> <target> <text>

    Admin-only. Sends message <text> from <source>, where <source> is the nick of a PyLink client."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        msgsource, target, text = args[0], args[1], ' '.join(args[2:])
    except IndexError:
        irc.reply(
            'Error: Not enough arguments. Needs 3: source nick, target, text.')
        return
    sourceuid = utils.nickToUid(irc, msgsource)
    if not sourceuid:
        irc.reply('Error: Unknown user %r.' % msgsource)
        return
    if not utils.isChannel(target):
        real_target = utils.nickToUid(irc, target)
        if real_target is None:
            irc.reply('Error: Unknown user %r.' % target)
            return
    else:
        real_target = target
    if not text:
        irc.reply('Error: No text given.')
        return
    irc.proto.messageClient(sourceuid, real_target, text)
    irc.callHooks([
        sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {
            'target': real_target,
            'text': text,
            'parse_as': 'PRIVMSG'
        }
    ])
Ejemplo n.º 2
0
def part(irc, source, args):
    """<target> <channel1>,[<channel2>],... [<reason>]

    Admin-only. Parts <target>, the nick of a PyLink client, from a comma-separated list of channels."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        nick = args[0]
        clist = args[1].split(',')
        reason = ' '.join(args[2:])
    except IndexError:
        irc.reply(
            "Error: Not enough arguments. Needs 2: nick, comma separated list of channels."
        )
        return
    u = utils.nickToUid(irc, nick)
    if not utils.isManipulatableClient(irc, u):
        irc.reply(
            "Error: Cannot force part a protected PyLink services client.")
        return
    for channel in clist:
        if not utils.isChannel(channel):
            irc.reply("Error: Invalid channel name %r." % channel)
            return
        irc.proto.partClient(u, channel, reason)
    irc.callHooks([
        u, 'PYLINK_BOTSPLUGIN_PART', {
            'channels': clist,
            'text': reason,
            'parse_as': 'PART'
        }
    ])
Ejemplo n.º 3
0
def quit(irc, source, args):
    """<target> [<reason>]

    Admin-only. Quits the PyLink client with nick <target>, if one exists."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        nick = args[0]
    except IndexError:
        irc.reply(
            "Error: Not enough arguments. Needs 1-2: nick, reason (optional).")
        return
    if irc.pseudoclient.uid == utils.nickToUid(irc, nick):
        irc.reply("Error: Cannot quit the main PyLink PseudoClient!")
        return
    u = utils.nickToUid(irc, nick)
    quitmsg = ' '.join(args[1:]) or 'Client Quit'
    if not utils.isManipulatableClient(irc, u):
        irc.reply(
            "Error: Cannot force quit a protected PyLink services client.")
        return
    irc.proto.quitClient(u, quitmsg)
    irc.callHooks(
        [u, 'PYLINK_BOTSPLUGIN_QUIT', {
            'text': quitmsg,
            'parse_as': 'QUIT'
        }])
Ejemplo n.º 4
0
def load(irc, source, args):
    """<plugin name>.

    Loads a plugin from the plugin folder."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        name = args[0]
    except IndexError:
        irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
        return
    if name in world.plugins:
        irc.reply("Error: %r is already loaded." % name)
        return
    log.info('(%s) Loading plugin %r for %s', irc.name, name, utils.getHostmask(irc, source))
    try:
        world.plugins[name] = pl = utils.loadModuleFromFolder(name, world.plugins_folder)
    except ImportError as e:
        if str(e) == ('No module named %r' % name):
            log.exception('Failed to load plugin %r: The plugin could not be found.', name)
        else:
            log.exception('Failed to load plugin %r: ImportError.', name)
        raise
    else:
        if hasattr(pl, 'main'):
            log.debug('Calling main() function of plugin %r', pl)
            pl.main(irc)
    irc.reply("Loaded plugin %r." % name)
Ejemplo n.º 5
0
def joinclient(irc, source, args):
    """<target> <channel1>,[<channel2>], etc.

    Admin-only. Joins <target>, the nick of a PyLink client, to a comma-separated list of channels."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        nick = args[0]
        clist = args[1].split(',')
        if not clist:
            raise IndexError
    except IndexError:
        irc.reply(
            "Error: Not enough arguments. Needs 2: nick, comma separated list of channels."
        )
        return
    u = utils.nickToUid(irc, nick)
    if not utils.isManipulatableClient(irc, u):
        irc.reply(
            "Error: Cannot force join a protected PyLink services client.")
        return
    for channel in clist:
        if not utils.isChannel(channel):
            irc.reply("Error: Invalid channel name %r." % channel)
            return
        irc.proto.joinClient(u, channel)
        irc.callHooks([
            u, 'PYLINK_BOTSPLUGIN_JOIN', {
                'channel': channel,
                'users': [u],
                'modes': irc.channels[channel].modes,
                'parse_as': 'JOIN'
            }
        ])
Ejemplo n.º 6
0
def kick(irc, source, args):
    """<source> <channel> <user> [<reason>]

    Admin-only. Kicks <user> from <channel> via <source>, where <source> is the nick of a PyLink client."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        nick = args[0]
        channel = args[1]
        target = args[2]
        reason = ' '.join(args[3:])
    except IndexError:
        irc.reply(
            "Error: Not enough arguments. Needs 3-4: source nick, channel, target, reason (optional)."
        )
        return
    u = utils.nickToUid(irc, nick) or nick
    targetu = utils.nickToUid(irc, target)
    if not utils.isChannel(channel):
        irc.reply("Error: Invalid channel name %r." % channel)
        return
    if utils.isInternalServer(irc, u):
        irc.proto.kickServer(u, channel, targetu, reason)
    else:
        irc.proto.kickClient(u, channel, targetu, reason)
    irc.callHooks([
        u, 'PYLINK_BOTSPLUGIN_KICK', {
            'channel': channel,
            'target': targetu,
            'text': reason,
            'parse_as': 'KICK'
        }
    ])
Ejemplo n.º 7
0
def nick(irc, source, args):
    """<target> <newnick>

    Admin-only. Changes the nick of <target>, a PyLink client, to <newnick>."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        nick = args[0]
        newnick = args[1]
    except IndexError:
        irc.reply("Error: Not enough arguments. Needs 2: nick, newnick.")
        return
    u = utils.nickToUid(irc, nick)
    if newnick in ('0', u):
        newnick = u
    elif not utils.isNick(newnick):
        irc.reply('Error: Invalid nickname %r.' % newnick)
        return
    elif not utils.isManipulatableClient(irc, u):
        irc.reply(
            "Error: Cannot force nick changes for a protected PyLink services client."
        )
        return
    irc.proto.nickClient(u, newnick)
    irc.callHooks([
        u, 'PYLINK_BOTSPLUGIN_NICK', {
            'newnick': newnick,
            'oldnick': nick,
            'parse_as': 'NICK'
        }
    ])
Ejemplo n.º 8
0
def spawnclient(irc, source, args):
    """<nick> <ident> <host>

    Admin-only. Spawns the specified PseudoClient on the PyLink server.
    Note: this doesn't check the validity of any fields you give it!"""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        nick, ident, host = args[:3]
    except ValueError:
        irc.reply("Error: Not enough arguments. Needs 3: nick, user, host.")
        return
    irc.proto.spawnClient(nick, ident, host, manipulatable=True)
Ejemplo n.º 9
0
def shutdown(irc, source, args):
    """takes no arguments.

    Exits PyLink by disconnecting all networks."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    u = irc.users[source]
    log.info('(%s) SHUTDOWN requested by "%s!%s@%s", exiting...', irc.name, u.nick,
             u.ident, u.host)
    for ircobj in world.networkobjects.values():
        # Disable auto-connect first by setting the time to negative.
        ircobj.serverdata['autoconnect'] = -1
        ircobj.aborted.set()
Ejemplo n.º 10
0
def _eval(irc, source, args):
    """<Python expression>

    Admin-only. Evaluates the given Python expression and returns the result.
    \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
    utils.checkAuthenticated(irc, source, allowOper=False)
    args = ' '.join(args)
    if not args.strip():
        irc.reply('No code entered!')
        return
    log.info('(%s) Evaluating %r for %s', irc.name, args,
             utils.getHostmask(irc, source))
    irc.reply(eval(args))
Ejemplo n.º 11
0
def _exec(irc, source, args):
    """<code>

    Admin-only. Executes <code> in the current PyLink instance.
    \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
    utils.checkAuthenticated(irc, source, allowOper=False)
    args = ' '.join(args)
    if not args.strip():
        irc.reply('No code entered!')
        return
    log.info('(%s) Executing %r for %s', irc.name, args,
             utils.getHostmask(irc, source))
    exec(args, globals(), locals())
Ejemplo n.º 12
0
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."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        level = args[0].upper()
        try:
            loglevel = loglevels[level]
        except KeyError:
            irc.reply('Error: Unknown log level "%s".' % level)
            return
        else:
            log.setLevel(loglevel)
            irc.reply("Done.")
    except IndexError:
        irc.reply(log.getEffectiveLevel())
Ejemplo n.º 13
0
def disconnect(irc, source, args):
    """<network>

    Disconnects the network <network>. When all networks are disconnected, PyLink will automatically exit.
    Note: This does not affect the autoreconnect settings of any network, so the network will likely just reconnect unless autoconnect is disabled (see the 'autoconnect' command)."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        netname = args[0]
        network = world.networkobjects[netname]
    except IndexError:  # No argument given.
        irc.reply(
            'Error: Not enough arguments (needs 1: network name (case sensitive)).'
        )
        return
    except KeyError:  # Unknown network.
        irc.reply('Error: No such network "%s" (case sensitive).' % netname)
        return
    irc.reply("Done.")
    # Abort the connection! Simple as that.
    network.aborted.set()
Ejemplo n.º 14
0
def rehash(irc, source, args):
    """takes no arguments.

    Reloads the configuration file for PyLink, (dis)connecting added/removed networks.
    Plugins must be manually reloaded."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    old_conf = conf.conf.copy()
    fname = conf.fname
    try:
        new_conf = conf.loadConf(fname, errors_fatal=False)
    except Exception as e:  # Something went wrong, abort.
        log.exception("Error REHASH'ing config: ")
        irc.reply("Error loading configuration file: %s: %s" %
                  (type(e).__name__, e))
        return
    else:
        new_conf = conf.validateConf(new_conf)
        conf.conf = new_conf
        for network, ircobj in world.networkobjects.copy().items():
            # Server was removed from the config file, disconnect them.
            log.debug('(%s) rehash: checking if %r is in new conf still.',
                      irc.name, network)
            if network not in new_conf['servers']:
                log.debug(
                    '(%s) rehash: removing connection to %r (removed from config).',
                    irc.name, network)
                # Disable autoconnect first.
                ircobj.serverdata['autoconnect'] = -1
                ircobj.aborted.set()
                del world.networkobjects[network]
            else:
                ircobj.conf = new_conf
                ircobj.serverdata = new_conf['servers'][network]
                ircobj.botdata = new_conf['bot']
        for network, sdata in new_conf['servers'].items():
            # New server was added. Connect them if not already connected.
            if network not in world.networkobjects:
                proto = utils.getProtoModule(sdata['protocol'])
                world.networkobjects[network] = classes.Irc(
                    network, proto, new_conf)
        irc.reply("Done.")
Ejemplo n.º 15
0
def mode(irc, source, args):
    """<source> <target> <modes>

    Admin-only. Sets modes <modes> on <target> from <source>, where <source> is either the nick of a PyLink client, or the SID of a PyLink server. <target> can be either a nick or a channel."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        modesource, target, modes = args[0], args[1], args[2:]
    except IndexError:
        irc.reply(
            'Error: Not enough arguments. Needs 3: source nick, target, modes to set.'
        )
        return
    target = utils.nickToUid(irc, target) or target
    extclient = target in irc.users and not utils.isInternalClient(irc, target)
    parsedmodes = utils.parseModes(irc, target, modes)
    ischannel = target in irc.channels
    if not (target in irc.users or ischannel):
        irc.reply("Error: Invalid channel or nick %r." % target)
        return
    elif not parsedmodes:
        irc.reply("Error: No valid modes were given.")
        return
    elif not (ischannel or utils.isManipulatableClient(irc, target)):
        irc.reply(
            "Error: Can only set modes on channels or non-protected PyLink clients."
        )
        return
    if utils.isInternalServer(irc, modesource):
        # Setting modes from a server.
        irc.proto.modeServer(modesource, target, parsedmodes)
    else:
        # Setting modes from a client.
        modesource = utils.nickToUid(irc, modesource)
        irc.proto.modeClient(modesource, target, parsedmodes)
    irc.callHooks([
        modesource, 'PYLINK_BOTSPLUGIN_MODE', {
            'target': target,
            'modes': parsedmodes,
            'parse_as': 'MODE'
        }
    ])
Ejemplo n.º 16
0
def autoconnect(irc, source, args):
    """<network> <seconds>

    Sets the autoconnect time for <network> to <seconds>.
    You can disable autoconnect for a network by setting <seconds> to a negative value."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        netname = args[0]
        seconds = float(args[1])
        network = world.networkobjects[netname]
    except IndexError:  # Arguments not given.
        irc.reply(
            'Error: Not enough arguments (needs 2: network name (case sensitive), autoconnect time (in seconds)).'
        )
        return
    except KeyError:  # Unknown network.
        irc.reply('Error: No such network "%s" (case sensitive).' % netname)
        return
    except ValueError:
        irc.reply('Error: Invalid argument "%s" for <seconds>.' % seconds)
        return
    network.serverdata['autoconnect'] = seconds
    irc.reply("Done.")
Ejemplo n.º 17
0
def connect(irc, source, args):
    """<network>

    Initiates a connection to the network <network>."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        netname = args[0]
        network = world.networkobjects[netname]
    except IndexError:  # No argument given.
        irc.reply(
            'Error: Not enough arguments (needs 1: network name (case sensitive)).'
        )
        return
    except KeyError:  # Unknown network.
        irc.reply('Error: No such network "%s" (case sensitive).' % netname)
        return
    if network.connection_thread.is_alive():
        irc.reply('Error: Network "%s" seems to be already connected.' %
                  netname)
    else:  # Reconnect the network!
        network.initVars()
        network.connection_thread = threading.Thread(target=network.connect)
        network.connection_thread.start()
        irc.reply("Done.")
Ejemplo n.º 18
0
def unload(irc, source, args):
    """<plugin name>.

    Unloads a currently loaded plugin."""
    utils.checkAuthenticated(irc, source, allowOper=False)
    try:
        name = args[0]
    except IndexError:
        irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
        return
    if name in world.plugins:
        log.info('(%s) Unloading plugin %r for %s', irc.name, name, utils.getHostmask(irc, source))
        pl = world.plugins[name]
        log.debug('sys.getrefcount of plugin %s is %s', pl, sys.getrefcount(pl))
        # Remove any command functions set by the plugin.
        for cmdname, cmdfuncs in world.commands.copy().items():
            log.debug('cmdname=%s, cmdfuncs=%s', cmdname, cmdfuncs)
            for cmdfunc in cmdfuncs:
                log.debug('__module__ of cmdfunc %s is %s', cmdfunc, cmdfunc.__module__)
                if cmdfunc.__module__ == name:
                    log.debug('Removing %s from world.commands[%s]', cmdfunc, cmdname)
                    world.commands[cmdname].remove(cmdfunc)
                    # If the cmdfunc list is empty, remove it.
                    if not cmdfuncs:
                        log.debug("Removing world.commands[%s] (it's empty now)", cmdname)
                        del world.commands[cmdname]

        # Remove any command hooks set by the plugin.
        for hookname, hookfuncs in world.hooks.copy().items():
            for hookfunc in hookfuncs:
                if hookfunc.__module__ == name:
                    world.hooks[hookname].remove(hookfunc)
                    # If the hookfuncs list is empty, remove it.
                    if not hookfuncs:
                        del world.hooks[hookname]

        # Remove whois handlers too.
        for f in world.whois_handlers:
            if f.__module__ == name:
                world.whois_handlers.remove(f)

        # Call the die() function in the plugin, if present.
        if hasattr(pl, 'die'):
            try:
                pl.die(irc)
            except:  # But don't allow it to crash the server.
                log.exception('(%s) Error occurred in die() of plugin %s, skipping...', irc.name, pl)

        # Delete it from memory (hopefully).
        del world.plugins[name]
        if name in sys.modules:
            del sys.modules[name]
        if name in globals():
            del globals()[name]

        # Garbage collect.
        gc.collect()

        irc.reply("Unloaded plugin %r." % name)
        return True  # We succeeded, make it clear (this status is used by reload() below)
    else:
        irc.reply("Unknown plugin %r." % name)