Example #1
0
class Chitanda:

    commands = {}

    def __init__(self):
        self.irc_listeners = {}
        self.discord_listener = None
        self.message_handlers = []
        self.response_handlers = []
        if config['webserver']['enable']:
            self.web_application = web.Application()

    def start(self):
        load_commands(self)
        if hasattr(self, 'web_application'):
            self.webserver = self._start_webserver()

        self.connect()

    def _start_webserver(self):
        try:
            return asyncio.ensure_future(
                asyncio.get_event_loop().create_server(
                    self.web_application.make_handler(),
                    port=int(config['webserver']['port']),
                ))
        except ValueError:
            logging.critical('Invalid port value for webserver.')
            sys.exit(1)

    def connect(self):
        logger.info('Initiating connection to listeners.')
        if config['irc_servers']:
            logger.info('IRC Servers found, connecting...')
            self._connect_irc()
        if config['discord_token']:
            logger.info('Discord token found, connecting...')
            self._connect_discord()

    def _connect_irc(self):
        for hostname, server in config['irc_servers'].items():
            logger.info(f'Connecting to IRC server: {hostname}.')
            self.irc_listeners[hostname] = IRCListener(self,
                                                       server['nickname'],
                                                       hostname)
            asyncio.ensure_future(self.irc_listeners[hostname].connect(
                hostname,
                server['port'],
                tls=server['tls'],
                tls_verify=server['tls_verify'],
            ))

    def _connect_discord(self):
        self.discord_listener = DiscordListener(self)
        self.discord_listener.run(config['discord_token'])

    async def handle_message(self, message):
        logger.debug(f'New message in {message.target} on {message.listener} '
                     f'from {message.author}: {message.contents}')
        try:
            for handler in self.message_handlers:
                await self.handle_response(handler(message), source=message)

            await self.dispatch_command(message)
        except BotError as e:
            logger.info(f'Error triggered by {message.author}: {e}.')
            await message.listener.message(message.target, f'Error: {e}')

    async def dispatch_command(self, message):
        try:
            response = message.call_command()
            if response:
                await self.handle_response(response, source=message)
        except NoCommandFound:
            pass

    async def handle_response(self, response, source):
        logger.debug(f'Response received of type: {type(response)}.')
        if isinstance(response, AsyncGeneratorType):
            async for resp in response:
                await self._handle_response_message(resp, source)
            return

        response = await response

        if isinstance(response, GeneratorType):
            for resp in response:
                await self._handle_response_message(resp, source)
        elif response:
            await self._handle_response_message(response, source)

    async def _handle_response_message(self, response, source):
        response = Response.wrap(response, source)
        await self.call_response_handlers(response)
        await response.listener.message(
            target=response.target,
            message=response.contents,
            **response.kwargs,
        )

    async def call_response_handlers(self, response):
        for handler in self.response_handlers:
            await handler(response)
Example #2
0
class Chitanda:

    commands = {}

    def __init__(self):
        self.irc_listeners = {}
        self.discord_listener = None
        self.message_handlers = []
        self.response_handlers = []
        if config['webserver']['enable']:
            self.web_application = web.Application()

    def start(self):
        load_commands(self)
        if self.web_application():
            self.webserver = self._start_webserver()

        self.connect()

    def _start_webserver(self):
        try:
            return asyncio.ensure_future(
                asyncio.get_event_loop().create_server(
                    self.web_application.make_handler(),
                    port=int(config['webserver']['port']),
                )
            )
        except ValueError:
            logging.critical('Invalid port value for webserver.')
            sys.exit(1)

    def connect(self):
        logger.info('Initiating connection to listeners.')
        if config['irc_servers']:
            logger.info('IRC Servers found, connecting...')
            self._connect_irc()
        if config['discord_token']:
            logger.info('Discord token found, connecting...')
            self._connect_discord()

    def _connect_irc(self):
        for hostname, server in config['irc_servers'].items():
            logger.info(f'Connecting to IRC server: {hostname}.')
            self.irc_listeners[hostname] = IRCListener(
                self, server['nickname'], hostname
            )
            asyncio.ensure_future(
                self.irc_listeners[hostname].connect(
                    hostname,
                    server['port'],
                    tls=server['tls'],
                    tls_verify=server['tls_verify'],
                )
            )

    def _connect_discord(self):
        self.discord_listener = DiscordListener(self)
        self.discord_listener.run(config['discord_token'])

    async def handle_message(self, listener, target, author, message, private):
        logger.debug(
            f'New message in {target} on {listener} from {author}: {message}'
        )
        for handler in self.message_handlers:
            response = handler(listener, target, author, message, private)
            await self.handle_response(listener, target, response)

        await self.dispatch_command(listener, target, author, message, private)

    async def dispatch_command(
        self, listener, target, author, message, private
    ):
        try:
            trigger, command, message = self._parse_command(message)
            logger.info(f'Command triggered: {trigger}.')

            response = command.call(
                bot=self,
                listener=listener,
                target=target,
                author=author,
                message=message,
                private=private,
            )

            if response:
                await self.handle_response(listener, target, response)
        except NoCommandFound:
            pass
        except BotError as e:
            logger.info(f'Error triggered by {author}: {e}.')
            await listener.message(target, f'Error: {e}')

    def _parse_command(self, message):
        try:
            if message.startswith(config['trigger_character']):
                split_message = message[1:].split(' ', 1)
                trigger, message = self._resolve_alias(*split_message)
                return trigger, self.commands[trigger], message
        except (IndexError, KeyError):
            pass
        raise NoCommandFound

    def _resolve_alias(self, trigger, message=''):
        try:
            return (f'{config["aliases"][trigger]} {message}').split(' ', 1)
        except KeyError:
            return trigger, message

    async def handle_response(self, listener, target, response):
        logger.debug(f'Response received of type: {type(response)}.')

        if isinstance(response, AsyncGeneratorType):
            async for resp in response:
                await self._handle_response_message(listener, target, resp)
            return

        response = await response

        if isinstance(response, GeneratorType):
            for resp in response:
                await self._handle_response_message(listener, target, resp)
        elif response:
            await self._handle_response_message(listener, target, response)

    async def _handle_response_message(self, listener, target, response):
        pr = self._pack_response(response, target)
        await self.call_response_handlers(listener, target, pr['message'])
        await listener.message(**pr)

    def _pack_response(self, response, target):
        if isinstance(response, dict):
            return response
        return {'target': target, 'message': str(response)}

    async def call_response_handlers(self, listener, target, response):
        for handler in self.response_handlers:
            handler(listener, target, response)