<commit_before># -*- coding: utf-8 -*-
import random
import requests
import humanize
import operator
import gevent

from six import BytesIO
from PIL import Image
from peewee import fn
from gevent.pool import Pool
from datetime import datetime, timedelta
from collections import defaultdict

from disco.types.user import GameType, Status, User as DiscoUser
from disco.types.message import MessageEmbed
from disco.types.channel import ChannelType
from disco.util.snowflake import to_datetime
from disco.util.sanitize import S
from disco.api.http import Routes, APIException

from rowboat.plugins import RowboatPlugin as Plugin, CommandFail
from rowboat.util.timing import Eventual
from rowboat.util.input import parse_duration, humanize_duration
from rowboat.util.gevent import wait_many
from rowboat.util.stats import statsd, to_tags
from rowboat.types.plugin import PluginConfig
from rowboat.models.guild import GuildVoiceSession
from rowboat.models.user import User, Infraction
from rowboat.models.message import Message, Reminder
from rowboat.util.images import get_dominant_colors_user, get_dominant_colors_guild
from rowboat.constants import (
    STATUS_EMOJI, SNOOZE_EMOJI, GREEN_TICK_EMOJI, GREEN_TICK_EMOJI_ID, RED_TICK_EMOJI, RED_TICK_EMOJI_ID, 
    EMOJI_RE, USER_MENTION_RE, YEAR_IN_SEC, CDN_URL
)


def get_status_emoji(presence):
    if presence.game and presence.game.type == GameType.STREAMING:
        return STATUS_EMOJI[GameType.STREAMING], 'Streaming'
    elif presence.status == Status.ONLINE:
        return STATUS_EMOJI[Status.ONLINE], 'Online'
    elif presence.status == Status.IDLE:
        return STATUS_EMOJI[Status.IDLE], 'Idle',
    elif presence.status == Status.DND:
        return STATUS_EMOJI[Status.DND], 'DND'
    elif presence.status in (Status.OFFLINE, Status.INVISIBLE):
        return STATUS_EMOJI[Status.OFFLINE], 'Offline'


def get_emoji_url(emoji):
    return CDN_URL.format('-'.join(
        char.encode("unicode_escape").decode("utf-8")[2:].lstrip("0")
        for char in emoji))


class UtilitiesConfig(PluginConfig):
    pass


