Beispiel #1
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatched most events to the different plugins.

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)


    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        if hasattr(self, 'shard_id'):
            msg = 'Chat Shard {}/{} restarted'.format(self.shard_id, self.shard_count)
        else:
            msg = 'Mee6 Chat restarted'
        self.stats.event(msg, 'Server count: {}'.format(len(self.servers)))

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set('server:{}:name'.format(server.id), server.name)
            if server.icon:
                await self.db.redis.set('server:{}:icon'.format(server.id), server.icon)

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')

        log.info('Joined {} server : {} !'.format(server.owner.name, server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.sadd('servers', server.id)
        await self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            await self.db.redis.set('server:{}:icon'.format(server.id), server.icon)
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(server.owner.name, server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(message.channel, "shard {}/{}".format(self.shard_id+1,
                                                                              self.shard_count))

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))

    async def on_server_role_create(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_create(server, role))

    async def on_server_role_delete(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_delete(server, role))

    async def on_server_role_update(self, before, after):
        server = None
        for s in self.servers:
            if after.id in map(lambda r:r.id, s.roles):
                server = s
                break

        if server is None:
            return

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_update(before, after))

    async def on_voice_state_update(self, before, after):
        if after is None:
            server = before.server
        elif before is None:
            server = after.server
        else:
            return

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_voice_state_update(before, after))

    async def on_member_ban(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_ban(member))

    async def on_member_unban(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_unban(member))

    async def on_typing(self, channel, user, when):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_typing(channel, user, when))
Beispiel #2
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatches most events to the different plugins.

    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)

    def run(self, *args):
        self.loop.run_until_complete(self.start(*args))

    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        if hasattr(self, 'shard_id'):
            msg = 'Chat Shard {}/{} restarted'.format(self.shard_id,
                                                      self.shard_count)
        else:
            msg = 'Mee6 Chat restarted'
        self.stats.event(msg, 'Server count: {}'.format(len(self.servers)))

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set('server:{}:name'.format(server.id),
                                        server.name)
            if server.icon:
                await self.db.redis.set('server:{}:icon'.format(server.id),
                                        server.icon)

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')

        log.info('Joined {} server : {} !'.format(server.owner.name,
                                                  server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.sadd('servers', server.id)
        await self.db.redis.set('server:{}:name'.format(server.id),
                                server.name)
        if server.icon:
            await self.db.redis.set('server:{}:icon'.format(server.id),
                                    server.icon)
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(server.owner.name,
                                                   server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id + 1, self.shard_count))

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))

    async def on_server_role_create(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_create(server, role))

    async def on_server_role_delete(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_delete(server, role))

    async def on_server_role_update(self, before, after):
        server = None
        for s in self.servers:
            if after.id in map(lambda r: r.id, s.roles):
                server = s
                break

        if server is None:
            return

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_update(before, after))

    async def on_voice_state_update(self, before, after):
        if after is None and before is None:
            return
        elif after is None:
            server = before.server
        elif before is None:
            server = after.server
        else:
            server = after.server

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_voice_state_update(before, after))

    async def on_member_ban(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_ban(member))

    async def on_member_unban(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_unban(member))

    async def on_typing(self, channel, user, when):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_typing(channel, user, when))
