Example #1
0
def toggle_safety(bot: SopelWrapper, trigger: Trigger):
    """Set safety setting for channel."""
    if not trigger.admin and bot.channels[trigger.sender].privileges[
            trigger.nick] < plugin.OP:
        bot.reply('Only channel operators can change safety settings')
        return

    new_mode = None
    if trigger.group(2):
        new_mode = trigger.group(2).lower()

    if not new_mode or (new_mode != "default"
                        and new_mode not in SAFETY_MODES):
        bot.reply(
            "Current mode: {}. Available modes: {}, or default ({})".format(
                bot.db.get_channel_value(
                    trigger.sender,
                    "safety",
                    "default",
                ),
                ", ".join(SAFETY_MODES),
                bot.settings.safety.default_mode,
            ))
        return

    if new_mode == "default":
        bot.db.delete_channel_value(trigger.sender, "safety")
    else:
        bot.db.set_channel_value(trigger.sender, "safety", new_mode)
    bot.say('Safety is now set to "%s" for this channel' % new_mode)
Example #2
0
def test_bot_legacy_permissions(sopel_bot):
    """
    Make sure permissions match after being updated from both RPL_NAMREPLY
    and RPL_WHOREPLY, #1482
    """

    nick = Identifier("Admin")

    # RPL_NAMREPLY
    pretrigger = PreTrigger("Foo",
                            ":test.example.com 353 Foo = #test :Foo ~@Admin")
    trigger = Trigger(sopel_bot.config, pretrigger, None)
    coretasks.handle_names(sopel_bot, trigger)

    assert (sopel_bot.channels["#test"].privileges[nick] ==
            sopel_bot.privileges["#test"][nick])

    # RPL_WHOREPLY
    pretrigger = PreTrigger(
        "Foo",
        ":test.example.com 352 Foo #test ~Admin adminhost test.example.com Admin Hr~ :0 Admin",
    )
    trigger = Trigger(sopel_bot.config, pretrigger, None)
    coretasks.recv_who(sopel_bot, trigger)

    assert (sopel_bot.channels["#test"].privileges[nick] ==
            sopel_bot.privileges["#test"][nick])

    assert sopel_bot.users.get(nick) is not None
Example #3
0
def test_intents_trigger(nick):
    line = '@intent=ACTION :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner = 'Foo'
    config.core.admins = ['Bar']

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'foo'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'PRIVMSG'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.args == ['#Sopel', 'Hello, world']
    assert trigger.tags == {'intent': 'ACTION'}
    assert trigger.admin is True
    assert trigger.owner is True
Example #4
0
def trigger_account(bot):
    line = '@account=egg :[email protected] PRIVMSG #Sopel :Hello, world'
    return Trigger(
        bot.config,
        PreTrigger(tools.Identifier('egg'), line),
        None,
        'egg')
Example #5
0
def test_ircv3_extended_join_trigger(nick):
    line = ':[email protected] JOIN #Sopel bar :Real Name'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner_account = 'bar'

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'foo'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'JOIN'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.args == ['#Sopel', 'bar', 'Real Name']
    assert trigger.account == 'bar'
    assert trigger.tags == {'account': 'bar'}
    assert trigger.owner is True
    assert trigger.admin is True
Example #6
0
def test_ircv3_extended_join_trigger(nick, configfactory):
    line = ':[email protected] JOIN #Sopel bar :Real Name'
    pretrigger = PreTrigger(nick, line)

    config = configfactory('default.cfg', TMP_CONFIG)

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'foo'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'JOIN'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.args == ['#Sopel', 'bar', 'Real Name']
    assert trigger.plain == 'Real Name'
    assert trigger.account == 'bar'
    assert trigger.tags == {'account': 'bar'}
    assert trigger.ctcp is None
    assert trigger.owner is True
    assert trigger.admin is True
