class TestPluginManager:
    def __init__(self):
        self.plugin_path = path / 'tests' / 'test_plugins'
        self.good_plugin = self.plugin_path / 'test_plugin_2.py'
        self.good_plugin_package = self.plugin_path / 'test_plugin_package'
        self.bad_plugin = self.plugin_path / 'bad_plugin'
        self.bad_path = self.plugin_path / 'bad_path.py'
        self.dependent_plugins = self.plugin_path / "dependent_plugins"
        self.plugin_manager = PluginManager(None)
        self.loop = None

    def setup(self):
        self.plugin_manager = PluginManager(None)
        self.loop = asyncio.new_event_loop()

    def test_bad_paths(self):
        assert_raises(FileNotFoundError,
                      self.plugin_manager._load_module, self.bad_path)

    def test_load_good_plugins(self):
        self.plugin_manager.load_plugin(self.good_plugin)
        self.plugin_manager.load_plugin(self.good_plugin_package)
        self.plugin_manager.resolve_dependencies()
        assert_in("test_plugin_2",
                  self.plugin_manager.list_plugins().keys())
        assert_in("test_plugin_1",
                  self.plugin_manager.list_plugins().keys())

    def test_load_bad_plugin(self):
        with assert_raises(SyntaxError):
            self.plugin_manager.load_plugin(self.bad_plugin)
            self.plugin_manager.resolve_dependencies()

    def test_load_plugin_dir(self):
        self.plugin_manager.load_from_path(self.plugin_path)
        self.plugin_manager.resolve_dependencies()
        assert_in("test_plugin_2",
                  self.plugin_manager.list_plugins())
        assert_in("test_plugin_1",
                  self.plugin_manager.list_plugins())
        assert_in("bad_plugin",
                  self.plugin_manager.failed)

    def test_the_do_method(self):
        self.plugin_manager.load_plugin(self.good_plugin)
        self.plugin_manager.load_plugin(self.good_plugin_package)
        self.plugin_manager.resolve_dependencies()
        result = self.loop.run_until_complete(
            self.plugin_manager.do("chat_sent", b""))
        assert_equals(result, True)

    def test_dependency_check(self):
        with assert_raises(ImportError):
            self.plugin_manager.load_plugin(self.dependent_plugins / 'b.py')
            self.plugin_manager.resolve_dependencies()

    def test_dependency_resolution(self):
        self.plugin_manager.load_plugins([
            self.dependent_plugins / 'a.py',
            self.dependent_plugins / 'b.py'
        ])

        self.plugin_manager.resolve_dependencies()

    def test_circular_dependency_error(self):
        with assert_raises(ImportError):
            self.plugin_manager.load_plugin(
                self.dependent_plugins / 'circular.py')
            self.plugin_manager.resolve_dependencies()

    def test_empty_overrides(self):
        self.plugin_manager.resolve_dependencies()
        owners = self.loop.run_until_complete(
            self.plugin_manager.get_overrides())
        assert_equal(owners, set())

    def test_override(self):
        self.plugin_manager.load_plugin(
            self.plugin_path / 'test_plugin_package')
        self.plugin_manager.load_plugin(self.plugin_path / 'test_plugin_2.py')
        self.plugin_manager.resolve_dependencies()
        self.plugin_manager.activate_all()
        overrides = self.loop.run_until_complete(
            self.plugin_manager.get_overrides())
        assert_equal(overrides, {'on_chat_sent'})

    def test_override_caching(self):
        self.plugin_manager.load_plugin(self.plugin_path / 'test_plugin_2.py')
        assert_equal(self.plugin_manager._overrides, set())
        assert_equal(self.plugin_manager._override_cache, set())
        self.plugin_manager.activate_all()
        self.loop.run_until_complete(self.plugin_manager.get_overrides())
        assert_is(self.plugin_manager._override_cache,
                  self.plugin_manager._activated_plugins)
        cache = self.plugin_manager._override_cache
        self.loop.run_until_complete(self.plugin_manager.get_overrides())
        assert_is(self.plugin_manager._override_cache, cache)


    def test_activate(self):
        self.plugin_manager.load_plugin(
            self.plugin_path / 'test_plugin_package')
        self.plugin_manager.load_plugin(self.plugin_path / 'test_plugin_2.py')
        self.plugin_manager.resolve_dependencies()
        self.plugin_manager.activate_all()
        assert_equal({x.name for x in self.plugin_manager._activated_plugins},
                     {'test_plugin_1', 'test_plugin_2'})
