예제 #1
0
    async def test_event(self) -> None:
        bot = MockBot()
        conn = MockConn(nick="bot")
        event = Event(
            irc_command="PRIVMSG",
            event_type=EventType.message,
            channel="#foo",
            nick="bar",
            conn=conn,
            content=".foo bar",
        )
        event.notice = MagicMock()
        plugin = MagicMock()

        run_hooks = []

        @hook.event(EventType.message)
        async def coro(hook):
            run_hooks.append(hook)

        full_event_hook = EventHook(plugin, hook._get_hook(coro, "event"))
        for event_type in full_event_hook.types:
            bot.plugin_manager.event_type_hooks[event_type].append(
                full_event_hook)

        await CloudBot.process(bot, event)
        assert sorted(run_hooks, key=id) == sorted(
            [
                full_event_hook,
            ],
            key=id,
        )
예제 #2
0
    async def test_command(self) -> None:
        bot = MockBot()
        conn = MockConn(nick="bot")
        event = Event(
            irc_command="PRIVMSG",
            event_type=EventType.message,
            channel="#foo",
            nick="bar",
            conn=conn,
            content=".foo bar",
        )
        event.notice = MagicMock()
        plugin = MagicMock()

        run_hooks = []

        @hook.command("foo")
        async def coro(hook):
            run_hooks.append(hook)

        full_hook = CommandHook(plugin, hook._get_hook(coro, "command"))

        for cmd in full_hook.aliases:
            bot.plugin_manager.commands[cmd] = full_hook

        await CloudBot.process(bot, event)
        assert sorted(run_hooks, key=id) == sorted(
            [
                full_hook,
            ],
            key=id,
        )
예제 #3
0
    async def test_irc_catch_all(self) -> None:
        bot = MockBot()
        conn = MockConn(nick="bot")
        event = Event(
            irc_command="PRIVMSG",
            event_type=EventType.message,
            channel="#foo",
            nick="bar",
            conn=conn,
            content=".foo bar",
        )
        event.notice = MagicMock()
        plugin = MagicMock()

        run_hooks = []

        @hook.irc_raw("*")
        async def coro(hook):
            run_hooks.append(hook)

        full_hook = RawHook(plugin, hook._get_hook(coro, "irc_raw"))
        bot.plugin_manager.catch_all_triggers.append(full_hook)

        await CloudBot.process(bot, event)
        assert sorted(run_hooks, key=id) == sorted(
            [
                full_hook,
            ],
            key=id,
        )
예제 #4
0
    async def test_command_partial(self) -> None:
        bot = MockBot()
        conn = MockConn(nick="bot")
        event = Event(
            irc_command="PRIVMSG",
            event_type=EventType.message,
            channel="#foo",
            nick="bar",
            conn=conn,
            content=".foo bar",
        )
        event.notice = MagicMock()
        plugin = MagicMock()

        run_hooks = []

        @hook.command("foob", "fooc")
        async def coro(hook):  # pragma: no cover
            run_hooks.append(hook)

        full_hook = CommandHook(plugin, hook._get_hook(coro, "command"))

        for cmd in full_hook.aliases:
            bot.plugin_manager.commands[cmd] = full_hook

        await CloudBot.process(bot, event)
        assert sorted(run_hooks, key=id) == sorted(
            [],
            key=id,
        )

        event.notice.assert_called_once_with("Possible matches: foob or fooc")
예제 #5
0
async def perm_sieve(bot: CloudBot, event: Event, _hook: Hook) -> Optional[Event]:
    """check permissions"""
    allowed_permissions = _hook.permissions
    if allowed_permissions:
        allowed = False
        for perm in allowed_permissions:
            if await event.check_permission(perm):
                allowed = True
                break

        if not allowed:
            event.notice("Sorry, you are not allowed to use this command.")
            return None

    return event
예제 #6
0
    async def _connect(self, timeout=None):
        # connect to the clients server
        if self.connected:
            logger.info("[%s] Reconnecting", self.name)
            self.quit("Reconnecting...")
        else:
            logger.info("[%s] Connecting", self.name)

        self._active = True

        optional_params = {}
        if self.local_bind:
            optional_params["local_addr"] = self.local_bind

        coro = self.loop.create_connection(
            partial(_IrcProtocol, self),
            host=self.server,
            port=self.port,
            ssl=self.ssl_context,
            **optional_params,
        )

        if timeout is not None:
            coro = asyncio.wait_for(coro, timeout)

        self._transport, self._protocol = await coro

        tasks = [
            self.bot.plugin_manager.launch(
                hook, Event(bot=self.bot, conn=self, hook=hook))
            for hook in self.bot.plugin_manager.connect_hooks
            if not hook.clients or self.type in hook.clients
        ]
        # TODO stop connecting if a connect hook fails?
        await asyncio.gather(*tasks)
예제 #7
0
def test_hook_args(hook):
    bot = MockBot()
    if hook.type in (
            "irc_raw",
            "perm_check",
            "periodic",
            "on_start",
            "on_stop",
            "event",
            "on_connect",
    ):
        event = Event(bot=bot)
    elif hook.type == "command":
        event = CommandEvent(bot=bot,
                             hook=hook,
                             text="",
                             triggered_command="",
                             cmd_prefix=".")
    elif hook.type == "regex":
        event = RegexEvent(bot=bot, hook=hook, match=None)
    elif hook.type.startswith("on_cap"):
        event = CapEvent(bot=bot, cap="")
    elif hook.type == "post_hook":
        event = PostHookEvent(bot=bot)
    elif hook.type == "irc_out":
        event = IrcOutEvent(bot=bot)
    elif hook.type == "sieve":
        return
    else:  # pragma: no cover
        assert False, "Unhandled hook type '{}' in tests".format(hook.type)

    for arg in hook.required_args:
        assert hasattr(
            event,
            arg), "Undefined parameter '{}' for hook function".format(arg)
