Beispiel #1
0
def handle_names(bot, trigger):
    """Handle NAMES response, happens when joining to channels."""
    names = trigger.split()

    #TODO specific to one channel type. See issue 281.
    channels = re.search('(#\S*)', trigger.raw)
    if not channels:
        return
    channel = Identifier(channels.group(1))
    if channel not in bot.privileges:
        bot.privileges[channel] = dict()

    # This could probably be made flexible in the future, but I don't think
    # it'd be worth it.
    mapping = {'+': sopel.module.VOICE,
               '%': sopel.module.HALFOP,
               '@': sopel.module.OP,
               '&': sopel.module.ADMIN,
               '~': sopel.module.OWNER}

    for name in names:
        priv = 0
        for prefix, value in iteritems(mapping):
            if prefix in name:
                priv = priv | value
        nick = Identifier(name.lstrip(''.join(mapping.keys())))
        bot.privileges[channel][nick] = priv
Beispiel #2
0
    def search_url_callbacks(self, url):
        """Yield callbacks found for ``url`` matching their regex pattern

        :param str url: URL found in a trigger
        :return: yield 2-value tuples of ``(callback, match)``

        For each pattern that matches the ``url`` parameter, it yields a
        2-value tuple of ``(callable, match)`` for that pattern.

        The ``callable`` is the one registered with
        :meth:`register_url_callback`, and the ``match`` is the result of
        the regex pattern's ``search`` method.

        .. versionadded:: 7.0

        .. seealso::

            The Python documentation for the `re.search`__ function and
            the `match object`__.

        .. __: https://docs.python.org/3.6/library/re.html#re.search
        .. __: https://docs.python.org/3.6/library/re.html#match-objects

        """
        for regex, function in tools.iteritems(self.memory['url_callbacks']):
            match = regex.search(url)
            if match:
                yield function, match
Beispiel #3
0
 def _modules(self):
     home = os.getcwd()
     modules_dir = os.path.join(home, 'modules')
     filenames = sopel.loader.enumerate_modules(self)
     os.sys.path.insert(0, modules_dir)
     for name, mod_spec in iteritems(filenames):
         path, type_ = mod_spec
         try:
             module, _ = sopel.loader.load_module(name, path, type_)
         except Exception as e:
             filename, lineno = sopel.tools.get_raising_file_and_line()
             rel_path = os.path.relpath(filename, os.path.dirname(__file__))
             raising_stmt = "%s:%d" % (rel_path, lineno)
             stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
         else:
             if hasattr(module, 'configure'):
                 prompt = name + ' module'
                 if module.__doc__:
                     doc = module.__doc__.split('\n', 1)[0]
                     if doc:
                         prompt = doc
                 prompt = 'Configure {} (y/n)? [n]'.format(prompt)
                 do_configure = get_input(prompt)
                 do_configure = do_configure and do_configure.lower() == 'y'
                 if do_configure:
                     module.configure(self)
     self.save()
Beispiel #4
0
def _clean_cache(bot):
    """Cleans up old entries in URL safety cache."""
    if bot.memory['safety_cache_lock'].acquire(False):
        LOGGER.info('Starting safety cache cleanup...')
        try:
            # clean up by age first
            cutoff = time.time() - (7 * 24 * 60 * 60)  # 7 days ago
            old_keys = []
            for key, data in tools.iteritems(bot.memory['safety_cache']):
                if data['fetched'] <= cutoff:
                    old_keys.append(key)
            for key in old_keys:
                bot.memory['safety_cache'].pop(key, None)

            # clean up more values if the cache is still too big
            overage = len(bot.memory['safety_cache']) - cache_limit
            if overage > 0:
                extra_keys = sorted(
                    (data.fetched, key)
                    for (key,
                         data) in bot.memory['safety_cache'].items())[:overage]
                for (_, key) in extra_keys:
                    bot.memory['safety_cache'].pop(key, None)
        finally:
            # No matter what errors happen (or not), release the lock
            bot.memory['safety_cache_lock'].release()

        LOGGER.info('Safety cache cleanup finished.')
    else:
        LOGGER.info('Skipping safety cache cleanup: Cache is locked, '
                    'cleanup already running.')