Example #7
0
def test_ircv3_intents_trigger(nick, configfactory):
    line = '@intent=ACTION :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = configfactory('default.cfg', TMP_CONFIG)
    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.sender == '#Sopel'
    assert trigger.raw == line
    assert trigger.is_privmsg is False
    assert trigger.hostmask == '[email protected]'
    assert trigger.user == 'bar'
    assert trigger.nick == Identifier('Foo')
    assert trigger.host == 'example.com'
    assert trigger.event == 'PRIVMSG'
    assert trigger.match == fakematch
    assert trigger.group == fakematch.group
    assert trigger.groups == fakematch.groups
    assert trigger.groupdict == fakematch.groupdict
    assert trigger.args == ['#Sopel', 'Hello, world']
    assert trigger.plain == 'Hello, world'
    assert trigger.tags == {'intent': 'ACTION'}
    assert trigger.ctcp == 'ACTION'
    assert trigger.account is None
    assert trigger.admin is True
    assert trigger.owner is True
Example #8
0
    def dispatch(self, pretrigger):
        args = pretrigger.args
        event, args, text = pretrigger.event, args, args[-1] if args else ''

        if self.config.core.nick_blocks or self.config.core.host_blocks:
            nick_blocked = self._nick_blocked(pretrigger.nick)
            host_blocked = self._host_blocked(pretrigger.host)
        else:
            nick_blocked = host_blocked = None

        list_of_blocked_functions = []
        for priority in ('high', 'medium', 'low'):
            items = list(self._callables[priority].items())

            for regexp, funcs in items:
                match = regexp.match(text)
                if not match:
                    continue
                user_obj = self.users.get(pretrigger.nick)
                account = user_obj.account if user_obj else None
                trigger = Trigger(self.config, pretrigger, match, account)
                wrapper = self.SopelWrapper(self, trigger)

                for func in funcs:
                    if (not trigger.admin and
                            not func.unblockable and
                            (nick_blocked or host_blocked)):
                        function_name = "%s.%s" % (
                            func.__module__, func.__name__
                        )
                        list_of_blocked_functions.append(function_name)
                        continue

                    if event not in func.event:
                        continue
                    if (hasattr(func, 'intents') and
                            trigger.tags.get('intent') not in func.intents):
                        continue
                    if func.thread:
                        targs = (func, wrapper, trigger)
                        t = threading.Thread(target=self.call, args=targs)
                        t.start()
                    else:
                        self.call(func, wrapper, trigger)

        if list_of_blocked_functions:
            if nick_blocked and host_blocked:
                block_type = 'both'
            elif nick_blocked:
                block_type = 'nick'
            else:
                block_type = 'host'
            LOGGER.info(
                "[%s]%s prevented from using %s.",
                block_type,
                trigger.nick,
                ', '.join(list_of_blocked_functions)
            )