@Plugin.with_config(UtilitiesConfig)
class UtilitiesPlugin(Plugin):
    def load(self, ctx):
        super(UtilitiesPlugin, self).load(ctx)
        self.reminder_task = Eventual(self.trigger_reminders)
        self.spawn_later(10, self.queue_reminders)

    def queue_reminders(self):
        try:
            next_reminder = Reminder.select().order_by(
                Reminder.remind_at.asc()
            ).limit(1).get()
        except Reminder.DoesNotExist:
            return

        self.reminder_task.set_next_schedule(next_reminder.remind_at)

    @Plugin.command('coin', group='random', global_=True)
    def coin(self, event):
        """
        Flip a coin
        """
        event.msg.reply(random.choice(['heads', 'tails']))

    @Plugin.command('number', '[end:int] [start:int]', group='random', global_=True)
    def random_number(self, event, end=10, start=0):
        """
        Returns a random number
        """

        # Because someone will be an idiot
        if end > 9223372036854775807:
            return event.msg.reply(':warning: ending number too big!')

        if end <= start:
            return event.msg.reply(':warning: ending number must be larger than starting number!')

        event.msg.reply(str(random.randint(start, end)))

    @Plugin.command('cat', global_=True)
    def cat(self, event):
        # Sometimes random.cat gives us gifs (smh)
        for _ in range(3):
            try:
                r = requests.get('https://aws.random.cat/meow')
                r.raise_for_status()
            except:
                continue

            url = r.json()['file']
            if not url.endswith('.gif'):
                break
        else:
            return event.msg.reply('404 cat not found :(')

        r = requests.get(url)
        r.raise_for_status()
        event.msg.reply('', attachments=[('cat.jpg', r.content)])

    @Plugin.command('urban', '<term:str...>', global_=True)
    def urban(self, event, term):
        r = requests.get('http://api.urbandictionary.com/v0/define', params={
            'term': term,
        })
        r.raise_for_status()
        data = r.json()

        if not len(data['list']):
            return event.msg.reply(':warning: no matches')

        event.msg.reply(u'{} - {}'.format(
            S(data['list'][0]['word']),
            S(data['list'][0]['definition']),
        ))

    @Plugin.command('pwnd', '<email:str>', global_=True)
    def pwnd(self, event, email):
        r = requests.get('https://haveibeenpwned.com/api/v2/breachedaccount/{}'.format(
            email
        ))

        if r.status_code == 404:
            return event.msg.reply(":white_check_mark: you haven't been pwnd yet, awesome!")

        r.raise_for_status()
        data = r.json()

        sites = []

        for idx, site in enumerate(data):
            sites.append(u'{} - {} ({})'.format(
                site['BreachDate'],
                site['Title'],
                site['Domain'],
            ))

        return event.msg.reply(u":warning: You've been pwnd on {} sites:\n{}".format(
            len(sites),
            '\n'.join(sites),
        ))

    @Plugin.command('geoip', '<ip:str>', global_=True)
    def geoip(self, event, ip):
        r = requests.get('http://json.geoiplookup.io/{}'.format(ip))
        r.raise_for_status()
        data = r.json()

        event.msg.reply(u'{} - {}, {} ({}) | {}, {}'.format(
            data['isp'],
            data['city'],
            data['region'],
            data['country_code'],
            data['latitude'],
            data['longitude'],
        ))

    @Plugin.command('emoji', '<emoji:str>', global_=True)
    def emoji(self, event, emoji):
        if not EMOJI_RE.match(emoji):
            return event.msg.reply(u'Unknown emoji: `{}`'.format(S(emoji)))

        fields = []

        name, eid = EMOJI_RE.findall(emoji)[0]
        fields.append('**ID:** {}'.format(eid))
        fields.append('**Name:** {}'.format(S(name)))

        guild = self.state.guilds.find_one(lambda v: eid in v.emojis)
        if guild:
            fields.append('**Guild:** {} ({})'.format(S(guild.name), guild.id))

        anim = emoji.startswith('<a:')
        fields.append('**Animated:** {}'.format('Yes' if anim else 'No'))

        ext = 'gif' if anim else 'png'
        url = 'https://discordapp.com/api/emojis/{}.{}'.format(eid, ext)
        r = requests.get(url)
        r.raise_for_status()
        return event.msg.reply('\n'.join(fields), attachments=[('emoji.'+ext, r.content)])

    # Full credit goes to: Xenthys
    def get_emoji_url(self, emoji):
        name = '-'.join(char.encode("unicode_escape").decode("utf-8")[2:].lstrip("0") for char in emoji)
        return 'https://cdn.oceanlord.me/emoji/{}.png'.format(name) if name.find('--') == -1 else None
    
    @Plugin.command('jumbo', '<emojis:str...>', global_=True)
    def jumbo(self, event, emojis):
        emojis = emojis.split(' ')
        if len(emojis) == 1:
            url = ext = ''
            emoji = emojis[0]
            if EMOJI_RE.match(emoji):
                _, eid = EMOJI_RE.findall(emoji)[0]
                ext = 'gif' if emoji.startswith('<a:') else 'png'
                url = 'https://cdn.discordapp.com/emojis/{}.{}?v=1'.format(eid, ext)
            else:
                ext = 'png'
                url = self.get_emoji_url(emoji)

            if not url:
                raise CommandFail('provided emoji is invalid')

            r = requests.get(url)
            try:
                r.raise_for_status()
            except requests.HTTPError:
                raise CommandFail('provided emoji is invalid')
            return event.msg.reply('', attachments=[('emoji.'+ext, r.content)])

        else:
            urls = []
            for emoji in emojis[:5]:
                if EMOJI_RE.match(emoji):
                    _, eid = EMOJI_RE.findall(emoji)[0]
                    urls.append('https://cdn.discordapp.com/emojis/{}.png?v=1'.format(eid))
                else:
                    url = self.get_emoji_url(emoji)
                    urls.append(url) if url else None

            width, height, images = 0, 0, []

            for r in Pool(6).imap(requests.get, urls):
                try:
                    r.raise_for_status()
                except requests.HTTPError:
                    continue

                img = Image.open(BytesIO(r.content))
                height = img.height if img.height > height else height
                width += img.width + 10
                images.append(img)

            if not images:
                raise CommandFail('provided emojis are invalid')

            image = Image.new('RGBA', (width, height))
            width_offset = 0
            for img in images:
                image.paste(img, (width_offset, 0))
                width_offset += img.width + 10

            combined = BytesIO()
            image.save(combined, 'png', quality=55)
            combined.seek(0)
            return event.msg.reply('', attachments=[('emoji.png', combined)])

    @Plugin.command('seen', '<user:user>', global_=True)
    def seen(self, event, user):
        try:
            msg = Message.select(Message.timestamp).where(
                Message.author_id == user.id
            ).order_by(Message.timestamp.desc()).limit(1).get()
        except Message.DoesNotExist:
            return event.msg.reply(u"I've never seen {}".format(user))

        event.msg.reply(u'I last saw {} {} ago (at {})'.format(
            user,
            humanize_duration(datetime.utcnow() - msg.timestamp),
            msg.timestamp
        ))

    @Plugin.command('search', '<query:str...>', global_=True)
    def search(self, event, query):
        queries = []

        if query.isdigit():
            queries.append((User.user_id == query))

        q = USER_MENTION_RE.findall(query)
        if len(q) and q[0].isdigit():
            queries.append((User.user_id == q[0]))
        else:
            queries.append((User.username ** u'%{}%'.format(query.replace('%', ''))))

        if '#' in query:
            username, discrim = query.rsplit('#', 1)
            if discrim.isdigit():
                queries.append((
                    (User.username == username) &
                    (User.discriminator == int(discrim))))

        users = User.select().where(reduce(operator.or_, queries))
        if len(users) == 0:
            return event.msg.reply(u'No users found for query `{}`'.format(S(query, escape_codeblocks=True)))

        if len(users) == 1:
            if users[0].user_id in self.state.users:
                return self.info(event, self.state.users.get(users[0].user_id))

        return event.msg.reply(u'Found the following users for your query: ```{}```'.format(
            u'\n'.join(map(lambda i: u'{} ({})'.format(unicode(i), i.user_id), users[:25]))
        ))

    @Plugin.command('server', '[guild_id:snowflake]', global_=True)
    def server(self, event, guild_id=None):
        guild = self.state.guilds.get(guild_id) if guild_id else event.guild
        if not guild:
            raise CommandFail('invalid server')

        content = []
        content.append(u'**\u276F Server Information**')

        created_at = to_datetime(guild.id)
        content.append(u'**Created:** {} ago ({})'.format(
            humanize.naturaldelta(datetime.utcnow() - created_at),
            created_at.isoformat(),
        ))
        content.append(u'**Members:** {:,}'.format(len(guild.members)))
        content.append(u'**Features:** {}'.format(', '.join(guild.features) or 'none'))
        content.append(u'**Voice region:** {}'.format(guild.region))
    
        if not bool(guild.max_members):
            self.state.guilds[guild.id].inplace_update(self.client.api.guilds_get(guild.id), ignored=[
                'channels',
                'members',
                'voice_states',
                'presences',
            ])

        content.append(u'**Max presences:** {:,}'.format(self.state.guilds[guild.id].max_presences))
        content.append(u'**Max members:** {:,}'.format(self.state.guilds[guild.id].max_members))

        content.append(u'\n**\u276F Counts**')
        count = {}
        for c in guild.channels.values():
            if not c.type:
                continue
            ctype = c.type.name.split('_')[1]
            count[ctype] = count.get(ctype, 0) + 1
        content.append(u'**Roles:** {}'.format(len(guild.roles)))
        content.append(u'**Categories:** {}'.format(count.get('category', 0)))
        content.append(u'**Text channels:** {}'.format(count.get('text', 0)))
        content.append(u'**Voice channels:** {}'.format(count.get('voice', 0)))
        content.append(u'**Server boost level:** Level {}'.format(int(guild.premium_tier)))
        real_boost_count = len(filter(lambda y: guild.members.get(y).premium_since, guild.members))
        content.append(u'**Server boosts:** {} {}'.format(
            guild.premium_subscription_count,
            '({})'.format(real_boost_count) if real_boost_count < guild.premium_subscription_count else ''
        ))

        content.append(u'\n**\u276F Members**')
        status_counts = defaultdict(int)
        for member in guild.members.values():
            if not member.user.presence:
                status = Status.OFFLINE
            else:
                status = member.user.presence.status
            status_counts[status] += 1

        for status, count in sorted(status_counts.items(), key=lambda i: str(i[0]), reverse=True):
            content.append(u'<{}> - {}'.format(
                STATUS_EMOJI[status], count
            ))

        embed = MessageEmbed()
        if guild.icon:
            embed.set_thumbnail(url=guild.icon_url)
            embed.color = get_dominant_colors_guild(guild, guild.get_icon_url('png'))
        embed.description = '\n'.join(content)
        event.msg.reply('', embed=embed)

    @Plugin.command('info', '[user:user|snowflake]')
    def info(self, event, user=None):<commit_msg>[utilities] add emoji info to !server<commit_after># -*- coding: utf-8 -*-