Beispiel #2
0
class TestPluginManager:
    def __init__(self):
        self.plugin_path = path / 'tests' / 'test_plugins'
        self.good_plugin = self.plugin_path / 'test_plugin_2.py'
        self.good_plugin_package = self.plugin_path / 'test_plugin_package'
        self.bad_plugin = self.plugin_path / 'bad_plugin'
        self.bad_path = self.plugin_path / 'bad_path.py'
        self.dependent_plugins = self.plugin_path / "dependent_plugins"
        self.plugin_manager = PluginManager(None)
        self.loop = None

    def setup(self):
        self.plugin_manager = PluginManager(None)
        self.loop = asyncio.new_event_loop()

    def test_bad_paths(self):
        assert_raises(FileNotFoundError, self.plugin_manager._load_module,
                      self.bad_path)

    def test_load_good_plugins(self):
        self.plugin_manager.load_plugin(self.good_plugin)
        self.plugin_manager.load_plugin(self.good_plugin_package)
        self.plugin_manager.resolve_dependencies()
        assert_in("test_plugin_2", self.plugin_manager.list_plugins().keys())
        assert_in("test_plugin_1", self.plugin_manager.list_plugins().keys())

    def test_load_bad_plugin(self):
        with assert_raises(SyntaxError):
            self.plugin_manager.load_plugin(self.bad_plugin)
            self.plugin_manager.resolve_dependencies()

    def test_load_plugin_dir(self):
        self.plugin_manager.load_from_path(self.plugin_path)
        self.plugin_manager.resolve_dependencies()
        assert_in("test_plugin_2", self.plugin_manager.list_plugins())
        assert_in("test_plugin_1", self.plugin_manager.list_plugins())
        assert_in("bad_plugin", self.plugin_manager.failed)

    def test_the_do_method(self):
        self.plugin_manager.load_plugin(self.good_plugin)
        self.plugin_manager.load_plugin(self.good_plugin_package)
        self.plugin_manager.resolve_dependencies()
        result = self.loop.run_until_complete(
            self.plugin_manager.do("chat_sent", b""))
        assert_equals(result, True)

    def test_dependency_check(self):
        with assert_raises(ImportError):
            self.plugin_manager.load_plugin(self.dependent_plugins / 'b.py')
            self.plugin_manager.resolve_dependencies()

    def test_dependency_resolution(self):
        self.plugin_manager.load_plugins(
            [self.dependent_plugins / 'a.py', self.dependent_plugins / 'b.py'])

        self.plugin_manager.resolve_dependencies()

    def test_circular_dependency_error(self):
        with assert_raises(ImportError):
            self.plugin_manager.load_plugin(self.dependent_plugins /
                                            'circular.py')
            self.plugin_manager.resolve_dependencies()

    def test_empty_overrides(self):
        self.plugin_manager.resolve_dependencies()
        owners = self.loop.run_until_complete(
            self.plugin_manager.get_overrides())
        assert_equal(owners, set())

    def test_override(self):
        self.plugin_manager.load_plugin(self.plugin_path /
                                        'test_plugin_package')
        self.plugin_manager.load_plugin(self.plugin_path / 'test_plugin_2.py')
        self.plugin_manager.resolve_dependencies()
        self.plugin_manager.activate_all()
        overrides = self.loop.run_until_complete(
            self.plugin_manager.get_overrides())
        assert_equal(overrides, {'on_chat_sent'})

    def test_override_caching(self):
        self.plugin_manager.load_plugin(self.plugin_path / 'test_plugin_2.py')
        assert_equal(self.plugin_manager._overrides, set())
        assert_equal(self.plugin_manager._override_cache, set())
        self.plugin_manager.activate_all()
        self.loop.run_until_complete(self.plugin_manager.get_overrides())
        assert_is(self.plugin_manager._override_cache,
                  self.plugin_manager._activated_plugins)
        cache = self.plugin_manager._override_cache
        self.loop.run_until_complete(self.plugin_manager.get_overrides())
        assert_is(self.plugin_manager._override_cache, cache)

    def test_activate(self):
        self.plugin_manager.load_plugin(self.plugin_path /
                                        'test_plugin_package')
        self.plugin_manager.load_plugin(self.plugin_path / 'test_plugin_2.py')
        self.plugin_manager.resolve_dependencies()
        self.plugin_manager.activate_all()
        assert_equal({x.name
                      for x in self.plugin_manager._activated_plugins},
                     {'test_plugin_1', 'test_plugin_2'})
Beispiel #3
0
def test_list_plugins():
    print PluginManager.list_plugins()