Beispiel #5
0
def handle_names(bot, trigger):
    """Handle NAMES response, happens when joining to channels."""
    names = trigger.split()

    #TODO specific to one channel type. See issue 281.
    channels = re.search('(#\S*)', trigger.raw)
    if not channels:
        return
    channel = Identifier(channels.group(1))
    if channel not in bot.privileges:
        bot.privileges[channel] = dict()

    # This could probably be made flexible in the future, but I don't think
    # it'd be worth it.
    mapping = {
        '+': sopel.module.VOICE,
        '%': sopel.module.HALFOP,
        '@': sopel.module.OP,
        '&': sopel.module.ADMIN,
        '~': sopel.module.OWNER
    }

    for name in names:
        priv = 0
        for prefix, value in iteritems(mapping):
            if prefix in name:
                priv = priv | value
        nick = Identifier(name.lstrip(''.join(mapping.keys())))
        bot.privileges[channel][nick] = priv
Beispiel #6
0
Datei: bot.py Projekt: nsnw/sopel
    def search_url_callbacks(self, url):
        """Yield callbacks found for ``url`` matching their regex pattern.

        :param str url: URL found in a trigger
        :return: yield 2-value tuples of ``(callback, match)``

        For each pattern that matches the ``url`` parameter, it yields a
        2-value tuple of ``(callable, match)`` for that pattern.

        The ``callable`` is the one registered with
        :meth:`register_url_callback`, and the ``match`` is the result of
        the regex pattern's ``search`` method.

        .. versionadded:: 7.0

        .. seealso::

            The Python documentation for the `re.search`__ function and
            the `match object`__.

        .. __: https://docs.python.org/3.6/library/re.html#re.search
        .. __: https://docs.python.org/3.6/library/re.html#match-objects

        """
        if 'url_callbacks' not in self.memory:
            # nothing to search
            return

        for regex, function in tools.iteritems(self.memory['url_callbacks']):
            match = regex.search(url)
            if match:
                yield function, match
Beispiel #7
0
 def _modules(self):
     home = os.getcwd()
     modules_dir = os.path.join(home, 'modules')
     filenames = sopel.loader.enumerate_modules(self)
     os.sys.path.insert(0, modules_dir)
     for name, mod_spec in iteritems(filenames):
         path, type_ = mod_spec
         try:
             module, _ = sopel.loader.load_module(name, path, type_)
         except Exception as e:
             filename, lineno = sopel.tools.get_raising_file_and_line()
             rel_path = os.path.relpath(filename, os.path.dirname(__file__))
             raising_stmt = "%s:%d" % (rel_path, lineno)
             stderr("Error loading %s: %s (%s)" % (name, e, raising_stmt))
         else:
             if hasattr(module, 'configure'):
                 prompt = name + ' module'
                 if module.__doc__:
                     doc = module.__doc__.split('\n', 1)[0]
                     if doc:
                         prompt = doc
                 prompt = 'Configure {} (y/n)? [n]'.format(prompt)
                 do_configure = get_input(prompt)
                 do_configure = do_configure and do_configure.lower() == 'y'
                 if do_configure:
                     module.configure(self)
     self.save()
Beispiel #8
0
def reload_module_tree(bot, name, seen=None, silent=False):
    from types import ModuleType

    old_module = sys.modules[name]

    if seen is None:
        seen = {}
    if name not in seen:
        seen[name] = []

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        if callable(obj):
            if (getattr(obj, '__name__', None) == 'shutdown' and
                        obj in bot.shutdown_methods):
                # If this is a shutdown method, call it first.
                try:
                    stderr(
                        "calling %s.%s" % (
                            obj.__module__, obj.__name__,
                        )
                    )
                    obj(bot)
                except Exception as e:
                    stderr(
                        "Error calling shutdown method for module %s:%s" % (
                            obj.__module__, e
                        )
                    )
            bot.unregister(obj)
        elif (type(obj) is ModuleType and
              obj.__name__.startswith(name + '.') and
              obj.__name__ not in sys.builtin_module_names):
            # recurse into submodules, see issue 1056
            if obj not in seen[name]:
                seen[name].append(obj)
                reload(obj)
                reload_module_tree(bot, obj.__name__, seen, silent)

    modules = sopel.loader.enumerate_modules(bot.config)
    if name not in modules:
        return  # Only reload the top-level module, once recursion is finished

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    # Sub-modules shouldn't have setup functions, so do after the recursion check
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    path, type_ = modules[name]
    load_module(bot, name, path, type_, silent)