예제 #8
0
파일: irc.py 프로젝트: Vault108-zz/Bot
    def connect(self):
        """
        Connects to the IRC server, or reconnects if already connected.
        """
        # connect to the clients server
        if self._quit:
            # we've quit, so close instead (because this has probably been called because of EOF received)
            self.close()
            return

        if self._connected:
            logger.info("[{}] Reconnecting".format(self.name))
            self._transport.close()
        else:
            self._connected = True
            logger.info("[{}] Connecting".format(self.name))
        optional_params = {}
        if self.local_bind:
            optional_params["local_addr"] = self.local_bind
        self._transport, self._protocol = yield from self.loop.create_connection(
            lambda: _IrcProtocol(self),
            host=self.server,
            port=self.port,
            ssl=self.ssl_context,
            **optional_params)

        tasks = [
            self.bot.plugin_manager.launch(
                hook, Event(bot=self.bot, conn=self, hook=hook))
            for hook in self.bot.plugin_manager.connect_hooks
        ]
        # TODO stop connecting if a connect hook fails?
        yield from asyncio.gather(*tasks)
예제 #9
0
    def test_flood(self, clear_cache, freeze_time):
        chan = "#foo"
        nick = "foobaruser"
        nick1 = "foonick"
        chan1 = "#barchan"
        herald.herald_cache[chan].update({
            nick: "Some herald",
            nick1: "Other herald,"
        })
        herald.herald_cache[chan1].update({
            nick: "Someother herald",
            nick1: "Yet another herald"
        })
        conn = MagicMock(name="fooconn")
        conn.mock_add_spec(["name", "bot", "action", "message", "notice"])
        event = Event(conn=conn, bot=conn.bot, channel=chan, nick=nick)
        event1 = Event(conn=conn, bot=conn.bot, channel=chan, nick=nick1)
        event2 = Event(
            conn=conn,
            bot=conn.bot,
            channel=chan1,
            nick=nick,
        )
        event3 = Event(conn=conn, bot=conn.bot, channel=chan1, nick=nick1)

        def check(ev):
            return wrap_hook_response(herald.welcome, ev)

        assert check(event) == [("message", ("#foo", "\u200b Some herald"))]

        assert check(event) == []
        assert check(event1) == []

        assert check(event2) == [("message", ("#barchan",
                                              "\u200b Someother herald"))]
        assert check(event3) == []

        freeze_time.tick(timedelta(seconds=10))
        # Channel spam time expired
        assert check(event1) == [("message", ("#foo", "\u200b Other herald,"))]
        # User spam time still in effect
        assert check(event) == []

        freeze_time.tick(timedelta(minutes=5))

        # User spam time expired
        assert check(event) == [("message", ("#foo", "\u200b Some herald"))]
예제 #10
0
def _do_test(
    plugin_name,
    loader,
    data_name,
    cmd,
    text: Optional[str] = "test _ data",
    is_nick_valid=None,
    nick=None,
    bot_nick=None,
):
    plugin = importlib.import_module("plugins." + plugin_name)
    bot = MagicMock()
    bot.data_dir = "data"
    bot.loop = asyncio.get_event_loop()
    event = Event(
        hook=MagicMock(),
        bot=bot,
        conn=MagicMock(),
        channel="#foo",
        nick=nick or "foobar",
    )
    if bot_nick:
        event.conn.nick = bot_nick
    else:
        event.conn.nick = "TestBot"

    if is_nick_valid:
        event.is_nick_valid = is_nick_valid

    if loader:
        _call(getattr(plugin, loader), event)

    if data_name:
        assert getattr(plugin, data_name)

    cmd_func = getattr(plugin, cmd)
    cmd_event = CommandEvent(
        text=text or "",
        cmd_prefix=".",
        hook=MagicMock(),
        triggered_command="foo",
        base_event=event,
    )
    if is_nick_valid:
        cmd_event.is_nick_valid = is_nick_valid

    return _call(cmd_func, cmd_event), cmd_event
예제 #11
0
 def _run(self):
     conn = MagicMock()
     event = Event(hook=MagicMock(),
                   bot=conn.bot,
                   conn=conn,
                   channel='#foo',
                   nick='foobaruser')
     return wrap_hook_response(herald.welcome, event)
예제 #12
0
    async def _start_periodic(self, hook):
        interval = hook.interval
        initial_interval = hook.initial_interval
        await asyncio.sleep(initial_interval)

        while True:
            event = Event(bot=self.bot, hook=hook)
            await self.launch(hook, event)
            await asyncio.sleep(interval)
예제 #13
0
파일: plugin.py 프로젝트: QuinceP/Quincebot
    def _start_periodic(self, hook):
        interval = hook.interval
        initial_interval = hook.initial_interval
        yield from asyncio.sleep(initial_interval)

        while True:
            event = Event(bot=self.bot, hook=hook)
            yield from self.launch(hook, event)
            yield from asyncio.sleep(interval)
예제 #14
0
 def cmd(cmd, subcmd, *args):
     calls.append((cmd, subcmd) + args)
     p = ParamList.parse("* ACK :" + " ".join(args))
     cmd_event = Event(
         irc_paramlist=p,
         bot=event.bot,
         conn=event.conn,
     )
     async_util.wrap_future(cap.on_cap(p, cmd_event), loop=event.loop)
예제 #15
0
def test_event_copy():
    from cloudbot.event import Event

    event = Event(bot=object(),
                  conn=object(),
                  hook=object(),
                  event_type=object(),
                  channel=object(),
                  nick=object(),
                  user=object(),
                  host=object())

    new_event = Event(base_event=event)

    assert event.bot is new_event.bot
    assert event.conn is new_event.conn
    assert event.hook is new_event.hook
    assert event.nick is new_event.nick