Beispiel #4
0
class Bot:
	"""
	Singleton Bot class that acts as the controller for messages received and sent to Telegram.
	This file should be run when starting the bot currently.

	...

	Methods
	-------
	start()
		Starts the bot where it will check for updates received from Telegram and send replies based on Plugins and Messages.

	reload_plugins()
		Reloads all plugins managed in self.plugin_manager, incorporating new changes made

	enable_plugin(plugin_name)
		Enables a plugin with a specific name

	disable_plugin(plugin_name)
		Disables a plugin with a specific name

	plugin_help(plugin_name)
		Returns a string containing the help message from a Plugin's get_help method

	list_plugins()
		Returns a str listing all plugins

	get_updates(last_update)
		Gets message updates from Telegram based on those last received.

	send_message(id, message)
		Sends a message to Telegram.

	send_photo(id, message, file_name)
		Sends a photo to Telegram.
	"""

	def __init__(self, config):
		"""
		Sets up initial bot data and the PluginManager which loads inital plugins.
		...

		Parameters
		----------
		config: Config
			Configuration object containing data found within the bot's configuration file.
		"""

		self.logger = logging.getLogger('bot_log')
		self.directory = config.bot_dir
		self.base_url = "https://api.telegram.org/bot"+config.token+"/"
		self.sleep_interval = config.sleep_interval
		self.username = json.loads(requests.get(self.base_url + "getMe").text)["result"]["username"]
		self.plugin_manager = PluginManager(config, self)

		print(self.plugin_manager.list_commands())
		print(self.plugin_manager.list_listeners())

	def start(self, thread):
		"""
		Receives and sends messages to Telegram forever.
		Responses made by the bot are based on functionality contained within Plugins.
		"""

		last_update = 0

		while not thread.stopped():
			updates = self.get_updates(last_update)

			for update in updates["result"]:
				last_update = update["update_id"]

				if "message" in update:
					message = Message(update["message"])
					self.logger.info(str(last_update)+": "+message.sent_from.username)

					if message.is_command:
						self.logger.info("Command received, processing plugins")
						t = threading.Thread(target = self.plugin_manager.process_plugin, args = (self, message))
						t.setDaemon(True)
						t.start()
					else:
						t = threading.Thread(target = self.plugin_manager.process_message, args = (self, message))
						t.setDaemon(True)
						t.start()
		self.logger.warn("Ending telegram update loop due to thread manually being killed")

	def reload_plugins(self):
		"""
		Reloads all plugins managed in self.plugin_manager, incorporating new changes made
		"""

		self.plugin_manager.reload_plugins(self)
		return True

	def enable_plugin(self, plugin_name):
		"""
		Enables a plugin with a specific name
		"""

		self.logger.info("Attempting to enable plugin with name {}".format(plugin_name))
		return self.plugin_manager.enable_plugin(plugin_name)

	def disable_plugin(self, plugin_name):
		"""
		Disables a plugin with a specific name
		"""

		self.logger.info("Attempting to disable plugin with name {}".format(plugin_name))
		return self.plugin_manager.disable_plugin(plugin_name)

	def plugin_help(self, plugin_name):
		"""
		Returns a string containing the help message from a Plugin's get_help method
		"""

		self.logger.info("Requested help message from plugin with name {}".format(plugin_name))
		return self.plugin_manager.plugin_help(plugin_name)

	def list_plugins(self):
		"""
		Returns a str listing all plugins
		"""

		self.logger.info("Request recieved to list all plugins")
		return self.plugin_manager.list_plugins()

	def get_updates(self, last_update):
		"""
		Gets message updates from Telegram based on those last received.

		...

		Parameters
		----------
		last_update: json
			Data received from telegram dictating new messages that were send/visible to the bot.
		"""

		return json.loads(requests.get(self.base_url + 'getUpdates', params=dict(offset=(last_update+1))).text)

	def send_message(self, id, message):
		"""
		Sends a string message to a specific telegram chatroom containing the designated id.

		...

		Parameters
		----------
		id: str
			The id of the chatroom to send a message to.

		message: str
			The message to be send to a chatroom.
		"""

		self.logger.info("Sending message ({}) to channel with id {}".format(message, id))
		return requests.get(self.base_url + 'sendMessage', params=dict(chat_id=id, text=message))

	def send_photo(self, id, message, file_name):
		"""
		Sends a photo found with the designated filename with an optional caption string message to a chatroom containing the designated id

		...

		Parameters
		----------
		id: str
			The id of the chatroom to send a message to.

		message: optional
			An optional string to send with an image as a caption

		file_name: str
			The filename of the photo to send to a Telegram chatroom
		"""

		files = {'photo': open(file_name, 'rb')}
		data = dict(chat_id=id, caption=message)

		self.logger.info("Sending photo with caption ({}) with filename ({}) to channel with id {}".format(message, file_name, id))
		return requests.get(self.base_url + 'sendPhoto', files=files, data=data)