Пример #1
0
 def test_generate_config_if_none_exist(self):
     cdf_backup = Loader.create_default_config
     Loader.create_default_config = mock.Mock(
         return_value=os.path.abspath("tests/configs/minimal.yaml"))
     Loader.load_config_file(["file_which_does_not_exist"])
     self.assertTrue(Loader.create_default_config.called)
     Loader.create_default_config = cdf_backup
Пример #2
0
 def test_generate_config_if_none_exist(self):
     cdf_backup = Loader.create_default_config
     Loader.create_default_config = mock.Mock(
         return_value=os.path.abspath("tests/configs/minimal.yaml"))
     Loader.load_config_file(["file_which_does_not_exist"])
     self.assertTrue(Loader.create_default_config.called)
     Loader.create_default_config = cdf_backup
Пример #3
0
 def test_load_non_existant_config_file(self):
     cdf_backup = Loader.create_default_config
     Loader.create_default_config = mock.Mock(
         return_value=os.path.abspath("/tmp/my_nonexistant_config"))
     with mock.patch("sys.exit") as mock_sysexit:
         Loader.load_config_file(["file_which_does_not_exist"])
         self.assertTrue(Loader.create_default_config.called)
         self.assertTrue(mock_sysexit.called)
     Loader.create_default_config = cdf_backup
Пример #4
0
 def test_load_non_existant_config_file(self):
     cdf_backup = Loader.create_default_config
     Loader.create_default_config = mock.Mock(
         return_value=os.path.abspath("/tmp/my_nonexistant_config"))
     with mock.patch('sys.exit') as mock_sysexit:
         Loader.load_config_file(["file_which_does_not_exist"])
         self.assertTrue(Loader.create_default_config.called)
         self.assertTrue(mock_sysexit.called)
     Loader.create_default_config = cdf_backup
Пример #5
0
 def test_yaml_load_exploit(self):
     with mock.patch("sys.exit"):
         config = Loader.load_config_file(
             [os.path.abspath("tests/configs/include_exploit.yaml")])
         self.assertIsNone(config)
         # If the command in exploit.yaml is echoed it will return 0
         self.assertNotEqual(config, 0)
Пример #6
0
 def test_yaml_load_exploit(self):
     with mock.patch('sys.exit'):
         config = Loader.load_config_file(
             [os.path.abspath("tests/configs/include_exploit.yaml")])
         self.assertIsNone(config)
         # If the command in exploit.yaml is echoed it will return 0
         self.assertNotEqual(config, 0)
Пример #7
0
 async def reload(self):
     """Reload opsdroid."""
     await self.unload()
     self.config = Loader.load_config_file([
         "configuration.yaml", DEFAULT_CONFIG_PATH,
         "/etc/opsdroid/configuration.yaml"
     ])
     self.load()
Пример #8
0
 async def reload(self):
     """Reload opsdroid."""
     await self.unload()
     self.config = Loader.load_config_file([
         "configuration.yaml",
         DEFAULT_CONFIG_PATH,
         "/etc/opsdroid/configuration.yaml"
     ])
     self.load()
Пример #9
0
 def test_load_minimal_config_file_2(self):
     opsdroid, loader = self.setup()
     loader._install_module = mock.MagicMock()
     loader.import_module = mock.MagicMock()
     config = Loader.load_config_file(
         [os.path.abspath("tests/configs/minimal_2.yaml")])
     modules = loader.load_modules_from_config(config)
     self.assertIsNotNone(modules["connectors"])
     self.assertIsNone(modules["databases"])
     self.assertIsNotNone(modules["skills"])
     self.assertIsNotNone(config)
Пример #10
0
 def test_load_minimal_config_file_2(self):
     opsdroid, loader = self.setup()
     loader._install_module = mock.MagicMock()
     loader.import_module = mock.MagicMock()
     config = Loader.load_config_file(
         [os.path.abspath("tests/configs/minimal_2.yaml")])
     modules = loader.load_modules_from_config(config)
     self.assertIsNotNone(modules["connectors"])
     self.assertIsNone(modules["databases"])
     self.assertIsNotNone(modules["skills"])
     self.assertIsNotNone(config)
Пример #11
0
def start():
    """Start the opsdroid bot."""
    check_dependencies()

    config = Loader.load_config_file([
        "configuration.yaml", DEFAULT_CONFIG_PATH,
        "/etc/opsdroid/configuration.yaml"
    ])
    configure_lang(config)
    configure_logging(config)
    welcome_message(config)

    with OpsDroid(config=config) as opsdroid:
        opsdroid.run()