예제 #16
0
def _do_test(plugin_name,
             loader,
             data_name,
             cmd,
             text='test _ data',
             is_nick_valid=None,
             nick=None,
             bot_nick=None):
    plugin = importlib.import_module('plugins.' + plugin_name)
    bot = MagicMock()
    bot.data_dir = 'data'
    bot.loop = asyncio.get_event_loop()
    event = Event(hook=MagicMock(),
                  bot=bot,
                  conn=MagicMock(),
                  channel='#foo',
                  nick=nick or 'foobar')
    if bot_nick:
        event.conn.nick = bot_nick
    else:
        event.conn.nick = 'TestBot'

    if is_nick_valid:
        event.is_nick_valid = is_nick_valid

    if loader:
        _call(getattr(plugin, loader), event)

    if data_name:
        assert getattr(plugin, data_name)

    cmd_func = getattr(plugin, cmd)
    cmd_event = CommandEvent(text=text or '',
                             cmd_prefix='.',
                             hook=MagicMock(),
                             triggered_command='foo',
                             base_event=event)
    if is_nick_valid:
        cmd_event.is_nick_valid = is_nick_valid

    return _call(cmd_func, cmd_event), cmd_event
예제 #17
0
    async def reload_config(self):
        self.config.load_config()

        # reload permissions
        for connection in self.connections.values():
            connection.reload()

        tasks = [
            self.plugin_manager.launch(hook, Event(bot=self, hook=hook))
            for hook in self.plugin_manager.config_hooks
        ]

        await asyncio.gather(*tasks)
예제 #18
0
    async def test_irc_raw_block(self):
        bot = MockBot()
        conn = MockConn(nick="bot")
        event = Event(
            irc_command="PRIVMSG",
            event_type=EventType.message,
            channel="#foo",
            nick="bar",
            conn=conn,
            content=".foo bar",
        )
        event.notice = MagicMock()
        plugin = MagicMock()

        run_hooks = []

        @hook.irc_raw("PRIVMSG",
                      priority=Priority.HIGH,
                      action=Action.HALTTYPE)
        async def coro(hook):
            run_hooks.append(hook)

        @hook.irc_raw("PRIVMSG", priority=Priority.NORMAL)
        async def coro1(hook):  # pragma: no cover
            run_hooks.append(hook)

        full_hook = RawHook(plugin, hook._get_hook(coro, "irc_raw"))
        full_hook1 = RawHook(plugin, hook._get_hook(coro1, "irc_raw"))
        bot.plugin_manager.raw_triggers["PRIVMSG"].append(full_hook)
        bot.plugin_manager.raw_triggers["PRIVMSG"].append(full_hook1)

        await CloudBot.process(bot, event)
        assert sorted(run_hooks, key=id) == sorted(
            [
                full_hook,
            ],
            key=id,
        )
예제 #19
0
def test_get_cmd_regex():
    event = Event(channel="TestUser", nick="TestUser", conn=MockConn("Bot"))
    regex = get_cmd_regex(event)
    assert textwrap.dedent(regex.pattern) == textwrap.dedent(r"""
    ^
    # Prefix or nick
    (?:
        (?P<prefix>[\.])?
        |
        Bot[,;:]+\s+
    )
    (?P<command>\w+)  # Command
    (?:$|\s+)
    (?P<text>.*)     # Text
    """)
예제 #20
0
    def quit(self, reason=None):
        if self._quit:
            return
        self._quit = True
        if reason:
            self.cmd("QUIT", reason)
        else:
            self.cmd("QUIT")

        # Log ourselves quitting
        quit_event = Event(bot=self.bot,
                           conn=self,
                           event_type=EventType.quit,
                           nick=self.bot_nick)
        yield from self._process_quit(quit_event)
예제 #21
0
def test_get_cmd_regex():
    from cloudbot.bot import get_cmd_regex
    from cloudbot.event import Event
    event = Event(channel='TestUser', nick='TestUser', conn=MockConn('Bot'))
    regex = get_cmd_regex(event)
    assert textwrap.dedent(regex.pattern) == textwrap.dedent(r"""
    ^
    # Prefix or nick
    (?:
        (?P<prefix>[\.])?
        |
        Bot[,;:]+\s+
    )
    (?P<command>\w+)  # Command
    (?:$|\s+)
    (?P<text>.*)     # Text
    """)