Beispiel #3
0
class Mee6(discord.Client):
    def __init__(self, *args, **kwargs):
        discord.Client.__init__(self, *args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.db = Db(self.redis_url)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []

    async def on_ready(self):
        with open('welcome_ascii.txt') as f:
            print(f.read())
        self.add_all_servers()
        discord.utils.create_task(self.heartbeat(5), loop=self.loop)
        discord.utils.create_task(self.update_stats(60), loop=self.loop)
        await self.change_status(game=discord.Game(name='http://mee6.xyz'))

    def add_all_servers(self):
        log.debug('Syncing servers and db')
        self.db.redis.delete('servers')
        for server in self.servers:
            log.debug('Adding server {}\'s id to db'.format(server.id))
            self.db.redis.sadd('servers', server.id)

    @asyncio.coroutine
    def send_message(self, *args, **kwargs):
        dest = args[0]
        if hasattr(args[0], 'server'):
            dest = args[0].server
        if isinstance(args[0], discord.Member):
            dest = args[0]
        log.info('Mee6@{} >> {}'.format(dest.name, args[1].replace('\n', '~')))
        yield from super().send_message(*args, **kwargs)

    @asyncio.coroutine
    def on_server_join(self, server):
        log.info('Joined {} server : {} !'.format(server.owner.name,
                                                  server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        self.db.redis.sadd('servers', server.id)
        self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            self.db.redis.set('server:{}:icon'.format(server.id), server.icon)

    @asyncio.coroutine
    def on_server_remove(self, server):
        log.info('Leaving {} server : {} !'.format(server.owner.name,
                                                   server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        self.db.redis.srem('servers', server.id)

    @asyncio.coroutine
    def heartbeat(self, interval):
        while self.is_logged_in:
            self.db.redis.set('heartbeat', 1, ex=interval)
            yield from asyncio.sleep(0.9 * interval)

    @asyncio.coroutine
    def update_stats(self, interval):
        while self.is_logged_in:
            # Total members and online members
            members = list(self.get_all_members())
            online_members = filter(
                lambda m: m.status is discord.Status.online, members)
            online_members = list(online_members)
            self.db.redis.set('mee6:stats:online_members', len(online_members))
            self.db.redis.set('mee6:stats:members', len(members))

            # Last messages
            for index, timestamp in enumerate(self.last_messages):
                if timestamp + interval < time():
                    self.last_messages.pop(index)
            self.db.redis.set('mee6:stats:last_messages',
                              len(self.last_messages))

            yield from asyncio.sleep(interval)

    @asyncio.coroutine
    def _run_plugin_event(self, plugin, event, *args, **kwargs):
        # A modified coro that is based on Client._run_event
        try:
            yield from getattr(plugin, event)(*args, **kwargs)
        except asyncio.CancelledError:
            pass
        except Exception:
            try:
                yield from self.on_error(event, *args, **kwargs)
            except asyncio.CancelledError:
                pass

    async def on_message(self, message):
        mee6_server_id = "159962941502783488"
        update_channel_id = "160784396679380992"
        if not hasattr(message, 'server'):
            return
        if (message.server.id, message.channel.id) == (mee6_server_id,
                                                       update_channel_id):
            owners = set(server.owner for server in self.servers)
            for owner in owners:
                await self.send_message(owner, message.content)

    def dispatch(self, event, *args, **kwargs):
        # A list of events that are available from the plugins
        plugin_events = ('message', 'message_delete', 'message_edit',
                         'channel_delete', 'channel_create', 'channel_update',
                         'member_join', 'member_update', 'server_update',
                         'server_role_create', 'server_role_delete',
                         'server_role_update', 'voice_state_update',
                         'member_ban', 'member_unban', 'typing')

        log.debug('Dispatching event {}'.format(event))
        method = 'on_' + event
        handler = 'handle_' + event

        # Total number of messages stats update
        if event == 'message':
            self.db.redis.incr('mee6:stats:messages')
            self.last_messages.append(time())
            if hasattr(self, method):
                discord.utils.create_task(self._run_event(method, *args,\
             **kwargs), loop=self.loop)

        if hasattr(self, handler):
            getattr(self, handler)(*args, **kwargs)

        if event in plugin_events:
            server_context = find_server(*args, **kwargs)
            if server_context is None:
                return
            # For each plugin that the server has enabled
            enabled_plugins = self.plugin_manager.get_all(server_context)
            for plugin in enabled_plugins:
                if hasattr(plugin, method):
                    discord.utils.create_task(self._run_plugin_event(\
                    plugin, method, *args, **kwargs), loop=self.loop)
        else:
            if hasattr(self, method):
                discord.utils.create_task(self._run_event(method, *args,\
             **kwargs), loop=self.loop)

    def run(self, token):
        self.token = token
        self.headers['authorization'] = token
        self._is_logged_in.set()
        try:
            self.loop.run_until_complete(self.connect())
        except KeyboardInterrupt:
            self.loop.run_until_complete(self.logout())
            pending = asyncio.Task.all_tasks()
            gathered = asyncio.gather(*pending)
            try:
                gathered.cancel()
                self.loop.run_forever()
                gathered.exception()
            except:
                pass
        finally:
            self.loop.close()
Beispiel #4
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatches most events to the different plugins.

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.sentry_dsn = kwargs.get('sentry_dsn')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)

    def run(self, *args):
        self.loop.run_until_complete(self.start(*args))

    async def ping(self):
        PING_INTERVAL = 3
        key = 'mee6.gateway.{}-{}'.format(self.shard_id,
                                          self.shard_count)
        while True:
            if self.is_logged_in:
                self.stats.check(key, 'OK')
                await self.db.redis.setex(key, PING_INTERVAL + 2, '1')
            else:
                self.stats.check(key, 'Critical')

            await asyncio.sleep(PING_INTERVAL)

    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

        self.loop.create_task(self.ping())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set(
                    'server:{}:name'.format(server.id),
                    server.name
                )
            if server.icon:
                await self.db.redis.set(
                    'server:{}:icon'.format(server.id),
                    server.icon
                )

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')
        await self.db.redis.sadd('servers', server.id)

        log.info('Joined {} server : {} !'.format(
            server.owner.name,
            server.name
        ))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            await self.db.redis.set(
                'server:{}:icon'.format(server.id),
                server.icon
            )
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(
            server.owner.name,
            server.name
        ))
        log.debug('Removing server {}\'s id from the db'.format(
            server.id
        ))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return
        if message.author.__class__ != discord.Member:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id+1, self.shard_count)
                )

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))
Beispiel #5
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatches most events to the different plugins.

    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.sentry_dsn = kwargs.get('sentry_dsn')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)
        self.voice_sessions_ids = dict()

        if self.shard_id is not None:
            self.shard = [self.shard_id, self.shard_count]
        else:
            self.shard = [0, 1]

    def run(self, *args):
        console_coro = self.loop.create_server(make_console(self), '127.0.0.1',
                                               8000)
        self.loop.run_until_complete(console_coro)
        self.loop.run_until_complete(self.start(*args))

    async def console(reader, writer):
        loop = asyncio.get_event_loop()
        data = await reader.read(100)
        message = data.decode()
        result = eval(message)
        writer.write(result)
        await writer.drain()
        writer.close()

    def send_monitoring_message(self, msg):
        self.loop.create_task(self._send_monitoring_message(msg))

    async def _send_monitoring_message(self, msg):
        url = os.getenv('MONITORING_WH')
        if not url:
            return
        headers = {}
        headers['Content-Type'] = 'application/json'
        data = json.dumps({'content': msg})
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=data) as d:
                pass

    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set('server:{}:name'.format(server.id),
                                        server.name)
            if server.icon:
                await self.db.redis.set('server:{}:icon'.format(server.id),
                                        server.icon)

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')
        await self.db.redis.sadd('servers', server.id)

        log.info('Joined {} server : {} !'.format(server.owner.name,
                                                  server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.set('server:{}:name'.format(server.id),
                                server.name)
        if server.icon:
            await self.db.redis.set('server:{}:icon'.format(server.id),
                                    server.icon)
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(server.owner.name,
                                                   server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return

        wh_key = 'channel.{}.last_message_whid'.format(message.channel.id)

        if message.author.__class__ != discord.Member:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id, self.shard_count))

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))
Beispiel #6
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatches most events to the different plugins.

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.sentry_dsn = kwargs.get('sentry_dsn')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)

    def run(self, *args):
        self.loop.run_until_complete(self.start(*args))

    async def ping(self):
        PING_INTERVAL = 3
        key = 'mee6.gateway.{}-{}'.format(self.shard_id,
                                          self.shard_count)
        while True:
            if self.is_logged_in:
                self.stats.check(key, 'OK')
                await self.db.redis.setex(key, PING_INTERVAL + 2, '1')
            else:
                self.stats.check(key, 'Critical')

            await asyncio.sleep(PING_INTERVAL)

    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

        self.loop.create_task(self.ping())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set(
                    'server:{}:name'.format(server.id),
                    server.name
                )
            if server.icon:
                await self.db.redis.set(
                    'server:{}:icon'.format(server.id),
                    server.icon
                )

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')
        await self.db.redis.sadd('servers', server.id)

        log.info('Joined {} server : {} !'.format(
            server.owner.name,
            server.name
        ))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            await self.db.redis.set(
                'server:{}:icon'.format(server.id),
                server.icon
            )
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(
            server.owner.name,
            server.name
        ))
        log.debug('Removing server {}\'s id from the db'.format(
            server.id
        ))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return
        if message.author.__class__ != discord.Member:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id, self.shard_count)
                )

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))
Beispiel #7
0
class ChatBot(discord.Client):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.__name__ = APP_NAME
        self.__version__ = APP_VERSION
        self.__author__ = APP_AUTHOR
        self.__copyright__ = "Copyright (c){} {}".format(
            '2016-2020', self.__author__)

    def run(self, *args):
        self.loop.run_until_complete(self.start(*args))

    async def on_ready(self):
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, channel, *args, **kwargs):
        return await channel.send(*args, **kwargs)

    async def on_message(self, message):
        if isinstance(message.channel, discord.abc.PrivateChannel):
            return

        server = message.guild

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if isinstance(before.channel, discord.abc.PrivateChannel):
            return

        server = after.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if isinstance(message.channel, discord.abc.PrivateChannel):
            return

        server = message.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if isinstance(channel, discord.abc.PrivateChannel):
            return

        server = channel.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if isinstance(before, discord.abc.PrivateChannel):
            return

        server = after.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if isinstance(channel, discord.abc.PrivateChannel):
            return

        server = channel.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))

    async def on_server_role_create(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_create(server, role))

    async def on_server_role_delete(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_delete(server, role))

    async def on_server_role_update(self, before, after):
        server = None
        for s in self.guilds:
            if after.id in map(lambda r: r.id, s.roles):
                server = s
                break

        if server is None:
            return

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_update(before, after))

    async def on_voice_state_update(self, before, after):
        if after is None and before is None:
            return
        elif after is None:
            server = before.guild
        elif before is None:
            server = after.guild
        else:
            server = after.guild

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_voice_state_update(before, after))

    async def on_member_ban(self, member):
        server = member.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_ban(member))

    async def on_member_unban(self, member):
        server = member.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_unban(member))

    async def on_typing(self, channel, user, when):
        if isinstance(channel, discord.abc.PrivateChannel):
            return

        server = channel.guild
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_typing(channel, user, when))
Beispiel #8
0
class Mee6(discord.Client):
    def __init__(self, *args, **kwargs):
        discord.Client.__init__(self, *args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.db = Db(self.redis_url)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []

    async def on_ready(self):
        with open('welcome_ascii.txt') as f:
            print(f.read())
        self.add_all_servers()
        discord.utils.create_task(self.heartbeat(5), loop=self.loop)
        discord.utils.create_task(self.update_stats(60), loop=self.loop)
        await self.change_status(game=discord.Game(name='http://mee6.xyz'))

    def add_all_servers(self):
        log.debug('Syncing servers and db')
        self.db.redis.delete('servers')
        for server in self.servers:
            log.debug('Adding server {}\'s id to db'.format(server.id))
            self.db.redis.sadd('servers', server.id)

    @asyncio.coroutine
    def send_message(self, *args, **kwargs):
        dest = args[0]
        if hasattr(args[0], 'server'):
            dest = args[0].server
        if isinstance(args[0], discord.Member):
            dest = args[0]
        log.info('Mee6@{} >> {}'.format(dest.name,args[1].replace('\n', '~')))
        yield from super().send_message(*args, **kwargs)

    @asyncio.coroutine
    def on_server_join(self, server):
        log.info('Joined {} server : {} !'.format(server.owner.name, server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        self.db.redis.sadd('servers', server.id)
        self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            self.db.redis.set('server:{}:icon'.format(server.id), server.icon)

    @asyncio.coroutine
    def on_server_remove(self, server):
        log.info('Leaving {} server : {} !'.format(server.owner.name, server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        self.db.redis.srem('servers', server.id)

    @asyncio.coroutine
    def heartbeat(self, interval):
        while self.is_logged_in:
            self.db.redis.set('heartbeat', 1, ex=interval)
            yield from asyncio.sleep(0.9 * interval)

    @asyncio.coroutine
    def update_stats(self, interval):
        while self.is_logged_in:
            # Total members and online members
            members = list(self.get_all_members())
            online_members = filter(lambda m: m.status is discord.Status.online, members)
            online_members = list(online_members)
            self.db.redis.set('mee6:stats:online_members', len(online_members))
            self.db.redis.set('mee6:stats:members', len(members))

            # Last messages
            for index, timestamp in enumerate(self.last_messages):
                if timestamp + interval < time():
                    self.last_messages.pop(index)
            self.db.redis.set('mee6:stats:last_messages', len(self.last_messages))

            yield from asyncio.sleep(interval)

    @asyncio.coroutine
    def _run_plugin_event(self, plugin, event, *args, **kwargs):
        # A modified coro that is based on Client._run_event
        try:
            yield from getattr(plugin, event)(*args, **kwargs)
        except asyncio.CancelledError:
            pass
        except Exception:
            try:
                yield from self.on_error(event, *args, **kwargs)
            except asyncio.CancelledError:
                pass

    async def on_message(self, message):
        mee6_server_id = "159962941502783488"
        update_channel_id = "160784396679380992"
        if not hasattr(message, 'server'):
            return
        if (message.server.id, message.channel.id) == (mee6_server_id, update_channel_id):
            owners = set(server.owner for server in self.servers)
            for owner in owners:
                await self.send_message(
                    owner,
                    message.content
                )

    def dispatch(self, event, *args, **kwargs):
        # A list of events that are available from the plugins
        plugin_events = (
            'message',
            'message_delete',
            'message_edit',
            'channel_delete',
            'channel_create',
            'channel_update',
            'member_join',
            'member_update',
            'server_update',
            'server_role_create',
            'server_role_delete',
            'server_role_update',
            'voice_state_update',
            'member_ban',
            'member_unban',
            'typing'
        )

        log.debug('Dispatching event {}'.format(event))
        method = 'on_' + event
        handler = 'handle_' + event

        # Total number of messages stats update
        if event=='message':
            self.db.redis.incr('mee6:stats:messages')
            self.last_messages.append(time())
            if hasattr(self, method):
                discord.utils.create_task(self._run_event(method, *args,\
             **kwargs), loop=self.loop)

        if hasattr(self, handler):
            getattr(self, handler)(*args, **kwargs)

        if event in plugin_events:
            server_context = find_server(*args, **kwargs)
            if server_context is None:
                return
            # For each plugin that the server has enabled
            enabled_plugins = self.plugin_manager.get_all(server_context)
            for plugin in enabled_plugins:
                if hasattr(plugin, method):
                    discord.utils.create_task(self._run_plugin_event(\
                    plugin, method, *args, **kwargs), loop=self.loop)
        else:
            if hasattr(self, method):
                discord.utils.create_task(self._run_event(method, *args,\
             **kwargs), loop=self.loop)

    def run(self, token):
        self.token = token
        self.headers['authorization'] = token
        self._is_logged_in.set()
        try:
            self.loop.run_until_complete(self.connect())
        except KeyboardInterrupt:
            self.loop.run_until_complete(self.logout())
            pending = asyncio.Task.all_tasks()
            gathered = asyncio.gather(*pending)
            try:
                gathered.cancel()
                self.loop.run_forever()
                gathered.exception()
            except:
                pass
        finally:
            self.loop.close()
Beispiel #9
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatched most events to the different plugins.

    """

    def __init__(self, *args, **kwargs):
        discord.Client.__init__(self, *args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.db = Db(self.redis_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []

    async def on_ready(self):
        """Called when the bot is ready.

        Launched heartbeat, update_stats cron jobs
        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')
        await self.add_all_servers()
        discord.utils.create_task(self.heartbeat(5), loop=self.loop)
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        await self.db.redis.delete('servers')
        for server in self.servers:
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set('server:{}:name'.format(server.id), server.name)
            if server.icon:
                await self.db.redis.set('server:{}:icon'.format(server.id), server.icon)

    async def on_server_join(self, server):
        """Called when joining a new server

        Adds the server to the db.
        Also adds its name and it's icon if it has one.

        """
        log.info('Joined {} server : {} !'.format(server.owner.name, server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.sadd('servers', server.id)
        await self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            await self.db.redis.set('server:{}:icon'.format(server.id), server.icon)

        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(server.owner.name, server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        await self.db.redis.srem('servers', server.id)

    async def heartbeat(self, interval):
        """Sends a heartbeat to the db every interval seconds"""
        while self.is_logged_in:
            await self.db.redis.set('heartbeat', 1, expire=interval)
            await asyncio.sleep(0.9 * interval)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        counter = 0
        msg = None
        while counter!=3:
            try:
                msg = await super().send_message(*args, **kwargs)
                counter=2
            except discord.errors.HTTPException as e:
                if e.response.status==502:
                    log.info('502 HTTP Exception, retrying...')
                else:
                    log.info('{} HTTP Exception.'.format(
                        e.response.status
                    ))
                    counter=2
            counter+=1
        return msg

    async def on_message(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))

    async def on_server_role_create(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_create(server, role))

    async def on_server_role_delete(self, server, role):
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_delete(server, role))

    async def on_server_role_update(self, before, after):
        server = None
        for s in self.servers:
            if after.id in map(lambda r:r.id, s.roles):
                server = s
                break

        if server is None:
            return

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_role_update(before, after))

    async def on_voice_state_update(self, before, after):
        if after is None:
            server = before.server
        elif before is None:
            server = after.server
        else:
            return

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_voice_state_update(before, after))

    async def on_member_ban(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_ban(member))

    async def on_member_unban(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_unban(member))

    async def on_typing(self, channel, user, when):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_typing(channel, user, when))
Beispiel #10
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatches most events to the different plugins.

    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.sentry_dsn = kwargs.get('sentry_dsn')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)
        self.voice_sessions_ids = dict()

        if self.shard_id is not None:
            self.shard = [self.shard_id, self.shard_count]
        else:
            self.shard = [0, 1]

    def run(self, *args):
        console_coro = self.loop.create_server(make_console(self), '127.0.0.1',
                                               8000)
        self.loop.run_until_complete(console_coro)
        self.loop.create_task(self.connect_schwifty())
        self.loop.run_until_complete(self.start(*args))

    async def connect_schwifty(self):
        self.schwifty = await SchwiftyWebsocket.create(self.shard, self)

        while not self.is_closed:
            try:
                await self.schwifty.poll_event()
            except (ConnectionClosed, asyncio.TimeoutError) as e:
                await asyncio.sleep(1)
                self.schwifty = await SchwiftyWebsocket.create(
                    self.shard, self)

    async def console(reader, writer):
        loop = asyncio.get_event_loop()
        data = await reader.read(100)
        message = data.decode()
        result = eval(message)
        writer.write(result)
        await writer.drain()
        writer.close()

    def send_monitoring_message(self, msg):
        self.loop.create_task(self._send_monitoring_message(msg))

    async def _send_monitoring_message(self, msg):
        url = os.getenv('MONITORING_WH')
        if not url:
            return
        headers = {}
        headers['Content-Type'] = 'application/json'
        data = json.dumps({'content': msg})
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=data) as d:
                pass

    async def on_socket_raw_receive(self, payload):
        tags = {'op': str(payload['op']), 't': payload.get('t') or "NONE"}
        self.stats.incr('discord.event', tags=tags)

        if not payload['op'] == 0:
            return

        if payload['t'] == 'VOICE_STATE_UPDATE':
            d = payload['d']
            if str(d['user_id']) != self.user.id:
                return

            guild_id = d['guild_id']
            self.voice_sessions_ids[guild_id] = d['session_id']

        if payload['t'] == 'VOICE_SERVER_UPDATE':
            d = payload['d']
            guild_id = d['guild_id']
            d['session_id'] = self.voice_sessions_ids.get(guild_id)
            if d.get('endpoint'):
                await self.schwifty.voice_update(d)

        if payload['t'] == 'READY':
            if not hasattr(self, 'shard_id'):
                return

            msg = "**[READY]** shard {}/{}".format(self.shard_id,
                                                   self.shard_count)
            self.send_monitoring_message(msg)

        if payload['t'] == 'RESUMED':
            if not hasattr(self, 'shard_id'):
                return

            msg = "**[RESUMED]** shard {}/{}".format(self.shard_id,
                                                     self.shard_count)
            self.send_monitoring_message(msg)

    async def dispatch_schwifty_event(self, t, d):
        listener_name = 'on_schwifty_' + t.lower()
        listener = getattr(self, listener_name, None)
        if listener:
            await listener(**d)

    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set('server:{}:name'.format(server.id),
                                        server.name)
            if server.icon:
                await self.db.redis.set('server:{}:icon'.format(server.id),
                                        server.icon)

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')
        await self.db.redis.sadd('servers', server.id)

        log.info('Joined {} server : {} !'.format(server.owner.name,
                                                  server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.set('server:{}:name'.format(server.id),
                                server.name)
        if server.icon:
            await self.db.redis.set('server:{}:icon'.format(server.id),
                                    server.icon)
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(server.owner.name,
                                                   server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return

        wh_key = 'channel.{}.last_message_whid'.format(message.channel.id)
        if message.webhook_id:
            self.db.redis.set(wh_key, message.webhook_id)
        else:
            self.db.redis.delete(wh_key)

        if message.author.__class__ != discord.Member:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id, self.shard_count))

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))

    async def on_schwifty_playing(self, guild_id, url):
        server = discord.Object(id=str(guild_id))
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_schwifty_playing(guild_id, url))

    async def on_schwifty_finished_playing(self, guild_id):
        server = discord.Object(id=str(guild_id))
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(
                plugin.on_schwifty_finished_playing(guild_id))