Пример #12
0
def main():
    """Opsdroid is a chat bot framework written in Python.

    It is designed to be extendable, scalable and simple.
    See https://opsdroid.github.io/ for more information.
    """
    check_dependencies()

    config = Loader.load_config_file([
        "configuration.yaml", DEFAULT_CONFIG_PATH,
        "/etc/opsdroid/configuration.yaml"
    ])
    configure_lang(config)
    configure_logging(config)
    welcome_message(config)

    with OpsDroid(config=config) as opsdroid:
        opsdroid.load()
        opsdroid.run()
Пример #13
0
def main():
    """Opsdroid is a chat bot framework written in Python.

    It is designed to be extendable, scalable and simple.
    See https://opsdroid.github.io/ for more information.
    """
    check_dependencies()

    config = Loader.load_config_file([
        "configuration.yaml",
        DEFAULT_CONFIG_PATH,
        "/etc/opsdroid/configuration.yaml"
        ])
    configure_lang(config)
    configure_logging(config)
    welcome_message(config)

    with OpsDroid(config=config) as opsdroid:
        opsdroid.load()
        opsdroid.run()
Пример #14
0
 def test_load_broken_config_file(self):
     with mock.patch("sys.exit") as patched_sysexit:
         Loader.load_config_file(
             [os.path.abspath("tests/configs/broken.yaml")])
         self.assertTrue(patched_sysexit.called)