예제 #22
0
    async def load_plugin(self, path):
        """
        Loads a plugin from the given path and plugin object,
        then registers all hooks from that plugin.

        :type path: str | Path
        """

        path = Path(path)
        file_path = self.safe_resolve(path)
        file_name = file_path.name
        # Resolve the path relative to the current directory
        plugin_path = file_path.relative_to(self.bot.base_dir)
        title = '.'.join(plugin_path.parts[1:]).rsplit('.', 1)[0]

        if not self.can_load(title):
            return

        # make sure to unload the previously loaded plugin from this path, if it was loaded.
        if self.get_plugin(file_path):
            await self.unload_plugin(file_path)

        module_name = "plugins.{}".format(title)
        try:
            plugin_module = self._load_mod(module_name)
        except Exception:
            logger.exception("Error loading %s:", title)
            return

        # create the plugin
        plugin = Plugin(str(file_path), file_name, title, plugin_module)

        # proceed to register hooks

        # create database tables
        await plugin.create_tables(self.bot)

        # run on_start hooks
        for on_start_hook in plugin.hooks["on_start"]:
            success = await self.launch(
                on_start_hook, Event(bot=self.bot, hook=on_start_hook))
            if not success:
                logger.warning(
                    "Not registering hooks from plugin %s: on_start hook errored",
                    plugin.title)

                # unregister databases
                plugin.unregister_tables(self.bot)
                return

        self._add_plugin(plugin)

        for on_cap_available_hook in plugin.hooks["on_cap_available"]:
            for cap in on_cap_available_hook.caps:
                self.cap_hooks["on_available"][cap.casefold()].append(
                    on_cap_available_hook)
            self._log_hook(on_cap_available_hook)

        for on_cap_ack_hook in plugin.hooks["on_cap_ack"]:
            for cap in on_cap_ack_hook.caps:
                self.cap_hooks["on_ack"][cap.casefold()].append(
                    on_cap_ack_hook)
            self._log_hook(on_cap_ack_hook)

        for periodic_hook in plugin.hooks["periodic"]:
            task = async_util.wrap_future(self._start_periodic(periodic_hook))
            plugin.tasks.append(task)
            self._log_hook(periodic_hook)

        # register commands
        for command_hook in plugin.hooks["command"]:
            for alias in command_hook.aliases:
                if alias in self.commands:
                    logger.warning(
                        "Plugin %s attempted to register command %s which was "
                        "already registered by %s. Ignoring new assignment.",
                        plugin.title, alias, self.commands[alias].plugin.title)
                else:
                    self.commands[alias] = command_hook
            self._log_hook(command_hook)

        # register raw hooks
        for raw_hook in plugin.hooks["irc_raw"]:
            if raw_hook.is_catch_all():
                self.catch_all_triggers.append(raw_hook)
            else:
                for trigger in raw_hook.triggers:
                    if trigger in self.raw_triggers:
                        self.raw_triggers[trigger].append(raw_hook)
                    else:
                        self.raw_triggers[trigger] = [raw_hook]
            self._log_hook(raw_hook)

        # register events
        for event_hook in plugin.hooks["event"]:
            for event_type in event_hook.types:
                if event_type in self.event_type_hooks:
                    self.event_type_hooks[event_type].append(event_hook)
                else:
                    self.event_type_hooks[event_type] = [event_hook]
            self._log_hook(event_hook)

        # register regexps
        for regex_hook in plugin.hooks["regex"]:
            for regex_match in regex_hook.regexes:
                self.regex_hooks.append((regex_match, regex_hook))
            self._log_hook(regex_hook)

        # register sieves
        for sieve_hook in plugin.hooks["sieve"]:
            self.sieves.append(sieve_hook)
            self._log_hook(sieve_hook)

        # register connect hooks
        for connect_hook in plugin.hooks["on_connect"]:
            self.connect_hooks.append(connect_hook)
            self._log_hook(connect_hook)

        for out_hook in plugin.hooks["irc_out"]:
            self.out_sieves.append(out_hook)
            self._log_hook(out_hook)

        for post_hook in plugin.hooks["post_hook"]:
            self.hook_hooks["post"].append(post_hook)
            self._log_hook(post_hook)

        for perm_hook in plugin.hooks["perm_check"]:
            for perm in perm_hook.perms:
                self.perm_hooks[perm].append(perm_hook)

            self._log_hook(perm_hook)

        # Sort hooks
        self.regex_hooks.sort(key=lambda x: x[1].priority)
        dicts_of_lists_of_hooks = (self.event_type_hooks, self.raw_triggers,
                                   self.perm_hooks, self.hook_hooks)
        lists_of_hooks = [
            self.catch_all_triggers, self.sieves, self.connect_hooks,
            self.out_sieves
        ]
        lists_of_hooks.extend(
            chain.from_iterable(d.values() for d in dicts_of_lists_of_hooks))

        for lst in lists_of_hooks:
            lst.sort(key=attrgetter("priority"))

        # we don't need this anymore
        del plugin.hooks["on_start"]
예제 #23
0
    def parse_line(self, line: str) -> Event:
        message = Message.parse(line)
        command = message.command
        command_params = message.parameters

        # Reply to pings immediately
        if command == "PING":
            self.conn.send("PONG " + command_params[-1], log=False)

        # Parse the command and params
        # Content
        content_raw = _get_param(message, content_params)
        if content_raw is not None:
            content = irc_clean(content_raw)
        else:
            content = None

        # Event type
        event_type = irc_command_to_event_type.get(command, EventType.other)
        target = _get_param(message, target_params)

        # Parse for CTCP
        if event_type is EventType.message and content_raw.startswith("\x01"):
            possible_ctcp = content_raw[1:]
            if content_raw.endswith("\x01"):
                possible_ctcp = possible_ctcp[:-1]

            if "\x01" in possible_ctcp:
                logger.debug(
                    "[%s] Invalid CTCP message received, "
                    "treating it as a mornal message",
                    self.conn.name,
                )
                ctcp_text = None
            else:
                ctcp_text = possible_ctcp
                ctcp_text_split = ctcp_text.split(None, 1)
                if ctcp_text_split[0] == "ACTION":
                    # this is a CTCP ACTION, set event_type and content accordingly
                    event_type = EventType.action
                    content = irc_clean(ctcp_text_split[1])
                else:
                    # this shouldn't be considered a regular message
                    event_type = EventType.other
        else:
            ctcp_text = None

        # Channel
        channel = _get_param(message, chan_params)

        prefix = message.prefix
        if prefix is None:
            nick = None
            user = None
            host = None
            mask = None
        else:
            nick = prefix.nick
            user = prefix.user
            host = prefix.host
            mask = prefix.mask

        if channel:
            # TODO Migrate plugins to accept the original case of the channel
            channel = channel.lower()

            channel = channel.split()[0]  # Just in case there is more data

            # Channel for a PM is the sending user
            if channel == self.conn.nick.lower():
                channel = nick.lower()
        else:
            # If the channel isn't set, it's the sending user/server
            channel = nick.lower() if nick else nick

        # Set up parsed message
        # TODO: Do we really want to send the raw `prefix` and `command_params` here?
        event = Event(
            bot=self.bot,
            conn=self.conn,
            event_type=event_type,
            content_raw=content_raw,
            content=content,
            target=target,
            channel=channel,
            nick=nick,
            user=user,
            host=host,
            mask=mask,
            irc_raw=line,
            irc_prefix=mask,
            irc_command=command,
            irc_paramlist=command_params,
            irc_ctcp_text=ctcp_text,
            irc_tags=message.tags,
        )
        return event
