Ejemplo n.º 1
0
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.")
Ejemplo n.º 2
0
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'}])
Ejemplo n.º 3
0
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.")
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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."
    )
Ejemplo n.º 7
0
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'
        }
    ])
Ejemplo n.º 8
0
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'
        }
    ])
Ejemplo n.º 9
0
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'}])
Ejemplo n.º 10
0
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.')
Ejemplo n.º 11
0
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.")
Ejemplo n.º 12
0
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))
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
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'}])
Ejemplo n.º 15
0
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))
Ejemplo n.º 16
0
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:]
Ejemplo n.º 17
0
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)))
Ejemplo n.º 18
0
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'
        }
    ])
Ejemplo n.º 19
0
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))
Ejemplo n.º 20
0
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)
Ejemplo n.º 21
0
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)
Ejemplo n.º 22
0
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)))
Ejemplo n.º 23
0
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)
Ejemplo n.º 24
0
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.")
Ejemplo n.º 25
0
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'}])
Ejemplo n.º 26
0
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
Ejemplo n.º 27
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."""
    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)
Ejemplo n.º 28
0
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())
Ejemplo n.º 29
0
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.")
Ejemplo n.º 30
0
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'
        }
    ])