Beispiel #9
0
def reload_module_tree(bot, name, seen=None, silent=False):
    from types import ModuleType

    old_module = sys.modules[name]

    if seen is None:
        seen = {}
    if name not in seen:
        seen[name] = []

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        if callable(obj):
            if (getattr(obj, '__name__', None) == 'shutdown' and
                        obj in bot.shutdown_methods):
                # If this is a shutdown method, call it first.
                try:
                    stderr(
                        "calling %s.%s" % (
                            obj.__module__, obj.__name__,
                        )
                    )
                    obj(bot)
                except Exception as e:
                    stderr(
                        "Error calling shutdown method for module %s:%s" % (
                            obj.__module__, e
                        )
                    )
            bot.unregister(obj)
        elif (type(obj) is ModuleType and
              obj.__name__.startswith(name + '.') and
              obj.__name__ not in sys.builtin_module_names):
            # recurse into submodules, see issue 1056
            if obj not in seen[name]:
                seen[name].append(obj)
                reload(obj)
                reload_module_tree(bot, obj.__name__, seen, silent)

    modules = sopel.loader.enumerate_modules(bot.config)
    if name not in modules:
        return  # Only reload the top-level module, once recursion is finished

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    # Sub-modules shouldn't have setup functions, so do after the recursion check
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    path, type_ = modules[name]
    load_module(bot, name, path, type_, silent)
Beispiel #10
0
def recieve_cap_ls_reply(bot, trigger):
    if bot.server_capabilities:
        # We've already seen the results, so someone sent CAP LS from a module.
        # We're too late to do SASL, and we don't want to send CAP END before
        # the module has done what it needs to, so just return
        return

    for cap in trigger.split():
        c = cap.split('=')
        if len(c) == 2:
            batched_caps[c[0]] = c[1]
        else:
            batched_caps[c[0]] = None

    # Not the last in a multi-line reply. First two args are * and LS.
    if trigger.args[2] == '*':
        return

    bot.server_capabilities = batched_caps

    # If some other module requests it, we don't need to add another request.
    # If some other module prohibits it, we shouldn't request it.
    if 'multi-prefix' not in bot._cap_reqs:
        # Whether or not the server supports multi-prefix doesn't change how we
        # parse it, so we don't need to worry if it fails.
        bot._cap_reqs['multi-prefix'] = (['', 'coretasks', None, None],)

    for cap, reqs in iteritems(bot._cap_reqs):
        # At this point, we know mandatory and prohibited don't co-exist, but
        # we need to call back for optionals if they're also prohibited
        prefix = ''
        for entry in reqs:
            if prefix == '-' and entry[0] != '-':
                entry[2](bot, entry[0] + cap)
                continue
            if entry[0]:
                prefix = entry[0]

        # It's not required, or it's supported, so we can request it
        if prefix != '=' or cap in bot.server_capabilities:
            # REQs fail as a whole, so we send them one capability at a time
            bot.write(('CAP', 'REQ', entry[0] + cap))
        # If it's required but not in server caps, we need to call all the
        # callbacks
        else:
            for entry in reqs:
                if entry[2] and entry[0] == '=':
                    entry[2](bot, entry[0] + cap)

    # If we want to do SASL, we have to wait before we can send CAP END. So if
    # we are, wait on 903 (SASL successful) to send it.
    if bot.config.core.auth_method == 'sasl':
        bot.write(('CAP', 'REQ', 'sasl'))
    else:
        bot.write(('CAP', 'END'))