import random
import requests
import humanize
import operator
import gevent

from six import BytesIO
from PIL import Image
from peewee import fn
from gevent.pool import Pool
from datetime import datetime, timedelta
from collections import defaultdict
from holster.enum import Enum
from math import max

from disco.types.user import GameType, Status, User as DiscoUser
from disco.types.message import MessageEmbed
from disco.types.channel import ChannelType
from disco.util.snowflake import to_datetime
from disco.util.sanitize import S
from disco.api.http import Routes, APIException

from rowboat.plugins import RowboatPlugin as Plugin, CommandFail
from rowboat.util.timing import Eventual
from rowboat.util.input import parse_duration, humanize_duration
from rowboat.util.gevent import wait_many
from rowboat.util.stats import statsd, to_tags
from rowboat.types.plugin import PluginConfig
from rowboat.models.guild import GuildVoiceSession
from rowboat.models.user import User, Infraction
from rowboat.models.message import Message, Reminder
from rowboat.util.images import get_dominant_colors_user, get_dominant_colors_guild
from rowboat.constants import (
    STATUS_EMOJI, SNOOZE_EMOJI, GREEN_TICK_EMOJI, GREEN_TICK_EMOJI_ID, RED_TICK_EMOJI, RED_TICK_EMOJI_ID, 
    EMOJI_RE, USER_MENTION_RE, YEAR_IN_SEC, CDN_URL
)