Beispiel #11
0
class Mee6(discord.Client):
    '''A modified discord.Client class

    This mod dispatches most events to the different plugins.

    '''
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.sentry_dsn = kwargs.get('sentry_dsn')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)
        self.voice_sessions_ids = dict()

        if self.shard_id is not None:
            self.shard = [self.shard_id, self.shard_count]
        else:
            self.shard = [0, 1]

    def run(self, *args):
        console_coro = self.loop.create_server(make_console(self), '127.0.0.1',
                                               8000)
        self.loop.run_until_complete(console_coro)
        # These are related to music player I think. Disabled currently since I couldn't get it working properly.
        #self.loop.create_task(self.connect_schwifty())
        self.loop.run_until_complete(self.start(*args))

    # These are related to music player I think. Disabled currently since I couldn't get it working properly.
    # async def connect_schwifty(self):
    #     self.schwifty = await SchwiftyWebsocket.create(
    #         self.shard,
    #         self
    #     )
    #
    #     while not self.is_closed:
    #         try:
    #             await self.schwifty.poll_event()
    #         except (ConnectionClosed, asyncio.TimeoutError) as e:
    #             await asyncio.sleep(1)
    #             self.schwifty = await SchwiftyWebsocket.create(
    #                 self.shard,
    #                 self
    #             )

    async def console(reader, writer):
        loop = asyncio.get_event_loop()
        data = await reader.read(100)
        message = data.decode()
        result = eval(message)
        writer.write(result)
        await writer.drain()
        writer.close()

    #These two functions log the bots status to some discord servers webhook. TODO need to adjust that.
    def send_monitoring_message(self, msg):
        self.loop.create_task(self._send_monitoring_message(msg))

    async def _send_monitoring_message(self, msg):
        url = os.getenv('MONITORING_WH')
        if not url:
            return
        headers = {}
        headers['Content-Type'] = 'application/json'
        data = json.dumps({'content': msg})
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=data) as d:
                pass

    async def on_voice_state_update(self, member):
        #TODO stats?
        if str(member.id) != self.user.id:
            return

        guild_id = member.guild_id
        self.voice_sessions_ids[guild_id] = member.server.id

    #TODO This function's parameters have been changed on the new discord.py version. It is no longer sent as parsed.

    # async def on_socket_raw_receive(self, payload):
    #     tags = {'op': str(payload['op']),
    #             't': payload.get('t') or "NONE"}
    #     self.stats.incr('discord.event', tags=tags)
    #
    #     if not payload['op'] == 0:
    #         return
    #
    #
    #     if payload['t'] == 'VOICE_SERVER_UPDATE':
    #         d = payload['d']
    #         guild_id = d['guild_id']
    #         d['session_id'] = self.voice_sessions_ids.get(guild_id)
    #         if d.get('endpoint'):
    #             await self.schwifty.voice_update(d)
    #
    #     if payload['t'] == 'READY':
    #         if not hasattr(self, 'shard_id'):
    #             return
    #
    #         msg = "**[READY]** shard {}/{}".format(
    #             self.shard_id,
    #             self.shard_count
    #         )
    #         self.send_monitoring_message(msg)
    #
    #     if payload['t'] == 'RESUMED':
    #         if not hasattr(self, 'shard_id'):
    #             return
    #
    #         msg = "**[RESUMED]** shard {}/{}".format(self.shard_id,
    #                                                  self.shard_count)
    #         self.send_monitoring_message(msg)

    async def dispatch_schwifty_event(self, t, d):
        listener_name = 'on_schwifty_' + t.lower()
        listener = getattr(self, listener_name, None)
        if listener:
            await listener(**d)

    async def on_ready(self):
        '''
            Called when the bot is ready.Connects to the database
            Dispatched all the ready events
        '''
        log.info('Connected to the database')

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        '''
            Syncing all the servers to the DB
        '''
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set('server:{}:name'.format(server.id),
                                        server.name)
            if server.icon:
                await self.db.redis.set('server:{}:icon'.format(server.id),
                                        server.icon)

    async def on_server_join(self, server):
        '''
            Called when joining a new server
        '''
        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')
        await self.db.redis.sadd('servers', server.id)

        log.info('Joined {} server : {} !'.format(server.owner.name,
                                                  server.name))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.set('server:{}:name'.format(server.id),
                                server.name)
        if server.icon:
            await self.db.redis.set('server:{}:icon'.format(server.id),
                                    server.icon)
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        '''
            Called when leaving or kicked from a server Removes the server from the db.
        '''

        log.info('Leaving {} server : {} !'.format(server.owner.name,
                                                   server.name))
        log.debug('Removing server {}\'s id from the db'.format(server.id))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return

        #Not sure about this discord.py docs doesn't list this among the variables passed with new messages so commented out for now.
        #This probably changed recently since webhook messages have a seperate event on the discord.py
        #wh_key = 'channel.{}.last_message_whid'.format(message.channel.id)
        #log.info(message)
        #if message.webhook_id:
        #    self.db.redis.set(wh_key, message.webhook_id)
        #else:
        #    self.db.redis.delete(wh_key)

        if message.author.__class__ != discord.Member:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id, self.shard_count))

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))
Beispiel #12
0
class RickBot(discord.Client):
    def __init__(self, *args, **kwargs):
        self.redis_url = kwargs.get('redis_url')
        self.db = Db(self.redis_url)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []

    async def on_ready(self):
        with open('welcome_ascii.txt') as f:
            print(f.read())

        self.add_all_servers()
        discord.utils.create_task(self.heartbeat(5), loop=self.loop)
        discord.utils.create_task(self.update_stats(60), loop=self.loop)

    async def add_all_servers(self):
        log.debug('Syncing servers and DB')
        self.db.redis.delete('servers')
        for server in self.servers:
            log.debug('Adding server {}\'s ID to DB'.format(server.id))
            self.db.redis.sadd('servers', server.id)

    async def send_message(self, *args, **kwargs):
        log.info('RickBot >> {}'.format(args[1].replace('\n', '~')))
        await super().send_message(*args, **kwargs)

    async def on_server_join(self, server):
        log.info('Joined {} server: {}!'.format(server.owner.name, server.name))
        log.debug('Adding self {}\'s ID to DB'.format(server.id))
        self.db.redis.sadd('servers', server.id)
        self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            self.db.redis.set('server:{}:icon'.format(server.id), server.icon)

    async def on_server_remove(self, server):
        log.info('Leaving {} server: {}'.format(server.owner.name, sever.name))
        log.debug('Removing server {}\'s from DB'.format(server.id))
        self.db.redis.srem('servers', server.id)

    async def heartbeat(self, interval):
        while self.is_logged_in:
            self.db.redis.set('heartbeat', 1, ex=interval)
            await asyncio.sleep(0.9 * interval)

    async def update_stats(self, interval):
        while self.is_logged_in:
            # Total members and online members
            members = list(self.get_all_members())
            online_members = filter(lambda m: m.status is discord.Status.online,
                                    members)
            online_members = list(online_members)
            self.db.redis.set('rickbot:stats:online_members',
                              len(online_members))
            self.db.redis.set('rickbot:stats:members', len(members))

            # Last messages
            for index, timestamp in enumerate(self.last_messages):
                if timestamp + interval < time():
                    self.last_messages.pop(index)
            self.db.redis.set('rickbot:stats:last_messages',
                              len(self.last_messages))

            await asyncio.sleep(interval)

    async def _run_plugin_event(self, plugin, event, *args, **kwargs):
        # A yummy modified coroutine that is based on Client._run_event
        try:
            await getattr(plugin, event)(*args, **kwargs)
        except asyncio.CancelledError:
            pass
        except Exception:
            try:
                await self.on_error(event, *args, **kwargs)
            except asyncio.CancelledError:
                pass

    def dispatch(self, event, *args, **kwargs):
        # A list of events that are avalible from the plugins.
        plugin_events = (
            'message',
            'message_delete',
            'message_edit',
            'channel_delete',
            'channel_create',
            'channel_update',
            'member_join',
            'member_update',
            'server_update',
            'server_role_create',
            'server_role_delete',
            'server_role_update',
            'voice_state_update',
            'member_ban',
            'member_unban',
            'typing'
        )

        # Total number of messages stats update
        if event == 'message':
            self.db.redis.incr('rickbot:stats:messages')
            self.last_messages.append(time())

        log.debug('Dispatching event {}'.format(event))
        method = 'on_' + event
        handler = 'handle_' + event

        if hasattr(self, handler):
            getattr(self, handler)(*args, **kwargs)

        if event in plugin_events:
            server_context = find_server(*args, **kwargs)
            if server_context is None:
                return

            # For each plugin that the server has enabled
            for plugin in enabled_plugins:
                if hasattr(plugin, method):
                    discord.utils.create_task(self._run_plugin_event(\
                    plugin, method, *args, **kwargs), loop=self.loop)
        else:
            if hasattr(self, method):
                discord.utils.channel_create(self._run_event(method, *args, \
                **kwargs), loop=self.loop)

    def run(self, token):
        self.token = token
        self.headers['authorization'] = token
        self._is_logged_in.set()
        try:
            self.loop.run_until_complete(self.connect())
        except KeyboardInterrupt:
            self.loop.run_until_complete(self.logout())
            pending = async.Task.all_tasks()
            gathered = asyncio.gather(*pending)
            try:
                gathered.cancel()
                self.loop.run_forever()
                gathered.exception()
            except:
                pass
        finally:
            self.loop.close()