Beispiel #11
0
def recieve_cap_ls_reply(bot, trigger):
    if bot.server_capabilities:
        # We've already seen the results, so someone sent CAP LS from a module.
        # We're too late to do SASL, and we don't want to send CAP END before
        # the module has done what it needs to, so just return
        return

    for cap in trigger.split():
        c = cap.split('=')
        if len(c) == 2:
            batched_caps[c[0]] = c[1]
        else:
            batched_caps[c[0]] = None

    # Not the last in a multi-line reply. First two args are * and LS.
    if trigger.args[2] == '*':
        return

    bot.server_capabilities = batched_caps

    # If some other module requests it, we don't need to add another request.
    # If some other module prohibits it, we shouldn't request it.
    if 'multi-prefix' not in bot._cap_reqs:
        # Whether or not the server supports multi-prefix doesn't change how we
        # parse it, so we don't need to worry if it fails.
        bot._cap_reqs['multi-prefix'] = (['', 'coretasks', None, None], )

    for cap, reqs in iteritems(bot._cap_reqs):
        # At this point, we know mandatory and prohibited don't co-exist, but
        # we need to call back for optionals if they're also prohibited
        prefix = ''
        for entry in reqs:
            if prefix == '-' and entry[0] != '-':
                entry[2](bot, entry[0] + cap)
                continue
            if entry[0]:
                prefix = entry[0]

        # It's not required, or it's supported, so we can request it
        if prefix != '=' or cap in bot.server_capabilities:
            # REQs fail as a whole, so we send them one capability at a time
            bot.write(('CAP', 'REQ', entry[0] + cap))
        # If it's required but not in server caps, we need to call all the
        # callbacks
        else:
            for entry in reqs:
                if entry[2] and entry[0] == '=':
                    entry[2](bot, entry[0] + cap)

    # If we want to do SASL, we have to wait before we can send CAP END. So if
    # we are, wait on 903 (SASL successful) to send it.
    if bot.config.core.auth_method == 'sasl':
        bot.write(('CAP', 'REQ', 'sasl'))
    else:
        bot.write(('CAP', 'END'))
Beispiel #12
0
def reload(bot, trigger):
    """Reloads a module, for use by admins only."""
    if not trigger.owner or not trigger.is_privmsg:
        return

    name = trigger.group(2)

    if not name or name == '*' or name.upper() == 'ALL THE THINGS':
        for module in bot.config.core.enable:
            try:
                module = sys.modules[module]
                if hasattr(module, 'unload'):
                    module.unload(bot)
            except:
                pass

        bot._callables = {
            'high': collections.defaultdict(list),
            'medium': collections.defaultdict(list),
            'low': collections.defaultdict(list)
        }
        bot._command_groups = collections.defaultdict(list)

        bot.setup()
        return bot.notice('Modules reloaded', trigger.nick)

    if name not in sys.modules:
        return bot.reply('{}: not loaded, try the `load` command'.format(name),
                         trigger.nick)

    old_module = sys.modules[name]
    if hasattr(old_module, 'unload'):
        old_module.unload(bot)

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        bot.unregister(obj)

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    modules = sopel.loader.enumerate_modules(bot.config)
    path, type_ = modules[name]
    load_module(bot, name, path, type_, trigger.nick)
Beispiel #13
0
def dump_database(filename, data):
    """Dump the remind database into a file

    :param str filename: absolute path to the remind database file
    :param dict data: remind database to dump

    The remind database is dumped into a plain text file (utf-8 encoded) as
    tab-separated columns of data: unixtime, channel, nick, and message.

    If the file does not exist, it is created.
    """
    with codecs.open(filename, 'w', encoding='utf-8') as database:
        for unixtime, reminders in tools.iteritems(data):
            for channel, nick, message in reminders:
                line = '%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message)
                database.write(line)
Beispiel #14
0
def dump_database(filename, data):
    """Dump the remind database into a file

    :param str filename: absolute path to the remind database file
    :param dict data: remind database to dump

    The remind database is dumped into a plain text file (utf-8 encoded) as
    tab-separated columns of data: unixtime, channel, nick, and message.

    If the file does not exist, it is created.
    """
    with codecs.open(filename, 'w', encoding='utf-8') as database:
        for unixtime, reminders in tools.iteritems(data):
            for channel, nick, message in reminders:
                line = '%s\t%s\t%s\t%s\n' % (unixtime, channel, nick, message)
                database.write(line)