Пример #15
0
class OpsDroid():
    """Root object for opsdroid."""

    # pylint: disable=too-many-instance-attributes
    # All are reasonable in this case.

    instances = []

    def __init__(self):
        """Start opsdroid."""
        self.bot_name = 'opsdroid'
        self.sys_status = 0
        self.connectors = []
        self.connector_tasks = []
        self.eventloop = asyncio.get_event_loop()
        for sig in (signal.SIGINT, signal.SIGTERM):
            self.eventloop.add_signal_handler(sig, self.call_stop)
        self.skills = []
        self.memory = Memory()
        self.loader = Loader(self)
        self.config = {}
        self.stats = {
            "messages_parsed": 0,
            "webhooks_called": 0,
            "total_response_time": 0,
            "total_responses": 0,
        }
        self.web_server = None
        self.should_restart = False
        self.stored_path = []

    def __enter__(self):
        """Add self to existing instances."""
        self.stored_path = copy.copy(sys.path)
        if not self.__class__.instances:
            self.__class__.instances.append(weakref.proxy(self))
        else:
            self.critical("opsdroid has already been started", 1)
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Remove self from existing instances."""
        sys.path = self.stored_path
        self.__class__.instances = []
        asyncio.set_event_loop(asyncio.new_event_loop())

    @property
    def default_connector(self):
        """Return the default connector."""
        default_connector = None
        for connector in self.connectors:
            if "default" in connector.config and connector.config["default"]:
                default_connector = connector
                break
        if default_connector is None:
            default_connector = self.connectors[0]
        return default_connector

    def exit(self):
        """Exit application."""
        _LOGGER.info("Exiting application with return code " +
                     str(self.sys_status))
        sys.exit(self.sys_status)

    def critical(self, error, code):
        """Exit due to unrecoverable error."""
        self.sys_status = code
        _LOGGER.critical(error)
        print("Error:", error)
        self.exit()

    def restart(self):
        """Restart opsdroid."""
        self.should_restart = True
        self.stop()

    def call_stop(self):
        """Signal handler to call disconnect and stop."""
        future = asyncio.ensure_future(self.disconnect())
        future.add_done_callback(self.stop)
        return future

    async def disconnect(self):
        """Disconnect all the connectors."""
        for connector in self.connectors:
            await connector.disconnect(self)

    def stop(self, future=None):
        """Stop the event loop."""
        pending = asyncio.Task.all_tasks()
        for task in pending:
            task.cancel()
        self.eventloop.stop()
        print('')  # Prints a character return for return to shell
        _LOGGER.info("Keyboard interrupt, exiting.")

    def load(self):
        """Load configuration."""
        self.config = self.loader.load_config_file([
            "configuration.yaml",
            DEFAULT_CONFIG_PATH,
            "/etc/opsdroid/configuration.yaml"
            ])

    def start_loop(self):
        """Start the event loop."""
        connectors, databases, skills = \
            self.loader.load_modules_from_config(self.config)
        _LOGGER.debug("Loaded %i skills", len(skills))
        if databases is not None:
            self.start_databases(databases)
        self.setup_skills(skills)
        self.start_connector_tasks(connectors)
        self.eventloop.create_task(parse_crontab(self))
        self.web_server.start()
        try:
            pending = asyncio.Task.all_tasks()
            self.eventloop.run_until_complete(asyncio.gather(*pending))
        except RuntimeError as error:
            if str(error) != 'Event loop is closed':
                raise error
        finally:
            self.eventloop.close()

    def setup_skills(self, skills):
        """Call the setup function on the passed in skills."""
        for skill in skills:
            try:
                skill["module"].setup(self)
            except AttributeError:
                pass

    def start_connector_tasks(self, connectors):
        """Start the connectors."""
        for connector_module in connectors:
            for _, cls in connector_module["module"].__dict__.items():
                if isinstance(cls, type) and \
                   issubclass(cls, Connector) and\
                   cls is not Connector:
                    connector = cls(connector_module["config"])
                    self.connectors.append(connector)

        if connectors:
            for connector in self.connectors:
                self.eventloop.run_until_complete(connector.connect(self))
            for connector in self.connectors:
                task = self.eventloop.create_task(connector.listen(self))
                self.connector_tasks.append(task)
        else:
            self.critical("All connectors failed to load", 1)

    def start_databases(self, databases):
        """Start the databases."""
        if not databases:
            _LOGGER.debug(databases)
            _LOGGER.warning("All databases failed to load")
        for database_module in databases:
            for name, cls in database_module["module"].__dict__.items():
                if isinstance(cls, type) and \
                   issubclass(cls, Database) and \
                   cls is not Database:
                    _LOGGER.debug("Adding database: %s", name)
                    database = cls(database_module["config"])
                    self.memory.databases.append(database)
                    self.eventloop.run_until_complete(database.connect(self))

    async def parse(self, message):
        """Parse a string against all skills."""
        self.stats["messages_parsed"] = self.stats["messages_parsed"] + 1
        tasks = []
        if message.text.strip() != "":
            _LOGGER.debug("Parsing input: %s", message.text)

            tasks.append(
                self.eventloop.create_task(parse_regex(self, message)))
            tasks.append(
                self.eventloop.create_task(parse_always(self, message)))

            if "parsers" in self.config:
                _LOGGER.debug("Processing parsers...")
                parsers = self.config["parsers"]

                dialogflow = [p for p in parsers if p["name"] == "dialogflow"
                              or p["name"] == "apiai"]

                # Show deprecation message but  parse message
                # Once it stops working remove this bit
                apiai = [p for p in parsers if p["name"] == "apiai"]
                if apiai:
                    _LOGGER.warning("Api.ai is now called Dialogflow. This "
                                    "parser will stop working in the future "
                                    "please swap: 'name: apiai' for "
                                    "'name: dialogflow' in configuration.yaml")

                _LOGGER.debug("Checking dialogflow...")
                if len(dialogflow) == 1 and \
                        ("enabled" not in dialogflow[0] or
                         dialogflow[0]["enabled"] is not False):
                    _LOGGER.debug("Parsing with Dialogflow.")
                    tasks.append(
                        self.eventloop.create_task(
                            parse_dialogflow(self, message, dialogflow[0])))

                luisai = [p for p in parsers if p["name"] == "luisai"]
                _LOGGER.debug("Checking luisai...")
                if len(luisai) == 1 and \
                        ("enabled" not in luisai[0] or
                         luisai[0]["enabled"] is not False):
                    _LOGGER.debug("Parsing with luisai.")
                    tasks.append(
                        self.eventloop.create_task(
                            parse_luisai(self, message, luisai[0])))

                witai = [p for p in parsers if p["name"] == "witai"]
                _LOGGER.debug("Checking wit.ai...")
                if len(witai) == 1 and \
                        ("enabled" not in witai[0] or
                         witai[0]["enabled"] is not False):
                    _LOGGER.debug("Parsing with witai.")
                    tasks.append(
                        self.eventloop.create_task(
                            parse_witai(self, message, witai[0])))

        return tasks
Пример #16
0
 def test_load_broken_config_file(self):
     with mock.patch('sys.exit') as patched_sysexit:
         Loader.load_config_file(
             [os.path.abspath("tests/configs/broken.yaml")])
         self.assertTrue(patched_sysexit.called)