def get_status_emoji(presence):
    if presence.game and presence.game.type == GameType.STREAMING:
        return STATUS_EMOJI[GameType.STREAMING], 'Streaming'
    elif presence.status == Status.ONLINE:
        return STATUS_EMOJI[Status.ONLINE], 'Online'
    elif presence.status == Status.IDLE:
        return STATUS_EMOJI[Status.IDLE], 'Idle',
    elif presence.status == Status.DND:
        return STATUS_EMOJI[Status.DND], 'DND'
    elif presence.status in (Status.OFFLINE, Status.INVISIBLE):
        return STATUS_EMOJI[Status.OFFLINE], 'Offline'


def get_emoji_url(emoji):
    return CDN_URL.format('-'.join(
        char.encode("unicode_escape").decode("utf-8")[2:].lstrip("0")
        for char in emoji))


class UtilitiesConfig(PluginConfig):
    pass


@Plugin.with_config(UtilitiesConfig)
class UtilitiesPlugin(Plugin):
    def load(self, ctx):
        super(UtilitiesPlugin, self).load(ctx)
        self.reminder_task = Eventual(self.trigger_reminders)
        self.spawn_later(10, self.queue_reminders)

    def queue_reminders(self):
        try:
            next_reminder = Reminder.select().order_by(
                Reminder.remind_at.asc()
            ).limit(1).get()
        except Reminder.DoesNotExist:
            return

        self.reminder_task.set_next_schedule(next_reminder.remind_at)

    @Plugin.command('coin', group='random', global_=True)
    def coin(self, event):
        """
        Flip a coin
        """
        event.msg.reply(random.choice(['heads', 'tails']))

    @Plugin.command('number', '[end:int] [start:int]', group='random', global_=True)
    def random_number(self, event, end=10, start=0):
        """
        Returns a random number
        """

        # Because someone will be an idiot
        if end > 9223372036854775807:
            return event.msg.reply(':warning: ending number too big!')

        if end <= start:
            return event.msg.reply(':warning: ending number must be larger than starting number!')

        event.msg.reply(str(random.randint(start, end)))

    @Plugin.command('cat', global_=True)
    def cat(self, event):
        # Sometimes random.cat gives us gifs (smh)
        for _ in range(3):
            try:
                r = requests.get('https://aws.random.cat/meow')
                r.raise_for_status()
            except:
                continue

            url = r.json()['file']
            if not url.endswith('.gif'):
                break
        else:
            return event.msg.reply('404 cat not found :(')

        r = requests.get(url)
        r.raise_for_status()
        event.msg.reply('', attachments=[('cat.jpg', r.content)])

    @Plugin.command('urban', '<term:str...>', global_=True)
    def urban(self, event, term):
        r = requests.get('http://api.urbandictionary.com/v0/define', params={
            'term': term,
        })
        r.raise_for_status()
        data = r.json()

        if not len(data['list']):
            return event.msg.reply(':warning: no matches')

        event.msg.reply(u'{} - {}'.format(
            S(data['list'][0]['word']),
            S(data['list'][0]['definition']),
        ))

    @Plugin.command('pwnd', '<email:str>', global_=True)
    def pwnd(self, event, email):
        r = requests.get('https://haveibeenpwned.com/api/v2/breachedaccount/{}'.format(
            email
        ))

        if r.status_code == 404:
            return event.msg.reply(":white_check_mark: you haven't been pwnd yet, awesome!")

        r.raise_for_status()
        data = r.json()

        sites = []

        for idx, site in enumerate(data):
            sites.append(u'{} - {} ({})'.format(
                site['BreachDate'],
                site['Title'],
                site['Domain'],
            ))

        return event.msg.reply(u":warning: You've been pwnd on {} sites:\n{}".format(
            len(sites),
            '\n'.join(sites),
        ))

    @Plugin.command('geoip', '<ip:str>', global_=True)
    def geoip(self, event, ip):
        r = requests.get('http://json.geoiplookup.io/{}'.format(ip))
        r.raise_for_status()
        data = r.json()

        event.msg.reply(u'{} - {}, {} ({}) | {}, {}'.format(
            data['isp'],
            data['city'],
            data['region'],
            data['country_code'],
            data['latitude'],
            data['longitude'],
        ))

    @Plugin.command('emoji', '<emoji:str>', global_=True)
    def emoji(self, event, emoji):
        if not EMOJI_RE.match(emoji):
            return event.msg.reply(u'Unknown emoji: `{}`'.format(S(emoji)))

        fields = []

        name, eid = EMOJI_RE.findall(emoji)[0]
        fields.append('**ID:** {}'.format(eid))
        fields.append('**Name:** {}'.format(S(name)))

        guild = self.state.guilds.find_one(lambda v: eid in v.emojis)
        if guild:
            fields.append('**Guild:** {} ({})'.format(S(guild.name), guild.id))

        anim = emoji.startswith('<a:')
        fields.append('**Animated:** {}'.format('Yes' if anim else 'No'))

        ext = 'gif' if anim else 'png'
        url = 'https://discordapp.com/api/emojis/{}.{}'.format(eid, ext)
        r = requests.get(url)
        r.raise_for_status()
        return event.msg.reply('\n'.join(fields), attachments=[('emoji.'+ext, r.content)])

    # Full credit goes to: Xenthys
    def get_emoji_url(self, emoji):
        name = '-'.join(char.encode("unicode_escape").decode("utf-8")[2:].lstrip("0") for char in emoji)
        return 'https://cdn.oceanlord.me/emoji/{}.png'.format(name) if name.find('--') == -1 else None
    
    @Plugin.command('jumbo', '<emojis:str...>', global_=True)
    def jumbo(self, event, emojis):
        emojis = emojis.split(' ')
        if len(emojis) == 1:
            url = ext = ''
            emoji = emojis[0]
            if EMOJI_RE.match(emoji):
                _, eid = EMOJI_RE.findall(emoji)[0]
                ext = 'gif' if emoji.startswith('<a:') else 'png'
                url = 'https://cdn.discordapp.com/emojis/{}.{}?v=1'.format(eid, ext)
            else:
                ext = 'png'
                url = self.get_emoji_url(emoji)

            if not url:
                raise CommandFail('provided emoji is invalid')

            r = requests.get(url)
            try:
                r.raise_for_status()
            except requests.HTTPError:
                raise CommandFail('provided emoji is invalid')
            return event.msg.reply('', attachments=[('emoji.'+ext, r.content)])

        else:
            urls = []
            for emoji in emojis[:5]:
                if EMOJI_RE.match(emoji):
                    _, eid = EMOJI_RE.findall(emoji)[0]
                    urls.append('https://cdn.discordapp.com/emojis/{}.png?v=1'.format(eid))
                else:
                    url = self.get_emoji_url(emoji)
                    urls.append(url) if url else None

            width, height, images = 0, 0, []

            for r in Pool(6).imap(requests.get, urls):
                try:
                    r.raise_for_status()
                except requests.HTTPError:
                    continue

                img = Image.open(BytesIO(r.content))
                height = img.height if img.height > height else height
                width += img.width + 10
                images.append(img)

            if not images:
                raise CommandFail('provided emojis are invalid')

            image = Image.new('RGBA', (width, height))
            width_offset = 0
            for img in images:
                image.paste(img, (width_offset, 0))
                width_offset += img.width + 10

            combined = BytesIO()
            image.save(combined, 'png', quality=55)
            combined.seek(0)
            return event.msg.reply('', attachments=[('emoji.png', combined)])

    @Plugin.command('seen', '<user:user>', global_=True)
    def seen(self, event, user):
        try:
            msg = Message.select(Message.timestamp).where(
                Message.author_id == user.id
            ).order_by(Message.timestamp.desc()).limit(1).get()
        except Message.DoesNotExist:
            return event.msg.reply(u"I've never seen {}".format(user))

        event.msg.reply(u'I last saw {} {} ago (at {})'.format(
            user,
            humanize_duration(datetime.utcnow() - msg.timestamp),
            msg.timestamp
        ))

    @Plugin.command('search', '<query:str...>', global_=True)
    def search(self, event, query):
        queries = []

        if query.isdigit():
            queries.append((User.user_id == query))

        q = USER_MENTION_RE.findall(query)
        if len(q) and q[0].isdigit():
            queries.append((User.user_id == q[0]))
        else:
            queries.append((User.username ** u'%{}%'.format(query.replace('%', ''))))

        if '#' in query:
            username, discrim = query.rsplit('#', 1)
            if discrim.isdigit():
                queries.append((
                    (User.username == username) &
                    (User.discriminator == int(discrim))))

        users = User.select().where(reduce(operator.or_, queries))
        if len(users) == 0:
            return event.msg.reply(u'No users found for query `{}`'.format(S(query, escape_codeblocks=True)))

        if len(users) == 1:
            if users[0].user_id in self.state.users:
                return self.info(event, self.state.users.get(users[0].user_id))

        return event.msg.reply(u'Found the following users for your query: ```{}```'.format(
            u'\n'.join(map(lambda i: u'{} ({})'.format(unicode(i), i.user_id), users[:25]))
        ))

    def get_max_emoji_slots(self, guild):
        emoji_max_slots = 50
        emoji_max_slots_more = 200
        PremiumGuildLimits = Enum(
            NONE=50,
            TIER_1=100,
            TIER_2=150,
            TIER_3=250,
        )
        return max(emoji_max_slots_more if 'MORE_EMOJI' in guild.features else emoji_max_slots, PremiumGuildLimits[guild.premium_tier.name].value)

    @Plugin.command('server', '[guild_id:snowflake]', global_=True)
    def server(self, event, guild_id=None):
        guild = self.state.guilds.get(guild_id) if guild_id else event.guild
        if not guild:
            raise CommandFail('invalid server')

        content = []
        content.append(u'**\u276F Server Information**')

        created_at = to_datetime(guild.id)
        content.append(u'**Created:** {} ago ({})'.format(
            humanize.naturaldelta(datetime.utcnow() - created_at),
            created_at.isoformat(),
        ))
        content.append(u'**Members:** {:,}'.format(len(guild.members)))
        content.append(u'**Features:** {}'.format(', '.join(guild.features) or 'none'))
        content.append(u'**Voice region:** {}'.format(guild.region))
    
        if not bool(guild.max_members):
            self.state.guilds[guild.id].inplace_update(self.client.api.guilds_get(guild.id), ignored=[
                'channels',
                'members',
                'voice_states',
                'presences',
            ])

        content.append(u'**Max presences:** {:,}'.format(self.state.guilds[guild.id].max_presences))
        content.append(u'**Max members:** {:,}'.format(self.state.guilds[guild.id].max_members))

        content.append(u'\n**\u276F Counts**')
        count = {}
        for c in guild.channels.values():
            if not c.type:
                continue
            ctype = c.type.name.split('_')[1]
            count[ctype] = count.get(ctype, 0) + 1
        content.append(u'**Roles:** {}'.format(len(guild.roles)))
        content.append(u'**Categories:** {}'.format(count.get('category', 0)))
        content.append(u'**Text channels:** {}'.format(count.get('text', 0)))
        content.append(u'**Voice channels:** {}'.format(count.get('voice', 0)))

        static_emojis = len(filter(lambda e: guild.emojis.get(e).animated, guild.emojis))
        animated_emojis = len(filter(lambda e: not guild.emojis.get(e).animated, guild.emojis))
        content.append(u'**Emojis:** {}/{total} static, {}/{total} animated'.format(static_emojis, animated_emojis, total=get_max_emoji_slots(guild)))

        content.append(u'**Voice channels:** {}'.format(count.get('voice', 0)))
        content.append(u'**Server boost level:** Level {}'.format(int(guild.premium_tier)))
        real_boost_count = len(filter(lambda y: guild.members.get(y).premium_since, guild.members))
        content.append(u'**Server boosts:** {} {}'.format(
            guild.premium_subscription_count,
            '({})'.format(real_boost_count) if real_boost_count < guild.premium_subscription_count else ''
        ))

        content.append(u'\n**\u276F Members**')
        status_counts = defaultdict(int)
        for member in guild.members.values():
            if not member.user.presence:
                status = Status.OFFLINE
            else:
                status = member.user.presence.status
            status_counts[status] += 1

        for status, count in sorted(status_counts.items(), key=lambda i: str(i[0]), reverse=True):
            content.append(u'<{}> - {}'.format(
                STATUS_EMOJI[status], count
            ))

        embed = MessageEmbed()
        if guild.icon:
            embed.set_thumbnail(url=guild.icon_url)
            embed.color = get_dominant_colors_guild(guild, guild.get_icon_url('png'))
        embed.description = '\n'.join(content)
        event.msg.reply('', embed=embed)

    @Plugin.command('info', '[user:user|snowflake]')
    def info(self, event, user=None):
{'fraction_non_alphanumeric': 0.14783505154639176, 'fraction_numerical': 0.006941580756013746, 'mean_word_length': 2.278985915492958, 'pattern_counts': {'":': 4, '<': 27, '<?xml version=': 0, '>': 23, 'https://': 12, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4711753', 'n_tokens_mistral': 9390, 'n_tokens_neox': 9003, 'n_words': 1772}