Beispiel #15
0
def check_callbacks(bot, trigger, url, run=True):
    """
    Check the given URL against the callbacks list. If it matches, and ``run``
    is given as ``True``, run the callback function, otherwise pass. Returns
    ``True`` if the url matched anything in the callbacks list.
    """
    # Check if it matches the exclusion list first
    matched = any(regex.search(url) for regex in bot.memory['url_exclude'])
    # Then, check if there's anything in the callback list
    for regex, function in tools.iteritems(bot.memory['url_callbacks']):
        match = regex.search(url)
        if match:
            if run:
                function(bot, trigger, match)
            matched = True
    return matched
Beispiel #16
0
def check_callbacks(bot, trigger, url, run=True):
    """
    Check the given URL against the callbacks list. If it matches, and ``run``
    is given as ``True``, run the callback function, otherwise pass. Returns
    ``True`` if the url matched anything in the callbacks list.
    """
    # Check if it matches the exclusion list first
    matched = any(regex.search(url) for regex in bot.memory['url_exclude'])
    # Then, check if there's anything in the callback list
    for regex, function in tools.iteritems(bot.memory['url_callbacks']):
        match = regex.search(url)
        if match:
            if run:
                function(bot, trigger, match)
            matched = True
    return matched
Beispiel #17
0
def handle_names(bot, trigger):
    """Handle NAMES responses.

    This function keeps track of users' privileges when Sopel joins channels.
    """
    names = trigger.split()

    # TODO specific to one channel type. See issue 281.
    channels = re.search(r'(#\S*)', trigger.raw)
    if not channels:
        return
    channel = Identifier(channels.group(1))
    if channel not in bot.privileges:
        bot.privileges[channel] = {}
    if channel not in bot.channels:
        bot.channels[channel] = target.Channel(channel)

    # This could probably be made flexible in the future, but I don't think
    # it'd be worth it.
    # If this ever needs to be updated, remember to change the mode handling in
    # the WHO-handler functions below, too.
    mapping = {
        "+": module.VOICE,
        "%": module.HALFOP,
        "@": module.OP,
        "&": module.ADMIN,
        "~": module.OWNER,
        "!": module.OPER,
    }

    for name in names:
        priv = 0
        for prefix, value in iteritems(mapping):
            if prefix in name:
                priv = priv | value
        nick = Identifier(name.lstrip(''.join(mapping.keys())))
        bot.privileges[channel][nick] = priv
        user = bot.users.get(nick)
        if user is None:
            # It's not possible to set the username/hostname from info received
            # in a NAMES reply, unfortunately.
            # Fortunately, the user should already exist in bot.users by the
            # time this code runs, so this is 99.9% ass-covering.
            user = target.User(nick, None, None)
            bot.users[nick] = user
        bot.channels[channel].add_user(user, privs=priv)
Beispiel #18
0
def check_callbacks(bot, trigger, url, run=True):
    """
    Check the given URL against the callbacks list. If it matches, and ``run``
    is given as ``True``, run the callback function, otherwise pass. Returns
    ``True`` if the url matched anything in the callbacks list.
    """
    # Check if it matches the exclusion list first
    matched = any(regex.search(url) for regex in bot.memory["url_exclude"])
    # Then, check if there's anything in the callback list
    for regex, function in tools.iteritems(bot.memory["url_callbacks"]):
        match = regex.search(url)
        if match:
            # Always run ones from @url; they don't run on their own.
            if run or hasattr(function, "url_regex"):
                function(bot, trigger, match)
            matched = True
    return matched
Beispiel #19
0
def f_reload(bot, trigger):
    """Reloads a module, for use by admins only."""
    if not trigger.admin:
        return

    name = trigger.group(2)
    if name == bot.config.core.owner:
        return bot.reply('What?')

    if not name or name == '*' or name.upper() == 'ALL THE THINGS':
        bot._callables = {
            'high': collections.defaultdict(list),
            'medium': collections.defaultdict(list),
            'low': collections.defaultdict(list)
        }
        bot.command_groups = collections.defaultdict(list)
        bot.setup()
        # return bot.reply('done')
        return bot.reply(random.choice(['Good to go!', 'Waiting for orders!', 'Ready!']))  # Custom line

    if name not in sys.modules:
        return bot.reply('%s: not loaded, try the `load` command' % name)

    old_module = sys.modules[name]

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        bot.unregister(obj)

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    modules = sopel.loader.enumerate_modules(bot.config)
    path, type_ = modules[name]
    load_module(bot, name, path, type_)