예제 #24
0
파일: plugin.py 프로젝트: QuinceP/Quincebot
    def load_plugin(self, path):
        """
        Loads a plugin from the given path and plugin object, then registers all hooks from that plugin.

        Won't load any plugins listed in "disabled_plugins".

        :type path: str
        """

        file_path = os.path.abspath(path)
        file_name = os.path.basename(path)
        title = os.path.splitext(file_name)[0]

        if "plugin_loading" in self.bot.config:
            pl = self.bot.config.get("plugin_loading")

            if pl.get("use_whitelist", False):
                if title not in pl.get("whitelist", []):
                    logger.info(
                        'Not loading plugin module "{}": plugin not whitelisted'
                        .format(file_name))
                    return
            else:
                if title in pl.get("blacklist", []):
                    logger.info(
                        'Not loading plugin module "{}": plugin blacklisted'.
                        format(file_name))
                    return

        # make sure to unload the previously loaded plugin from this path, if it was loaded.
        if file_name in self.plugins:
            yield from self.unload_plugin(file_path)

        module_name = "plugins.{}".format(title)
        try:
            plugin_module = importlib.import_module(module_name)
            # if this plugin was loaded before, reload it
            if hasattr(plugin_module, "_cloudbot_loaded"):
                importlib.reload(plugin_module)
        except Exception:
            logger.exception("Error loading {}:".format(file_name))
            return

        # create the plugin
        plugin = Plugin(file_path, file_name, title, plugin_module)

        # proceed to register hooks

        # create database tables
        yield from plugin.create_tables(self.bot)

        # run on_start hooks
        for on_start_hook in plugin.run_on_start:
            success = yield from self.launch(
                on_start_hook, Event(bot=self.bot, hook=on_start_hook))
            if not success:
                logger.warning(
                    "Not registering hooks from plugin {}: on_start hook errored"
                    .format(plugin.title))

                # unregister databases
                plugin.unregister_tables(self.bot)
                return

        self.plugins[plugin.file_name] = plugin

        for periodic_hook in plugin.periodic:
            asyncio. async (self._start_periodic(periodic_hook))
            self._log_hook(periodic_hook)

        # register commands
        for command_hook in plugin.commands:
            for alias in command_hook.aliases:
                if alias in self.commands:
                    logger.warning(
                        "Plugin {} attempted to register command {} which was already registered by {}. "
                        "Ignoring new assignment.".format(
                            plugin.title, alias,
                            self.commands[alias].plugin.title))
                else:
                    self.commands[alias] = command_hook
            self._log_hook(command_hook)

        # register raw hooks
        for raw_hook in plugin.raw_hooks:
            if raw_hook.is_catch_all():
                self.catch_all_triggers.append(raw_hook)
            else:
                for trigger in raw_hook.triggers:
                    if trigger in self.raw_triggers:
                        self.raw_triggers[trigger].append(raw_hook)
                    else:
                        self.raw_triggers[trigger] = [raw_hook]
            self._log_hook(raw_hook)

        # register events
        for event_hook in plugin.events:
            for event_type in event_hook.types:
                if event_type in self.event_type_hooks:
                    self.event_type_hooks[event_type].append(event_hook)
                else:
                    self.event_type_hooks[event_type] = [event_hook]
            self._log_hook(event_hook)

        # register regexps
        for regex_hook in plugin.regexes:
            for regex_match in regex_hook.regexes:
                self.regex_hooks.append((regex_match, regex_hook))
            self._log_hook(regex_hook)

        # register sieves
        for sieve_hook in plugin.sieves:
            self.sieves.append(sieve_hook)
            self._log_hook(sieve_hook)

        # sort sieve hooks by priority
        self.sieves.sort(key=lambda x: x.priority)

        # we don't need this anymore
        del plugin.run_on_start