def test_mode_colon(sopel):
    """
    Ensure mode messages with colons are parsed properly
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Uvoice Uadmin".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger("Foo", "MODE #test +av Uadmin :Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uadmin")] == ADMIN
Example #10
0
def test_ircv3_account_tag_trigger(nick, configfactory):
    line = '@account=bar :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = configfactory('default.cfg', TMP_CONFIG_ACCOUNT)
    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.admin is True
    assert trigger.owner is True
def get_weather(bot: SopelWrapper, trigger: Trigger) -> None:
    """
    Gets the weather at a given location and returns the first result
    """
    location_lookup_args = trigger.group(2)
    if location_lookup_args is None:

        location_lookup_args = bot.db.get_nick_value(trigger.nick, 'place_id')
        if not location_lookup_args:
            bot.reply("I don't know where you live. "
                      "Give me a location, like {pfx}{command} London, "
                      "or tell me where you live by saying {pfx}setlocation "
                      "London, for example.".format(
                          command=trigger.group(1),
                          pfx=bot.config.core.help_prefix))
            return plugin.NOLIMIT

    api = get_owm_api(bot)
    location = parse_location_args(location_lookup_args)
    message = get_weather_message(api, location)
    bot.reply(message)
Example #12
0
def url_unban(bot: SopelWrapper, trigger: Trigger):
    """Allow a URL for auto title.

    Use ``urlpallow`` to allow a pattern instead of a URL.
    """
    url = trigger.group(2)

    if not url:
        bot.reply('This command requires a URL to allow.')
        return

    if trigger.group(1) in ['urlpallow', 'urlpunban']:
        # validate regex pattern
        try:
            re.compile(url)
        except re.error as err:
            bot.reply('Invalid regex pattern: %s' % err)
            return
    else:
        # escape the URL to ensure a valid pattern
        url = re.escape(url)

    patterns = bot.settings.url.exclude

    if url not in patterns:
        bot.reply('This URL was not excluded from auto title.')
        return

    # update settings
    patterns.remove(url)
    bot.settings.url.exclude = patterns  # set the config option
    bot.settings.save()
    LOGGER.info('%s allowed the URL pattern "%s"', trigger.nick, url)

    # re-compile
    bot.memory['url_exclude'] = [re.compile(s) for s in patterns]

    # tell the user
    bot.reply('This URL is not excluded from auto title anymore.')
Example #13
0
def test_ircv3_account_tag_trigger(nick):
    line = '@account=Foo :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner_account = 'Foo'
    config.core.admins = ['Bar']

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.admin is True
    assert trigger.owner is True
Example #14
0
def test_ircv3_server_time_trigger(nick, configfactory):
    line = '@time=2016-01-09T03:15:42.000Z :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    config = configfactory('default.cfg', TMP_CONFIG)
    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.time == datetime.datetime(2016, 1, 9, 3, 15, 42, 0)

    # Spec-breaking string
    line = '@time=2016-01-09T04:20 :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.time is not None
def test_bot_mixed_mode_types(sopel):
    """
    Ensure mixed argument- and non-argument- modes are handled
    Sopel 6.6.6 and older did not behave well. #1575
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Uvoice Uop Uadmin Uvoice2 Uop2 Uadmin2".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    # Non-attribute-requiring non-permission mode
    pretrigger = PreTrigger("Foo", "MODE #test +amov Uadmin Uop Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uop")] == OP
    assert sopel.channels["#test"].privileges[Identifier("Uadmin")] == ADMIN

    # Attribute-requiring non-permission modes
    # This results in a _send_who, which isn't supported in MockSopel or this
    # test, so we just make sure it results in an exception instead of privesc.
    pretrigger = PreTrigger("Foo",
                            "MODE #test +abov Uadmin2 x!y@z Uop2 Uvoice2")
    trigger = Trigger(sopel.config, pretrigger, None)
    try:
        coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)
    except AttributeError as e:
        if e.args[
                0] == "'MockSopel' object has no attribute 'enabled_capabilities'":
            return

    assert sopel.channels["#test"].privileges[Identifier("Uvoice2")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uop2")] == OP
    assert sopel.channels["#test"].privileges[Identifier("Uadmin2")] == ADMIN
Example #16
0
def vt_command(bot: SopelWrapper, trigger: Trigger):
    """Look up VT results on demand."""
    if not bot.settings.safety.vt_api_key:
        bot.reply("Sorry, I don't have a VirusTotal API key configured.")
        return

    url = trigger.group(2)
    safe_url = safeify_url(url)

    result = virustotal_lookup(bot, url, max_cache_age=timedelta(minutes=1))
    if not result:
        bot.reply("Sorry, an error occurred while looking that up.")
        return

    analysis = result["virustotal_data"]["last_analysis_stats"]

    result_types = {
        "malicious": colors.RED,
        "suspicious": colors.YELLOW,
        "harmless": colors.GREEN,
        "undetected": colors.GREY,
    }
    result_strs = []
    for result_type, result_color in result_types.items():
        if analysis[result_type] == 0:
            result_strs.append("0 " + result_type)
        else:
            result_strs.append(
                bold(
                    color(
                        str(analysis[result_type]) + " " + result_type,
                        result_color)))
    results_str = ", ".join(result_strs)

    vt_scan_time = datetime.fromtimestamp(
        result["virustotal_data"]["last_analysis_date"],
        timezone.utc,
    )
    bot.reply("Results: {} at {} for {}".format(
        results_str,
        tools.time.format_time(
            bot.db,
            bot.config,
            nick=trigger.nick,
            channel=trigger.sender,
            time=vt_scan_time,
        ),
        safe_url,
    ))
def test_bot_mixed_mode_removal(sopel):
    """
    Ensure mixed mode types like -h+a are handled
    Sopel 6.6.6 and older did not handle this correctly. #1575
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Uvoice Uop".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger("Foo", "MODE #test +qao Uvoice Uvoice Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger(
        "Foo", "MODE #test -o+o-qa+v Uvoice Uop Uvoice Uvoice Uvoice")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uop")] == OP
def test_bot_mixed_modes(sopel):
    """
    Ensure mixed modes like +vha are tracked correctly.
    Sopel 6.6.6 and older would assign all modes to all users. #1575
    """

    # RPL_NAMREPLY to create Users and (zeroed) privs
    for user in set("Unothing Uvoice Uhalfop Uop Uadmin Uowner".split(" ")):
        pretrigger = PreTrigger(
            "Foo", ":test.example.com 353 Foo = #test :Foo %s" % user)
        trigger = Trigger(sopel.config, pretrigger, None)
        coretasks.handle_names(MockSopelWrapper(sopel, trigger), trigger)

    pretrigger = PreTrigger(
        "Foo", "MODE #test +qvhao Uowner Uvoice Uhalfop Uadmin Uop")
    trigger = Trigger(sopel.config, pretrigger, None)
    coretasks.track_modes(MockSopelWrapper(sopel, trigger), trigger)

    assert sopel.channels["#test"].privileges[Identifier("Unothing")] == 0
    assert sopel.channels["#test"].privileges[Identifier("Uvoice")] == VOICE
    assert sopel.channels["#test"].privileges[Identifier("Uhalfop")] == HALFOP
    assert sopel.channels["#test"].privileges[Identifier("Uop")] == OP
    assert sopel.channels["#test"].privileges[Identifier("Uadmin")] == ADMIN
    assert sopel.channels["#test"].privileges[Identifier("Uowner")] == OWNER
Example #19
0
def test_ircv3_server_time_trigger(nick):
    line = '@time=2016-01-09T03:15:42.000Z :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)

    config = MockConfig()
    config.core.owner = 'Foo'
    config.core.admins = ['Bar']

    fakematch = re.match('.*', line)

    trigger = Trigger(config, pretrigger, fakematch)
    assert trigger.time == datetime.datetime(2016, 1, 9, 3, 15, 42, 0)

    # Spec-breaking string
    line = '@time=2016-01-09T04:20 :[email protected] PRIVMSG #Sopel :Hello, world'
    pretrigger = PreTrigger(nick, line)
    assert pretrigger.time is not None
Example #20
0
def title_command(bot: SopelWrapper, trigger: Trigger):
    """
    Show the title or URL information for the given URL, or the last URL seen
    in this channel.
    """
    result_count = 0

    if not trigger.group(2):
        if trigger.sender not in bot.memory['last_seen_url']:
            return
        urls = [bot.memory["last_seen_url"][trigger.sender]]
    else:
        # needs to be a list so len() can be checked later
        urls = list(web.search_urls(trigger))

    for url, title, domain, tinyurl, dispatched in process_urls(
            bot, trigger, urls, requested=True):
        if dispatched:
            result_count += 1
            continue
        message = "%s | %s" % (title, domain)
        if tinyurl:
            message += ' ( %s )' % tinyurl
        bot.reply(message)
        bot.memory['last_seen_url'][trigger.sender] = url
        result_count += 1

    expected_count = len(urls)
    if result_count < expected_count:
        if expected_count == 1:
            bot.reply(
                "Sorry, fetching that title failed. Make sure the site is working."
            )
        elif result_count == 0:
            bot.reply("Sorry, I couldn't fetch titles for any of those.")
        else:
            bot.reply(
                "I couldn't get all of the titles, but I fetched what I could!"
            )
Example #21
0
File: bot.py Project: nsnw/sopel
    def dispatch(self, pretrigger):
        """Dispatch a parsed message to any registered callables.

        :param PreTrigger pretrigger: a parsed message from the server
        """
        args = pretrigger.args
        text = args[-1] if args else ''
        event = pretrigger.event
        intent = pretrigger.tags.get('intent')
        nick = pretrigger.nick
        is_echo_message = nick.lower() == self.nick.lower()
        user_obj = self.users.get(nick)
        account = user_obj.account if user_obj else None

        if self.config.core.nick_blocks or self.config.core.host_blocks:
            nick_blocked = self._nick_blocked(pretrigger.nick)
            host_blocked = self._host_blocked(pretrigger.host)
        else:
            nick_blocked = host_blocked = None
        blocked = bool(nick_blocked or host_blocked)

        list_of_blocked_functions = []
        for priority in ('high', 'medium', 'low'):
            for regexp, funcs in self._callables[priority].items():
                match = regexp.match(text)
                if not match:
                    continue

                for func in funcs:
                    trigger = Trigger(self.config, pretrigger, match, account)

                    # check blocked nick/host
                    if blocked and not func.unblockable and not trigger.admin:
                        function_name = "%s.%s" % (func.__module__,
                                                   func.__name__)
                        list_of_blocked_functions.append(function_name)
                        continue

                    # check event
                    if event not in func.event:
                        continue

                    # check intents
                    if hasattr(func, 'intents'):
                        if not intent:
                            continue

                        match = any(
                            func_intent.match(intent)
                            for func_intent in func.intents)
                        if not match:
                            continue

                    # check echo-message feature
                    if is_echo_message and not func.echo:
                        continue

                    # call triggered function
                    wrapper = SopelWrapper(self, trigger)
                    if func.thread:
                        targs = (func, wrapper, trigger)
                        t = threading.Thread(target=self.call, args=targs)
                        t.start()
                    else:
                        self.call(func, wrapper, trigger)

        if list_of_blocked_functions:
            if nick_blocked and host_blocked:
                block_type = 'both'
            elif nick_blocked:
                block_type = 'nick'
            else:
                block_type = 'host'
            LOGGER.info("[%s]%s prevented from using %s.", block_type, nick,
                        ', '.join(list_of_blocked_functions))
Example #22
0
    def dispatch(self, pretrigger):
        """Dispatch a parsed message to any registered callables.

        :param pretrigger: a parsed message from the server
        :type pretrigger: :class:`~sopel.trigger.PreTrigger`

        The ``pretrigger`` (a parsed message) is used to find matching rules;
        it will retrieve them by order of priority, and execute them. It runs
        triggered rules in separate threads, unless they are marked otherwise.

        However, it won't run triggered blockable rules at all when they can't
        be executed for blocked nickname or hostname.

        .. seealso::

            The pattern matching is done by the
            :class:`Rules Manager<sopel.plugins.rules.Manager>`.

        """
        # list of commands running in separate threads for this dispatch
        running_triggers = []
        # nickname/hostname blocking
        nick_blocked, host_blocked = self._is_pretrigger_blocked(pretrigger)
        blocked = bool(nick_blocked or host_blocked)
        list_of_blocked_rules = set()
        # account info
        nick = pretrigger.nick
        user_obj = self.users.get(nick)
        account = user_obj.account if user_obj else None

        for rule, match in self._rules_manager.get_triggered_rules(
                self, pretrigger):
            trigger = Trigger(self.settings, pretrigger, match, account)

            is_unblockable = trigger.admin or rule.is_unblockable()
            if blocked and not is_unblockable:
                list_of_blocked_rules.add(str(rule))
                continue

            wrapper = SopelWrapper(self,
                                   trigger,
                                   output_prefix=rule.get_output_prefix())

            if rule.is_threaded():
                # run in a separate thread
                targs = (rule, wrapper, trigger)
                t = threading.Thread(target=self.call_rule, args=targs)
                plugin_name = rule.get_plugin_name()
                rule_label = rule.get_rule_label()
                t.name = '%s-%s-%s' % (t.name, plugin_name, rule_label)
                t.start()
                running_triggers.append(t)
            else:
                # direct call
                self.call_rule(rule, wrapper, trigger)

        # update currently running triggers
        self._update_running_triggers(running_triggers)

        if list_of_blocked_rules:
            if nick_blocked and host_blocked:
                block_type = 'both blocklists'
            elif nick_blocked:
                block_type = 'nick blocklist'
            else:
                block_type = 'host blocklist'
            LOGGER.debug(
                "%s prevented from using %s by %s.",
                pretrigger.nick,
                ', '.join(list_of_blocked_rules),
                block_type,
            )
Example #23
0
    def get_triggered_callables(self, priority, pretrigger, blocked):
        """Get triggered callables by priority.

        :param str priority: priority to retrieve callables
        :param pretrigger: Sopel pretrigger object
        :type pretrigger: :class:`~sopel.trigger.PreTrigger`
        :param bool blocked: true if nick or channel is blocked from triggering
                             callables
        :return: a tuple with the callable, the trigger, and if it's blocked
        :rtype: :class:`tuple`

        This methods retrieves all triggered callables for this ``priority``
        level. It yields each function with its :class:`trigger
        <sopel.trigger.Trigger>` object and a boolean flag to tell if this
        callable is blocked or not.

        To be triggered, a callable must match the ``pretrigger`` using a regex
        pattern. Then it must comply with other criteria (if any) such as
        event, intents, and echo-message filters.

        A triggered callable won't actually be invoked by Sopel if the nickname
        or hostname is ``blocked``, *unless* the nickname is an admin or
        the callable is marked as "unblockable".

        .. seealso::

            Sopel invokes triggered callables in its :meth:`~.dispatch` method.
            The priority of a callable can be set with the
            :func:`sopel.module.priority` decorator. Other decorators from
            :mod:`sopel.module` provide additional criteria and conditions.
        """
        args = pretrigger.args
        text = args[-1] if args else ''
        event = pretrigger.event
        intent = pretrigger.tags.get('intent')
        nick = pretrigger.nick
        is_echo_message = (nick.lower() == self.nick.lower()
                           and event in ["PRIVMSG", "NOTICE"])
        user_obj = self.users.get(nick)
        account = user_obj.account if user_obj else None

        # get a copy of the list of (regex, function) to prevent race-condition
        # e.g. when a callable wants to remove callable (other or itself)
        # from the bot, Python won't (and must not) allow to modify a dict
        # while it iterates over this very same dict.
        items = list(self._callables[priority].items())

        for regexp, funcs in items:
            match = regexp.match(text)
            if not match:
                continue

            for func in funcs:
                trigger = Trigger(self.settings, pretrigger, match, account)

                # check event
                if event not in func.event:
                    continue

                # check intents
                if hasattr(func, 'intents'):
                    if not intent:
                        continue

                    match = any(
                        func_intent.match(intent)
                        for func_intent in func.intents)
                    if not match:
                        continue

                # check echo-message feature
                if is_echo_message and not func.echo:
                    continue

                is_unblockable = func.unblockable or trigger.admin
                is_blocked = blocked and not is_unblockable
                yield (func, trigger, is_blocked)
Example #24
0
def trigger_owner(bot):
    line = ':[email protected] PRIVMSG #Sopel :Hello, world'
    return Trigger(bot.config, PreTrigger(tools.Identifier('Bar'), line), None)
Example #25
0
def trigger(bot, pretrigger):
    return Trigger(bot.config, pretrigger, None)
Example #26
0
def trigger_pm(bot, pretrigger_pm):
    return Trigger(bot.config, pretrigger_pm, None)