Beispiel #20
0
def f_reload(bot, trigger):
    """Reloads a module, for use by admins only."""
    if not trigger.admin:
        return

    name = trigger.group(2)

    if not name or name == '*' or name.upper() == 'ALL THE THINGS':
        bot._callables = {
            'high': collections.defaultdict(list),
            'medium': collections.defaultdict(list),
            'low': collections.defaultdict(list)
        }
        bot._command_groups = collections.defaultdict(list)
        bot.setup()
        return bot.reply('done')

    if name not in sys.modules:
        return bot.reply('"%s" not loaded, try the `load` command' % name)

    old_module = sys.modules[name]

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        bot.unregister(obj)

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    modules = sopel.loader.enumerate_modules(bot.config)
    if name not in modules:
        return bot.reply('"%s" not loaded, try the `load` command' % name)
    path, type_ = modules[name]
    load_module(bot, name, path, type_)
Beispiel #21
0
def handle_names(bot, trigger):
    """Handle NAMES response, happens when joining to channels."""
    names = trigger.split()

    # TODO specific to one channel type. See issue 281.
    channels = re.search(r'(#\S*)', trigger.raw)
    if not channels:
        return
    channel = Identifier(channels.group(1))
    if channel not in bot.privileges:
        bot.privileges[channel] = dict()
    if channel not in bot.channels:
        bot.channels[channel] = Channel(channel)

    # This could probably be made flexible in the future, but I don't think
    # it'd be worth it.
    # If this ever needs to be updated, remember to change the mode handling in
    # the WHO-handler functions below, too.
    mapping = {'+': sopel.module.VOICE,
               '%': sopel.module.HALFOP,
               '@': sopel.module.OP,
               '&': sopel.module.ADMIN,
               '~': sopel.module.OWNER}

    for name in names:
        priv = 0
        for prefix, value in iteritems(mapping):
            if prefix in name:
                priv = priv | value
        nick = Identifier(name.lstrip(''.join(mapping.keys())))
        bot.privileges[channel][nick] = priv
        user = bot.users.get(nick)
        if user is None:
            # It's not possible to set the username/hostname from info received
            # in a NAMES reply, unfortunately.
            # Fortunately, the user should already exist in bot.users by the
            # time this code runs, so this is 99.9% ass-covering.
            user = User(nick, None, None)
            bot.users[nick] = user
        bot.channels[channel].add_user(user, privs=priv)
Beispiel #22
0
def handle_names(bot, trigger):
    """Handle NAMES response, happens when joining to channels."""
    names = trigger.split()

    #TODO specific to one channel type. See issue 281.
    channels = re.search('(#\S*)', trigger.raw)
    if not channels:
        return
    channel = Identifier(channels.group(1))
    if channel not in bot.privileges:
        bot.privileges[channel] = dict()
    bot.init_ops_list(channel)

    # This could probably be made flexible in the future, but I don't think
    # it'd be worth it.
    mapping = {'+': sopel.module.VOICE,
               '%': sopel.module.HALFOP,
               '@': sopel.module.OP,
               '&': sopel.module.ADMIN,
               '~': sopel.module.OWNER}

    for name in names:
        priv = 0
        for prefix, value in iteritems(mapping):
            if prefix in name:
                priv = priv | value
        nick = Identifier(name.lstrip(''.join(mapping.keys())))
        bot.privileges[channel][nick] = priv

        # Old op list maintenance is down here, and should be removed at some
        # point
        if '@' in name or '~' in name or '&' in name:
            bot.add_op(channel, name.lstrip('@&%+~'))
            bot.add_halfop(channel, name.lstrip('@&%+~'))
            bot.add_voice(channel, name.lstrip('@&%+~'))
        elif '%' in name:
            bot.add_halfop(channel, name.lstrip('@&%+~'))
            bot.add_voice(channel, name.lstrip('@&%+~'))
        elif '+' in name:
            bot.add_voice(channel, name.lstrip('@&%+~'))