예제 #25
0
    async def unload_plugin(self, path):
        """
        Unloads the plugin from the given path, unregistering all hooks from the plugin.

        Returns True if the plugin was unloaded, False if the plugin wasn't loaded in the first place.

        :type path: str | Path
        :rtype: bool
        """
        path = Path(path)
        file_path = self.safe_resolve(path)

        # make sure this plugin is actually loaded
        plugin = self.get_plugin(file_path)
        if not plugin:
            return False

        for on_cap_available_hook in plugin.hooks["on_cap_available"]:
            available_hooks = self.cap_hooks["on_available"]
            for cap in on_cap_available_hook.caps:
                cap_cf = cap.casefold()
                available_hooks[cap_cf].remove(on_cap_available_hook)
                if not available_hooks[cap_cf]:
                    del available_hooks[cap_cf]

        for on_cap_ack in plugin.hooks["on_cap_ack"]:
            ack_hooks = self.cap_hooks["on_ack"]
            for cap in on_cap_ack.caps:
                cap_cf = cap.casefold()
                ack_hooks[cap_cf].remove(on_cap_ack)
                if not ack_hooks[cap_cf]:
                    del ack_hooks[cap_cf]

        # unregister commands
        for command_hook in plugin.hooks["command"]:
            for alias in command_hook.aliases:
                if alias in self.commands and self.commands[
                        alias] == command_hook:
                    # we need to make sure that there wasn't a conflict, so we don't delete another plugin's command
                    del self.commands[alias]

        # unregister raw hooks
        for raw_hook in plugin.hooks["irc_raw"]:
            if raw_hook.is_catch_all():
                self.catch_all_triggers.remove(raw_hook)
            else:
                for trigger in raw_hook.triggers:
                    assert trigger in self.raw_triggers  # this can't be not true
                    self.raw_triggers[trigger].remove(raw_hook)
                    if not self.raw_triggers[
                            trigger]:  # if that was the last hook for this trigger
                        del self.raw_triggers[trigger]

        # unregister events
        for event_hook in plugin.hooks["event"]:
            for event_type in event_hook.types:
                assert event_type in self.event_type_hooks  # this can't be not true
                self.event_type_hooks[event_type].remove(event_hook)
                if not self.event_type_hooks[
                        event_type]:  # if that was the last hook for this event type
                    del self.event_type_hooks[event_type]

        # unregister regexps
        for regex_hook in plugin.hooks["regex"]:
            for regex_match in regex_hook.regexes:
                self.regex_hooks.remove((regex_match, regex_hook))

        # unregister sieves
        for sieve_hook in plugin.hooks["sieve"]:
            self.sieves.remove(sieve_hook)

        # unregister connect hooks
        for connect_hook in plugin.hooks["on_connect"]:
            self.connect_hooks.remove(connect_hook)

        for out_hook in plugin.hooks["irc_out"]:
            self.out_sieves.remove(out_hook)

        for post_hook in plugin.hooks["post_hook"]:
            self.hook_hooks["post"].remove(post_hook)

        for perm_hook in plugin.hooks["perm_check"]:
            for perm in perm_hook.perms:
                self.perm_hooks[perm].remove(perm_hook)

        # Run on_stop hooks
        for on_stop_hook in plugin.hooks["on_stop"]:
            event = Event(bot=self.bot, hook=on_stop_hook)
            await self.launch(on_stop_hook, event)

        # unregister databases
        plugin.unregister_tables(self.bot)

        task_count = len(plugin.tasks)
        if task_count > 0:
            logger.debug("Cancelling running tasks in %s", plugin.title)
            for task in plugin.tasks:
                task.cancel()

            logger.info("Cancelled %d tasks from %s", task_count, plugin.title)

        # remove last reference to plugin
        self._rem_plugin(plugin)

        if self.bot.config.get("logging", {}).get("show_plugin_loading", True):
            logger.info("Unloaded all plugins from %s", plugin.title)

        return True
예제 #26
0
파일: bot.py 프로젝트: Vault108-zz/Bot
    def process(self, event):
        """
        :type event: Event
        """
        run_before_tasks = []
        tasks = []
        command_prefix = event.conn.config.get('command_prefix', '.')
        halted = False

        def add_hook(hook, _event, _run_before=False):
            nonlocal halted
            if halted:
                return False

            coro = self.plugin_manager.launch(hook, _event)
            if _run_before:
                run_before_tasks.append(coro)
            else:
                tasks.append(coro)

            if hook.action is Action.HALTALL:
                halted = True
                return False
            elif hook.action is Action.HALTTYPE:
                return False
            return True

        # Raw IRC hook
        for raw_hook in self.plugin_manager.catch_all_triggers:
            # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument
            run_before = not raw_hook.threaded
            if not add_hook(raw_hook,
                            Event(hook=raw_hook, base_event=event),
                            _run_before=run_before):
                # The hook has an action of Action.HALT* so stop adding new tasks
                break

        if event.irc_command in self.plugin_manager.raw_triggers:
            for raw_hook in self.plugin_manager.raw_triggers[
                    event.irc_command]:
                if not add_hook(raw_hook, Event(hook=raw_hook,
                                                base_event=event)):
                    # The hook has an action of Action.HALT* so stop adding new tasks
                    break

        # Event hooks
        if event.type in self.plugin_manager.event_type_hooks:
            for event_hook in self.plugin_manager.event_type_hooks[event.type]:
                if not add_hook(event_hook,
                                Event(hook=event_hook, base_event=event)):
                    # The hook has an action of Action.HALT* so stop adding new tasks
                    break

        if event.type is EventType.message:
            # Commands
            if event.chan.lower() == event.nick.lower(
            ):  # private message, no command prefix
                command_re = r'(?i)^(?:[{}]?|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'
            else:
                command_re = r'(?i)^(?:[{}]|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'

            cmd_match = re.match(
                command_re.format(command_prefix, event.conn.nick),
                event.content_raw)

            if cmd_match:
                command = cmd_match.group(1).lower()
                text = irc_clean(cmd_match.group(2).strip())
                if command in self.plugin_manager.commands:
                    command_hook = self.plugin_manager.commands[command]
                    command_event = CommandEvent(hook=command_hook,
                                                 text=text,
                                                 triggered_command=command,
                                                 base_event=event)
                    add_hook(command_hook, command_event)
                else:
                    potential_matches = []
                    for potential_match, plugin in self.plugin_manager.commands.items(
                    ):
                        if potential_match.startswith(command):
                            potential_matches.append((potential_match, plugin))
                    if potential_matches:
                        if len(potential_matches) == 1:
                            command_hook = potential_matches[0][1]
                            command_event = CommandEvent(
                                hook=command_hook,
                                text=text,
                                triggered_command=command,
                                base_event=event)
                            add_hook(command_hook, command_event)
                        else:
                            event.notice("Possible matches: {}".format(
                                formatting.get_text_list([
                                    command
                                    for command, plugin in potential_matches
                                ])))

            # Regex hooks
            regex_matched = False
            for regex, regex_hook in self.plugin_manager.regex_hooks:
                if not regex_hook.run_on_cmd and cmd_match:
                    continue

                if regex_hook.only_no_match and regex_matched:
                    continue

                regex_match = regex.search(event.content)
                if regex_match:
                    regex_matched = True
                    regex_event = RegexEvent(hook=regex_hook,
                                             match=regex_match,
                                             base_event=event)
                    if not add_hook(regex_hook, regex_event):
                        # The hook has an action of Action.HALT* so stop adding new tasks
                        break

        # Run the tasks
        yield from asyncio.gather(*run_before_tasks, loop=self.loop)
        yield from asyncio.gather(*tasks, loop=self.loop)