Beispiel #13
0
class Mee6(discord.Client):
    """A modified discord.Client class

    This mod dispatches most events to the different plugins.

    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.redis_url = kwargs.get('redis_url')
        self.mongo_url = kwargs.get('mongo_url')
        self.dd_agent_url = kwargs.get('dd_agent_url')
        self.sentry_dsn = kwargs.get('sentry_dsn')
        self.db = Db(self.redis_url, self.mongo_url, self.loop)
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.load_all()
        self.last_messages = []
        self.stats = DDAgent(self.dd_agent_url)
        self.voice_sessions_ids = dict()

        if self.shard_id is not None:
            self.shard = [self.shard_id, self.shard_count]
        else:
            self.shard = [0, 1]

    def run(self, *args):
        console_coro = self.loop.create_server(make_console(self),
                                               '127.0.0.1', 8000)
        self.loop.run_until_complete(console_coro)
        self.loop.create_task(self.connect_schwifty())
        self.loop.run_until_complete(self.start(*args))

    async def connect_schwifty(self):
        self.schwifty = await SchwiftyWebsocket.create(
            self.shard,
            self
        )

        while not self.is_closed:
            try:
                await self.schwifty.poll_event()
            except (ConnectionClosed, asyncio.TimeoutError) as e:
                await asyncio.sleep(1)
                self.schwifty = await SchwiftyWebsocket.create(
                    self.shard,
                    self
                )

    async def console(reader, writer):
        loop = asyncio.get_event_loop()
        data = await reader.read(100)
        message = data.decode()
        result = eval(message)
        writer.write(result)
        await writer.drain()
        writer.close()

    def send_monitoring_message(self, msg):
        self.loop.create_task(self._send_monitoring_message(msg))

    async def _send_monitoring_message(self, msg):
        url = os.getenv('MONITORING_WH')
        if not url:
            return
        headers = {}
        headers['Content-Type'] = 'application/json'
        data = json.dumps({'content': msg})
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=data) as d:
                pass

    async def on_socket_raw_receive(self, payload):
        tags = {'op': str(payload['op']),
                't': payload.get('t') or "NONE"}
        self.stats.incr('discord.event', tags=tags)

        if not payload['op'] == 0:
            return

        if payload['t'] == 'VOICE_STATE_UPDATE':
            d = payload['d']
            if str(d['user_id']) != self.user.id:
                return

            guild_id = d['guild_id']
            self.voice_sessions_ids[guild_id] = d['session_id']

        if payload['t'] == 'VOICE_SERVER_UPDATE':
            d = payload['d']
            guild_id = d['guild_id']
            d['session_id'] = self.voice_sessions_ids.get(guild_id)
            if d.get('endpoint'):
                await self.schwifty.voice_update(d)

        if payload['t'] == 'READY':
            if not hasattr(self, 'shard_id'):
                return

            msg = "**[READY]** shard {}/{}".format(
                self.shard_id,
                self.shard_count
            )
            self.send_monitoring_message(msg)

        if payload['t'] == 'RESUMED':
            if not hasattr(self, 'shard_id'):
                return

            msg = "**[RESUMED]** shard {}/{}".format(self.shard_id,
                                                     self.shard_count)
            self.send_monitoring_message(msg)

    async def dispatch_schwifty_event(self, t, d):
        listener_name = 'on_schwifty_' + t.lower()
        listener = getattr(self, listener_name, None)
        if listener:
            await listener(**d)

    async def on_ready(self):
        """Called when the bot is ready.

        Connects to the database
        Dispatched all the ready events

        """
        log.info('Connected to the database')

        await self.add_all_servers()
        for plugin in self.plugins:
            self.loop.create_task(plugin.on_ready())

    async def add_all_servers(self):
        """Syncing all the servers to the DB"""
        log.debug('Syncing servers and db')
        for server in self.servers:
            self.stats.set('mee6.servers', server.id)
            log.debug('Adding server {}\'s id to db'.format(server.id))
            await self.db.redis.sadd('servers', server.id)
            if server.name:
                await self.db.redis.set(
                    'server:{}:name'.format(server.id),
                    server.name
                )
            if server.icon:
                await self.db.redis.set(
                    'server:{}:icon'.format(server.id),
                    server.icon
                )

    async def on_server_join(self, server):
        """Called when joining a new server"""

        self.stats.set('mee6.servers', server.id)
        self.stats.incr('mee6.server_join')
        await self.db.redis.sadd('servers', server.id)

        log.info('Joined {} server : {} !'.format(
            server.owner.name,
            server.name
        ))
        log.debug('Adding server {}\'s id to db'.format(server.id))
        await self.db.redis.set('server:{}:name'.format(server.id), server.name)
        if server.icon:
            await self.db.redis.set(
                'server:{}:icon'.format(server.id),
                server.icon
            )
        # Dispatching to global plugins
        for plugin in self.plugins:
            if plugin.is_global:
                self.loop.create_task(plugin.on_server_join(server))

    async def on_server_remove(self, server):
        """Called when leaving or kicked from a server

        Removes the server from the db.

        """
        log.info('Leaving {} server : {} !'.format(
            server.owner.name,
            server.name
        ))
        log.debug('Removing server {}\'s id from the db'.format(
            server.id
        ))
        await self.db.redis.srem('servers', server.id)

    async def get_plugins(self, server):
        plugins = await self.plugin_manager.get_all(server)
        return plugins

    async def send_message(self, *args, **kwargs):
        self.stats.incr('mee6.sent_messages')
        return await super().send_message(*args, **kwargs)

    async def on_message(self, message):
        self.stats.incr('mee6.recv_messages')
        if message.channel.is_private:
            return

        wh_key = 'channel.{}.last_message_whid'.format(message.channel.id)
        if message.webhook_id:
            self.db.redis.set(wh_key, message.webhook_id)
        else:
            self.db.redis.delete(wh_key)

        if message.author.__class__ != discord.Member:
            return

        server = message.server

        if message.content == "!shard?":
            if hasattr(self, 'shard_id'):
                await self.send_message(
                    message.channel,
                    "shard {}/{}".format(self.shard_id, self.shard_count)
                )

        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin._on_message(message))

    async def on_message_edit(self, before, after):
        if before.channel.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_edit(before, after))

    async def on_message_delete(self, message):
        if message.channel.is_private:
            return

        server = message.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_message_delete(message))

    async def on_channel_create(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_create(channel))

    async def on_channel_update(self, before, after):
        if before.is_private:
            return

        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_update(before, after))

    async def on_channel_delete(self, channel):
        if channel.is_private:
            return

        server = channel.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_channel_delete(channel))

    async def on_member_join(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_join(member))

    async def on_member_remove(self, member):
        server = member.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_remove(member))

    async def on_member_update(self, before, after):
        server = after.server
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_member_update(before, after))

    async def on_server_update(self, before, after):
        server = after
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_server_update(before, after))

    async def on_schwifty_playing(self, guild_id, url):
        server = discord.Object(id=str(guild_id))
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_schwifty_playing(guild_id, url))

    async def on_schwifty_finished_playing(self, guild_id):
        server = discord.Object(id=str(guild_id))
        enabled_plugins = await self.get_plugins(server)
        for plugin in enabled_plugins:
            self.loop.create_task(plugin.on_schwifty_finished_playing(guild_id))