Beispiel #23
0
def f_reload(bot, trigger):
    """Reloads a module, for use by admins only."""
    if not trigger.admin:
        return

    name = trigger.group(2)

    if not name or name == "*" or name.upper() == "ALL THE THINGS":
        bot._callables = {
            "high": collections.defaultdict(list),
            "medium": collections.defaultdict(list),
            "low": collections.defaultdict(list),
        }
        bot._command_groups = collections.defaultdict(list)
        bot.setup()
        return bot.reply("done")

    if name not in sys.modules:
        return bot.reply("%s: not loaded, try the `load` command" % name)

    old_module = sys.modules[name]

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        bot.unregister(obj)

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    modules = sopel.loader.enumerate_modules(bot.config)
    path, type_ = modules[name]
    load_module(bot, name, path, type_)
Beispiel #24
0
def process_urls(bot, trigger, urls):
    results = []
    for url in urls:
        try:
            url = web.iri_to_uri(url)
        except:
            pass

        title = None

        for regex, function in tools.iteritems(bot.memory['url_callbacks']):
            match = regex.search(url)
            if match is not None:
                title = function(bot, trigger, match)
                if title:
                    results.append((title, get_hostname(url)))
                    break

        if not title:
            title = find_title(url, verify=bot.config.core.verify_ssl)
            if title:
                results.append((title, get_hostname(url)))
    return results
Beispiel #25
0
def f_reload(bot, trigger):
    """Reloads a module, for use by admins only."""
    if not trigger.admin:
        return

    name = trigger.group(2)
    if name == bot.config.core.owner:
        return bot.reply('What?')

    if not name or name == '*' or name.upper() == 'ALL THE THINGS':
        bot.callables = None
        bot.commands = None
        bot.setup()
        return bot.reply('done')

    if name not in sys.modules:
        return bot.reply('%s: not loaded, try the `load` command' % name)

    old_module = sys.modules[name]

    old_callables = {}
    for obj_name, obj in iteritems(vars(old_module)):
        bot.unregister(obj)

    # Also remove all references to sopel callables from top level of the
    # module, so that they will not get loaded again if reloading the
    # module does not override them.
    for obj_name in old_callables.keys():
        delattr(old_module, obj_name)

    # Also delete the setup function
    if hasattr(old_module, "setup"):
        delattr(old_module, "setup")

    modules = sopel.loader.enumerate_modules(bot.config)
    path, type_ = modules[name]
    load_module(bot, name, path, type_)
Beispiel #26
0
def recieve_cap_ls_reply(bot, trigger):
    if bot.server_capabilities:
        # We've already seen the results, so someone sent CAP LS from a module.
        # We're too late to do SASL, and we don't want to send CAP END before
        # the module has done what it needs to, so just return
        return

    for cap in trigger.split():
        c = cap.split('=')
        if len(c) == 2:
            batched_caps[c[0]] = c[1]
        else:
            batched_caps[c[0]] = None

    # Not the last in a multi-line reply. First two args are * and LS.
    if trigger.args[2] == '*':
        return

    bot.server_capabilities = batched_caps

    # If some other module requests it, we don't need to add another request.
    # If some other module prohibits it, we shouldn't request it.
    core_caps = ['multi-prefix', 'away-notify', 'cap-notify', 'server-time']
    for cap in core_caps:
        if cap not in bot._cap_reqs:
            bot._cap_reqs[cap] = [_CapReq('', 'coretasks')]

    def acct_warn(bot, cap):
        LOGGER.info(
            'Server does not support %s, or it conflicts with a custom '
            'module. User account validation unavailable or limited.', cap[1:])
        if bot.config.core.owner_account or bot.config.core.admin_accounts:
            LOGGER.warning(
                'Owner or admin accounts are configured, but %s is not '
                'supported by the server. This may cause unexpected behavior.',
                cap[1:])

    auth_caps = ['account-notify', 'extended-join', 'account-tag']
    for cap in auth_caps:
        if cap not in bot._cap_reqs:
            bot._cap_reqs[cap] = [_CapReq('=', 'coretasks', acct_warn)]

    for cap, reqs in iteritems(bot._cap_reqs):
        # At this point, we know mandatory and prohibited don't co-exist, but
        # we need to call back for optionals if they're also prohibited
        prefix = ''
        for entry in reqs:
            if prefix == '-' and entry.prefix != '-':
                entry.failure(bot, entry.prefix + cap)
                continue
            if entry.prefix:
                prefix = entry.prefix

        # It's not required, or it's supported, so we can request it
        if prefix != '=' or cap in bot.server_capabilities:
            # REQs fail as a whole, so we send them one capability at a time
            bot.write(('CAP', 'REQ', entry.prefix + cap))
        # If it's required but not in server caps, we need to call all the
        # callbacks
        else:
            for entry in reqs:
                if entry.failure and entry.prefix == '=':
                    entry.failure(bot, entry.prefix + cap)

    # If we want to do SASL, we have to wait before we can send CAP END. So if
    # we are, wait on 903 (SASL successful) to send it.
    if bot.config.core.auth_method == 'sasl':
        bot.write(('CAP', 'REQ', 'sasl'))
    else:
        bot.write(('CAP', 'END'))