예제 #27
0
    def data_received(self, data):
        self._input_buffer += data

        while b"\r\n" in self._input_buffer:
            line_data, self._input_buffer = self._input_buffer.split(b"\r\n", 1)
            line = decode(line_data)

            try:
                message = Message.parse(line)
            except Exception:
                logger.exception(
                    "[%s] Error occurred while parsing IRC line '%s' from %s",
                    self.conn.name, line, self.conn.describe_server()
                )
                continue

            command = message.command
            command_params = message.parameters

            # Reply to pings immediately

            if command == "PING":
                self.conn.send("PONG " + command_params[-1], log=False)

            # Parse the command and params

            # Content
            if command_params.has_trail:
                content_raw = command_params[-1]
                content = irc_clean(content_raw)
            else:
                content_raw = None
                content = None

            # Event type
            event_type = irc_command_to_event_type.get(
                command, EventType.other
            )

            # Target (for KICK, INVITE)
            if event_type is EventType.kick:
                target = command_params[1]
            elif command in ("INVITE", "MODE"):
                target = command_params[0]
            else:
                # TODO: Find more commands which give a target
                target = None

            # Parse for CTCP
            if event_type is EventType.message and content_raw.startswith("\x01"):
                possible_ctcp = content_raw[1:]
                if content_raw.endswith('\x01'):
                    possible_ctcp = possible_ctcp[:-1]

                if '\x01' in possible_ctcp:
                    logger.debug(
                        "[%s] Invalid CTCP message received, "
                        "treating it as a mornal message",
                        self.conn.name
                    )
                    ctcp_text = None
                else:
                    ctcp_text = possible_ctcp
                    ctcp_text_split = ctcp_text.split(None, 1)
                    if ctcp_text_split[0] == "ACTION":
                        # this is a CTCP ACTION, set event_type and content accordingly
                        event_type = EventType.action
                        content = irc_clean(ctcp_text_split[1])
                    else:
                        # this shouldn't be considered a regular message
                        event_type = EventType.other
            else:
                ctcp_text = None

            # Channel
            channel = None
            if command_params:
                if command in ["NOTICE", "PRIVMSG", "KICK", "JOIN", "PART", "MODE"]:
                    channel = command_params[0]
                elif command == "INVITE":
                    channel = command_params[1]
                elif len(command_params) > 2 or not (command_params.has_trail and len(command_params) == 1):
                    channel = command_params[0]

            prefix = message.prefix

            if prefix is None:
                nick = None
                user = None
                host = None
                mask = None
            else:
                nick = prefix.nick
                user = prefix.user
                host = prefix.host
                mask = prefix.mask

            if channel:
                # TODO Migrate plugins to accept the original case of the channel
                channel = channel.lower()

                channel = channel.split()[0]  # Just in case there is more data

                if channel == self.conn.nick.lower():
                    channel = nick.lower()

            # Set up parsed message
            # TODO: Do we really want to send the raw `prefix` and `command_params` here?
            event = Event(
                bot=self.bot, conn=self.conn, event_type=event_type, content_raw=content_raw, content=content,
                target=target, channel=channel, nick=nick, user=user, host=host, mask=mask, irc_raw=line,
                irc_prefix=mask, irc_command=command, irc_paramlist=command_params, irc_ctcp_text=ctcp_text
            )

            # handle the message, async
            async_util.wrap_future(self.bot.process(event), loop=self.loop)
예제 #28
0
def handle_error(event: Event, error):
    event.reply("Failed to contact thetvdb.com")
    raise error
예제 #29
0
파일: bot.py 프로젝트: rjshaver/mycroft
    async def process(self, event):
        """
        :type event: Event
        """
        run_before_tasks = []
        tasks = []
        halted = False

        def add_hook(hook, _event, _run_before=False):
            nonlocal halted
            if halted:
                return False

            if hook.clients and _event.conn.type not in hook.clients:
                return True

            coro = self.plugin_manager.launch(hook, _event)
            if _run_before:
                run_before_tasks.append(coro)
            else:
                tasks.append(coro)

            if hook.action is Action.HALTALL:
                halted = True
                return False

            if hook.action is Action.HALTTYPE:
                return False

            return True

        # Raw IRC hook
        for raw_hook in self.plugin_manager.catch_all_triggers:
            # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument
            run_before = not raw_hook.threaded
            if not add_hook(raw_hook,
                            Event(hook=raw_hook, base_event=event),
                            _run_before=run_before):
                # The hook has an action of Action.HALT* so stop adding new tasks
                break

        if event.irc_command in self.plugin_manager.raw_triggers:
            for raw_hook in self.plugin_manager.raw_triggers[
                    event.irc_command]:
                if not add_hook(raw_hook, Event(hook=raw_hook,
                                                base_event=event)):
                    # The hook has an action of Action.HALT* so stop adding new tasks
                    break

        # Event hooks
        if event.type in self.plugin_manager.event_type_hooks:
            for event_hook in self.plugin_manager.event_type_hooks[event.type]:
                if not add_hook(event_hook,
                                Event(hook=event_hook, base_event=event)):
                    # The hook has an action of Action.HALT* so stop adding new tasks
                    break

        matched_command = False

        if event.type is EventType.message:
            # Commands
            cmd_match = get_cmd_regex(event).match(event.content)

            if cmd_match:
                command_prefix = event.conn.config.get('command_prefix', '.')
                prefix = cmd_match.group('prefix') or command_prefix[0]
                command = cmd_match.group('command').lower()
                text = cmd_match.group('text').strip()
                cmd_event = partial(CommandEvent,
                                    text=text,
                                    triggered_command=command,
                                    base_event=event,
                                    cmd_prefix=prefix)
                if command in self.plugin_manager.commands:
                    command_hook = self.plugin_manager.commands[command]
                    command_event = cmd_event(hook=command_hook)
                    add_hook(command_hook, command_event)
                    matched_command = True
                else:
                    potential_matches = []
                    for potential_match, plugin in self.plugin_manager.commands.items(
                    ):
                        if potential_match.startswith(command):
                            potential_matches.append((potential_match, plugin))

                    if potential_matches:
                        matched_command = True
                        if len(potential_matches) == 1:
                            command_hook = potential_matches[0][1]
                            command_event = cmd_event(hook=command_hook)
                            add_hook(command_hook, command_event)
                        else:
                            commands = sorted(
                                command
                                for command, plugin in potential_matches)
                            txt_list = formatting.get_text_list(commands)
                            event.notice(
                                "Possible matches: {}".format(txt_list))

        if event.type in (EventType.message, EventType.action):
            # Regex hooks
            regex_matched = False
            for regex, regex_hook in self.plugin_manager.regex_hooks:
                if not regex_hook.run_on_cmd and matched_command:
                    continue

                if regex_hook.only_no_match and regex_matched:
                    continue

                regex_match = regex.search(event.content)
                if regex_match:
                    regex_matched = True
                    regex_event = RegexEvent(hook=regex_hook,
                                             match=regex_match,
                                             base_event=event)
                    if not add_hook(regex_hook, regex_event):
                        # The hook has an action of Action.HALT* so stop adding new tasks
                        break

        # Run the tasks
        await asyncio.gather(*run_before_tasks, loop=self.loop)
        await asyncio.gather(*tasks, loop=self.loop)
예제 #30
0
파일: bot.py 프로젝트: qCzar/ComicBot
    def process(self, event):
        """
        :type event: Event
        """
        run_before_tasks = []
        tasks = []
        command_prefix = event.conn.config.get('command_prefix', '.')

        # Raw IRC hook
        for raw_hook in self.plugin_manager.catch_all_triggers:
            # run catch-all coroutine hooks before all others - TODO: Make this a plugin argument
            if not raw_hook.threaded:
                run_before_tasks.append(
                    self.plugin_manager.launch(
                        raw_hook, Event(hook=raw_hook, base_event=event)))
            else:
                tasks.append(
                    self.plugin_manager.launch(
                        raw_hook, Event(hook=raw_hook, base_event=event)))
        if event.irc_command in self.plugin_manager.raw_triggers:
            for raw_hook in self.plugin_manager.raw_triggers[
                    event.irc_command]:
                tasks.append(
                    self.plugin_manager.launch(
                        raw_hook, Event(hook=raw_hook, base_event=event)))

        # Event hooks
        if event.type in self.plugin_manager.event_type_hooks:
            for event_hook in self.plugin_manager.event_type_hooks[event.type]:
                tasks.append(
                    self.plugin_manager.launch(
                        event_hook, Event(hook=event_hook, base_event=event)))

        if event.type is EventType.message:
            # Commands
            if event.chan.lower() == event.nick.lower(
            ):  # private message, no command prefix
                command_re = r'(?i)^(?:[{}]?|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'.format(
                    command_prefix, event.conn.nick)
            else:
                command_re = r'(?i)^(?:[{}]|{}[,;:]+\s+)(\w+)(?:$|\s+)(.*)'.format(
                    command_prefix, event.conn.nick)

            cmd_match = re.match(command_re, event.content)

            if cmd_match:
                command = cmd_match.group(1).lower()
                if command in self.plugin_manager.commands:
                    command_hook = self.plugin_manager.commands[command]
                    command_event = CommandEvent(
                        hook=command_hook,
                        text=cmd_match.group(2).strip(),
                        triggered_command=command,
                        base_event=event)
                    tasks.append(
                        self.plugin_manager.launch(command_hook,
                                                   command_event))
                else:
                    potential_matches = []
                    for potential_match, plugin in self.plugin_manager.commands.items(
                    ):
                        if potential_match.startswith(command):
                            potential_matches.append((potential_match, plugin))
                    if potential_matches:
                        if len(potential_matches) == 1:
                            command_hook = potential_matches[0][1]
                            command_event = CommandEvent(
                                hook=command_hook,
                                text=cmd_match.group(2).strip(),
                                triggered_command=command,
                                base_event=event)
                            tasks.append(
                                self.plugin_manager.launch(
                                    command_hook, command_event))
                        else:
                            event.notice("Possible matches: {}".format(
                                formatting.get_text_list([
                                    command
                                    for command, plugin in potential_matches
                                ])))

            # Regex hooks
            for regex, regex_hook in self.plugin_manager.regex_hooks:
                if not regex_hook.run_on_cmd and cmd_match:
                    pass
                else:
                    regex_match = regex.search(event.content)
                    if regex_match:
                        regex_event = RegexEvent(hook=regex_hook,
                                                 match=regex_match,
                                                 base_event=event)
                        tasks.append(
                            self.plugin_manager.launch(regex_hook,
                                                       regex_event))

        # Run the tasks
        yield from asyncio.gather(*run_before_tasks, loop=self.loop)
        yield from asyncio.gather(*tasks, loop=self.loop)