Beispiel #27
0
def receive_cap_ls_reply(bot, trigger):
    if bot.server_capabilities:
        # We've already seen the results, so someone sent CAP LS from a module.
        # We're too late to do SASL, and we don't want to send CAP END before
        # the module has done what it needs to, so just return
        return

    for cap in trigger.split():
        c = cap.split('=')
        if len(c) == 2:
            batched_caps[c[0]] = c[1]
        else:
            batched_caps[c[0]] = None

    # Not the last in a multi-line reply. First two args are * and LS.
    if trigger.args[2] == '*':
        return

    bot.server_capabilities = batched_caps

    # If some other module requests it, we don't need to add another request.
    # If some other module prohibits it, we shouldn't request it.
    core_caps = [
        'echo-message',
        'multi-prefix',
        'away-notify',
        'cap-notify',
        'server-time',
    ]
    for cap in core_caps:
        if cap not in bot._cap_reqs:
            bot._cap_reqs[cap] = [_CapReq('', 'coretasks')]

    def acct_warn(bot, cap):
        LOGGER.info('Server does not support %s, or it conflicts with a custom '
                    'module. User account validation unavailable or limited.',
                    cap[1:])
        if bot.config.core.owner_account or bot.config.core.admin_accounts:
            LOGGER.warning(
                'Owner or admin accounts are configured, but %s is not '
                'supported by the server. This may cause unexpected behavior.',
                cap[1:])
    auth_caps = ['account-notify', 'extended-join', 'account-tag']
    for cap in auth_caps:
        if cap not in bot._cap_reqs:
            bot._cap_reqs[cap] = [_CapReq('', 'coretasks', acct_warn)]

    for cap, reqs in iteritems(bot._cap_reqs):
        # At this point, we know mandatory and prohibited don't co-exist, but
        # we need to call back for optionals if they're also prohibited
        prefix = ''
        for entry in reqs:
            if prefix == '-' and entry.prefix != '-':
                entry.failure(bot, entry.prefix + cap)
                continue
            if entry.prefix:
                prefix = entry.prefix

        # It's not required, or it's supported, so we can request it
        if prefix != '=' or cap in bot.server_capabilities:
            # REQs fail as a whole, so we send them one capability at a time
            bot.write(('CAP', 'REQ', entry.prefix + cap))
        # If it's required but not in server caps, we need to call all the
        # callbacks
        else:
            for entry in reqs:
                if entry.failure and entry.prefix == '=':
                    entry.failure(bot, entry.prefix + cap)

    # If we want to do SASL, we have to wait before we can send CAP END. So if
    # we are, wait on 903 (SASL successful) to send it.
    if bot.config.core.auth_method == 'sasl':
        bot.write(('CAP', 'REQ', 'sasl'))
    else:
        bot.write(('CAP', 'END'))
Beispiel #28
0
 def search_url_callbacks(self, url):
     for regex, function in tools.iteritems(self.memory['url_callbacks']):
         match = regex.search(url)
         if match:
             yield function, match