itsVale/Vale.py

View on GitHub
cogs/utility/starboard.py

Summary

Maintainability
D
2 days
Test Coverage
import asyncio
import collections
import datetime
import logging
import re
import time
import typing
import weakref

import asyncpg
import discord
from discord.ext import commands

from utils import db, cache
from utils.formats import pluralize, human_join
from utils.paginator import Paginator

# Skidded from Danny, but with some "improvements".

logger = logging.getLogger(__name__)


class StarBoardError(Exception):
    pass


def requires_starboard():
    async def predicate(ctx):
        if not ctx.guild:
            return False

        starboard = ctx.bot.get_cog('StarBoard')

        ctx.starboard = await starboard.get_starboard(ctx.guild.id, connection=ctx.db)
        if not ctx.starboard.channel:
            raise StarBoardError('\N{WARNING SIGN} Starboard channel not found.')

        return True

    return commands.check(predicate)


class MessageID(commands.Converter):
    async def convert(self, ctx, argument):
        try:
            return int(argument, base=10)
        except ValueError:
            raise StarBoardError(f'`{argument}` is not a valid message ID. Use Developer Mode to get the `Copy ID` option.')

    @staticmethod
    def random_example(ctx):
        return ctx.message.id


class Starboard(db.Table):
    id = db.Column(db.BigInt, primary_key=True)
    channel_id = db.Column(db.BigInt)
    threshold = db.Column(db.Integer, default=1)
    locked = db.Column(db.Boolean, default=False)
    max_age = db.Column(db.Interval, default="'7 days'::interval")


class StarboardEntry(db.Table, table_name='starboard_entries'):
    id = db.Column(db.Serial, primary_key=True)
    bot_message_id = db.Column(db.BigInt, nullable=True)
    message_id = db.Column(db.BigInt, unique=True)
    channel_id = db.Column(db.BigInt)
    author_id = db.Column(db.BigInt)
    guild_id = db.ForeignKey(Starboard.id, type=db.BigInt)

    # There is some fuckery with ForeignKeys that I don't want to fix rn. They must be referenced as strings.
    starboard_entries_index = db.Index(bot_message_id, message_id, 'guild_id')


class Starrers(db.Table):
    id = db.Column(db.Serial, primary_key=True)
    author_id = db.Column(db.BigInt)
    entry_id = db.ForeignKey(StarboardEntry.id, type=db.BigInt)

    starrers_index = db.Index(author_id, 'entry_id', unique=True)


class StarBoardConfig:
    __slots__ = ('bot', 'id', 'channel_id', 'threshold', 'locked', 'needs_migration', 'max_age')

    def __init__(self, *, guild_id, bot, record=None):
        self.id = guild_id
        self.bot = bot

        if record:
            self.channel_id = record['channel_id']
            self.threshold = record['threshold']
            self.locked = record['locked']
            self.needs_migration = self.locked is None
            if self.needs_migration:
                self.locked = True

            self.max_age = record['max_age']
        else:
            self.channel_id = None

    @property
    def channel(self):
        guild = self.bot.get_guild(self.id)
        return guild and guild.get_channel(self.channel_id)


class StarBoard(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

        # To save Discord some HTTP requests
        self._message_cache = {}
        self._cleaner = self.bot.loop.create_task(self.clean_message_cache())

        self._about_to_be_deleted = set()
        self._locks = weakref.WeakValueDictionary()

    def cog_unload(self):
        self._cleaner.cancel()

    async def cog_command_error(self, ctx, error):
        if isinstance(error, StarBoardError):
            await ctx.send(error)

    async def clean_message_cache(self):
        try:
            while not self.bot.is_closed():
                self._message_cache.clear()
                await asyncio.sleep(3600)

        except asyncio.CancelledError:
            pass

    @cache.cache(max_size=None)
    async def get_starboard(self, guild_id, *, connection=None):
        connection = connection or self.bot.pool

        query = 'SELECT * FROM starboard WHERE id = $1;'
        record = await connection.fetchrow(query, guild_id)

        return StarBoardConfig(guild_id=guild_id, bot=self.bot, record=record)

    @staticmethod
    def star_emoji(stars):
        if 5 > stars >= 0:
            return '\N{WHITE MEDIUM STAR}'
        elif 10 > stars >= 5:
            return '\N{GLOWING STAR}'
        elif 25 > stars >= 10:
            return '\N{DIZZY SYMBOL}'
        else:
            return '\N{SPARKLES}'

    @staticmethod
    def star_gradient_color(stars):
        percentage = stars / 13
        if percentage > 1.0:
            percentage = 1.0

        red = 255
        green = int((194 * percentage) + (253 * (1 - percentage)))
        blue = int((12 * percentage) + (247 * (1 - percentage)))
        return (red << 16) + (green << 8) + blue

    def get_emoji_message(self, message, stars):
        emoji = self.star_emoji(stars)

        if stars > 1:
            content = f'{emoji} **{stars}** {message.channel.mention} ID: {message.id}'
        else:
            content = f'{emoji} {message.channel.mention} ID: {message.id}'

        embed = discord.Embed(title='Jump to message', description=message.content, url=message.jump_url)
        if message.embeds:
            data = message.embeds[0]
            if data.type == 'image':
                embed.set_image(url=data.url)

        if message.attachments:
            file = message.attachments[0]
            if file.url.lower().endswith(('png', 'jpg', 'jpeg', 'gif', 'webp')):
                embed.set_image(url=file.url)
            else:
                embed.add_field(name='Attachment:', value=f'[{file.filename}]({file.url})', inline=False)

        embed.set_author(name=message.author.display_name, icon_url=message.author.avatar_url_as(format='png'))
        embed.timestamp = message.created_at
        embed.colour = self.star_gradient_color(stars)

        return content, embed

    async def get_message(self, channel, message_id):
        try:
            return self._message_cache[message_id]
        except KeyError:
            try:
                fake = discord.Object(message_id + 1)
                msg = await channel.history(limit=1, before=fake).next()

                if msg.id != message_id:
                    return None

                self._message_cache[message_id] = msg
                return msg

            except Exception:  # muh pycodestyle
                return None

    async def reaction_action(self, fmt, payload):
        if str(payload.emoji) != '\N{WHITE MEDIUM STAR}':
            return

        channel = self.bot.get_channel(payload.channel_id)
        if not isinstance(channel, discord.TextChannel):
            return

        method = getattr(self, f'{fmt}_message')

        user = self.bot.get_user(payload.user_id)
        if not user or user.bot:
            return

        async with self.bot.pool.acquire() as con:
            query = 'SELECT snowflake FROM blacklist WHERE snowflake = $1;'
            result = await con.fetchrow(query, channel.guild.id)
            if result:
                return

            try:
                await method(channel, payload.message_id, payload.user_id, connection=con)
            except StarBoardError:
                pass

    async def on_guild_channel_delete(self, channel):
        if not isinstance(channel, discord.TextChannel):
            return

        starboard = await self.get_starboard(channel.guild.id)
        if not starboard.channel or starboard.channel.id != channel.id:
            return

        async with self.bot.pool.acquire() as con:
            query = 'DELETE FROM starboard WHERE id = $1;'
            await con.execute(query, channel.guild.id)

    @commands.Cog.listener()
    async def on_raw_reaction_add(self, payload):
        await self.reaction_action('star', payload)

    @commands.Cog.listener()
    async def on_raw_reaction_remove(self, payload):
        await self.reaction_action('unstar', payload)

    @commands.Cog.listener()
    async def on_raw_message_delete(self, payload):
        if payload.message_id in self._about_to_be_deleted:
            self._about_to_be_deleted.discard(payload.message_id)
            return

        starboard = await self.get_starboard(payload.guild_id)
        if not starboard.channel or starboard.channel.id != payload.channel_id:
            return

        async with self.bot.pool.acquire() as con:
            query = 'DELETE FROM starboard_entries WHERE bot_message_id = $1;'
            await con.execute(query, list(payload.message_id))

    @commands.Cog.listener()
    async def on_raw_bulk_message_delete(self, payload):
        if payload.message_ids <= self._about_to_be_deleted:
            self._about_to_be_deleted.difference_update(payload.message_ids)
            return

        starboard = await self.get_starboard(payload.guild_id)
        if not starboard.channel or starboard.channel.id != payload.channel_id:
            return

        async with self.bot.pool.acquire() as con:
            query = 'DELETE FROM starboard_entries WHERE bot_message_id = ANY($1::BIGINT[]);'
            await con.execute(query, list(payload.message_ids))

    @commands.Cog.listener()
    async def on_raw_reaction_clear(self, payload):
        channel = self.bot.get_channel(payload.channel_id)
        if not channel or not isinstance(channel, discord.TextChannel):
            return

        async with self.bot.pool.acquire() as con:
            starboard = await self.get_starboard(channel.guild.id, connection=con)
            if not starboard.channel:
                return

            query = 'DELETE FROM starboard_entries WHERE message_id = $1 RETURNING bot_message_id;'
            bot_message_id = await con.fetchrow(query, payload.message_id)

            if not bot_message_id:
                return

            bot_message_id = bot_message_id[0]
            msg = await self.get_message(starboard.channel, bot_message_id)
            if msg:
                await msg.delete()

    async def star_message(self, channel, message_id, starrer_id, *, connection):
        guild_id = channel.guild.id
        lock = self._locks.get(guild_id)
        if not lock:
            self._locks[guild_id] = lock = asyncio.Lock(loop=self.bot.loop)

        async with lock:
            await self._star_message(channel, message_id, starrer_id, connection=connection)

    async def _star_message(self, channel, message_id, starrer_id, *, connection):
        """Stars a message."""

        guild_id = channel.guild.id
        starboard = await self.get_starboard(guild_id)
        if not starboard.channel:
            raise StarBoardError('\N{WARNING SIGN} Starboard channel not found.')

        if starboard.locked:
            raise StarBoardError('\N{WARNING SIGN} Starboard is locked.')

        if channel.id == starboard.channel.id:
            query = 'SELECT channel_id, message_id FROM starboard_entries WHERE bot_message_id = $1;'
            record = await connection.fetchrow(query, message_id)
            if not record:
                raise StarBoardError('Couldn\'t find message in the starboard.')

            ch = channel.guild.get_channel(record['channel_id'])
            if not ch:
                raise StarBoardError('Couldn\'t find original channel.')

            return await self._star_message(ch, record['message_id'], starrer_id, connection=connection)

        msg = await self.get_message(channel, message_id)
        if not msg:
            raise StarBoardError('\N{BLACK QUESTION MARK ORNAMENT} This message couldn\'t be found.')

        if msg.author.id == starrer_id:
            raise StarBoardError('\N{NO ENTRY SIGN} You cannot star your own message.')

        if (len(msg.content) == 0 and len(msg.attachments) == 0) or msg.type is not discord.MessageType.default:
            raise StarBoardError('\N{NO ENTRY SIGN} This message cannot be starred.')

        oldest_allowed = datetime.datetime.utcnow() - starboard.max_age
        if msg.created_at < oldest_allowed:
            raise StarBoardError('\N{NO ENTRY SIGN} This message is too old.')

        # Ew shit. Fuckery incoming
        query = """
            WITH to_insert AS (
                INSERT INTO starboard_entries AS entries (message_id, channel_id, guild_id, author_id)
                VALUES      ($1, $2, $3, $4)
                ON CONFLICT (message_id)
                DO NOTHING
                RETURNING   entries.id
            )
            INSERT INTO starrers (author_id, entry_id)
            SELECT      $5, entry.id
            FROM (
                SELECT    id
                FROM      to_insert
                UNION ALL
                SELECT    id
                FROM      starboard_entries
                WHERE     message_id = $1
                LIMIT     1
            ) AS entry
            RETURNING entry_id;
        """
        try:
            record = await connection.fetchrow(query, message_id, channel.id, guild_id, msg.author.id, starrer_id)
        except asyncpg.UniqueViolationError:
            raise StarBoardError('\N{NO ENTRY SIGN} You already starred this message.')

        entry_id = record[0]

        query = 'SELECT COUNT(*) FROM starrers WHERE entry_id = $1;'
        record = await connection.fetchrow(query, entry_id)

        count = record[0]
        if count < starboard.threshold:
            return

        content, embed = self.get_emoji_message(msg, count)

        query = 'SELECT bot_message_id FROM starboard_entries WHERE message_id = $1;'
        record = await connection.fetchrow(query, message_id)
        bot_message_id = record[0]

        if not bot_message_id:
            new_msg = await starboard.channel.send(content, embed=embed)
            query = 'UPDATE starboard_entries SET bot_message_id = $1 WHERE message_id = $2;'
            await connection.execute(query, new_msg.id, message_id)
        else:
            new_msg = await self.get_message(starboard.channel, bot_message_id)
            if not new_msg:
                query = 'DELETE FROM starboard_entries WHERE message_id = $1;'
                await connection.execute(query, message_id)
            else:
                await new_msg.edit(content=content, embed=embed)

    async def unstar_message(self, channel, message_id, starrer_id, *, connection):
        guild_id = channel.guild.id
        lock = self._locks.get(guild_id)
        if not lock:
            self._locks[guild_id] = lock = asyncio.Lock(loop=self.bot.loop)

        async with lock:
            await self._unstar_message(channel, message_id, starrer_id, connection=connection)

    async def _unstar_message(self, channel, message_id, starrer_id, *, connection):
        """Unstars a message."""

        guild_id = channel.guild.id
        starboard = await self.get_starboard(guild_id)
        if not starboard.channel:
            raise StarBoardError('\N{WARNING SIGN} Starboard channel not found.')

        if starboard.locked:
            raise StarBoardError('\N{NO ENTRY SIGN} Starboard is locked.')

        if channel.id == starboard.channel.id:
            query = 'SELECT channel_id, message_id FROM starboard_entries WHERE bot_message_id = $1;'
            record = await connection.fetchrow(query, message_id)
            if not record:
                raise StarBoardError('Couldn\'t find message in the starboard.')

            ch = channel.guild.get_channel(record['channel_id'])
            if not ch:
                raise StarBoardError('Couldn\'t find original message.')

            return await self._unstar_message(ch, record['message_id'], starrer_id, connection=connection)

        query = """
            DELETE FROM starrers
            USING       starboard_entries entry
            WHERE       entry.message_id = $1
            AND         entry.id = starrers.entry_id
            AND         starrers.author_id = $2
            RETURNING   starrers.entry_id, entry.bot_message_id;
        """
        record = await connection.fetchrow(query, message_id, starrer_id)
        if not record:
            raise StarBoardError('\N{NO ENTRY SIGN} You haven\'t starred this message.')

        entry_id = record[0]
        bot_message_id = record[1]

        query = 'SELECT COUNT(*) FROM starrers WHERE entry_id = $1;'
        count = await connection.fetchrow(query, entry_id)
        count = count[0]

        if count == 0:
            query = 'DELETE FROM starboard_entries WHERE id = $1;'
            await connection.execute(query, entry_id)

        if not bot_message_id:
            return

        bot_message = await self.get_message(starboard.channel, bot_message_id)
        if not bot_message:
            return

        if count < starboard.threshold:
            self._about_to_be_deleted.add(bot_message_id)
            if count:
                query = 'UPDATE starboard_entries SET bot_message_id = NULL WHERE id = $1;'
                await connection.execute(query, entry_id)

            await bot_message.delete()
        else:
            msg = await self.get_message(channel, message_id)
            if not msg:
                raise StarBoardError('\N{BLACK QUESTION MARK ORNAMENT} This message couldn\'t be found.')

            content, embed = self.get_emoji_message(msg, count)
            await bot_message.edit(content=content, embed=embed)

    @commands.command(name='starboard')
    @commands.guild_only()
    @commands.has_permissions(manage_guild=True)
    @commands.bot_has_permissions(manage_roles=True, manage_channels=True)
    async def _starboard(self, ctx, *, name_or_channel: typing.Union[discord.TextChannel, str] = 'starboard'):
        """Sets up the starboard for this server.

        You can either reference an existing channel by mentioning it like `{prefix}starboard #channel`.
        This will make a starboard out of an already existing channel.

        If you want a new channel, you can provide a name like `{prefix}starboard shitcode`.
        In that case, the bot will create a new channel called `shitcode` which will be the starboard channel.
        *(Assuming there is no such channel with that name)*

        Or just use `{prefix}starboard`. This will create a channel called starboard.
        """

        self.get_starboard.invalidate(self, ctx.guild.id)

        starboard = await self.get_starboard(ctx.guild.id, connection=ctx.db)
        if starboard.channel:
            return await ctx.send(f'This server already has a starboard ({starboard.channel.mention}).')

        if hasattr(starboard, 'locked'):
            try:
                confirm = await ctx.confirm('Apparently, a previously configured starboard channel was deleted. Is this true?')
            except RuntimeError as e:
                await ctx.send(e)
            else:
                if confirm:
                    await ctx.db.execute('DELETE FROM starboard WHERE id = $1;', ctx.guild.id)
                else:
                    return await ctx.send(f'Aborting starboard creation. DM {self.bot.creator} for more information.')

        overwrites = {
            ctx.me: discord.PermissionOverwrite(read_messages=True, send_messages=True, manage_messages=True,
                                                embed_links=True, read_message_history=True),
            ctx.guild.default_role: discord.PermissionOverwrite(read_messages=True, send_messages=False,
                                                                read_message_history=True)
        }

        reason = f'{ctx.author} (ID: {ctx.author.id}) has created the starboard channel.'

        try:
            if isinstance(name_or_channel, discord.TextChannel):
                for target, ow in overwrites.items():
                    await name_or_channel.set_permissions(target, overwrite=ow, reason=reason)

            elif isinstance(name_or_channel, str):
                name_or_channel = await ctx.guild.create_text_channel(name=name_or_channel, overwrites=overwrites, reason=reason)

        except discord.Forbidden:
            return await ctx.send('\N{NO ENTRY SIGN} I do not have permissions to create a channel.')
        except discord.HTTPException:
            return await ctx.send('\N{NO ENTRY SIGN} This channel name is bad or an unknown error happened. No idea.')

        query = 'INSERT INTO starboard (id, channel_id) VALUES ($1, $2);'
        try:
            await ctx.db.execute(query, ctx.guild.id, name_or_channel.id)
        except Exception:  # muh pycodestyle
            await name_or_channel.delete(reason='Failure on creating the starboard.')
            await ctx.send(f'Could not create starboard due to an internal error. DM {self.bot.creator} for more information.')
        else:
            self.get_starboard.invalidate(self, ctx.guild.id)
            await ctx.send(f'\N{GLOWING STAR} Starboard created at {name_or_channel.mention}.')

    @commands.group(name='star', invoke_without_command=True, ignore_extra=False)
    @commands.guild_only()
    async def _star(self, ctx, message: MessageID):
        """Stars a message via message ID.

        To star a message, right click on the message and use the option `Copy ID`.
        You must have Developer Mode enabled to get that functionality.

        Though it's recommended that you react to a message with \N{WHITE MEDIUM STAR} instead.

        You can only star a message once.
        """

        await self.star_message(ctx.channel, message, ctx.author.id, connection=ctx.db)
        await ctx.message.delete()

    @commands.command(name='unstar')
    @commands.guild_only()
    async def _unstar(self, ctx, message: MessageID):
        """Unstars a message via message ID.

        To unstar a message, right click on the message and use the option `Copy ID`.
        You must have Developer Mode enabled to get that functionality.
        """

        await self.unstar_message(ctx.channel, message, ctx.author.id, connection=ctx.db)
        await ctx.message.delete()

    @_star.command(name='clean')
    @commands.has_permissions(manage_guild=True)
    @requires_starboard()
    async def _star_clean(self, ctx, stars=1):
        """Cleans the starboard.

        This removes messages in the starboard that only have less than or equal to the number of specified stars.
        This defaults to 1.

        *This only checks the last 100 messages in the starboard.*

        **This command requires the Manage Server permission.**
        """

        stars = max(stars, 1)
        channel = ctx.starboard.channel

        last_messages = await channel.history(limit=100).map(lambda m: m.id).flatten()

        query = """
            WITH bad_entries AS (
                SELECT     entry_id
                FROM       starrers
                INNER JOIN starboard_entries
                ON         starboard_entries.id = starrers.entry_id
                WHERE      starboard_entries.guild_id = $1
                AND        starboard_entries.bot_message_id = ANY($2::BIGINT[])
                GROUP BY   entry_id
                HAVING     COUNT(*) <= $3
            )
            DELETE FROM starboard_entries USING bad_entries
            WHERE       starboard_entries.id = bad_entries.entry_id
            RETURNING   starboard_entries.bot_message_id;
        """

        to_delete = await ctx.db.fetch(query, ctx.guild.id, last_messages, stars)

        min_snowflake = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22
        to_delete = [discord.Object(r[0]) for r in to_delete if r[0] > min_snowflake]

        try:
            self._about_to_be_deleted.update(o.id for o in to_delete)
            await channel.delete_messages(to_delete)
        except discord.HTTPException:
            await ctx.send('Could not delete the messages.')
        else:
            await ctx.send(f'\N{PUT LITTER IN ITS PLACE SYMBOL} Deleted {pluralize(**{"message": len(to_delete)})}.')

    @_star.command(name='show')
    @requires_starboard()
    @commands.cooldown(1, 10.0, commands.BucketType.user)
    async def _star_show(self, ctx, message: MessageID):
        """Shows the starred message via its ID.

        To get the ID of a message, right click on the message and use the option `Copy ID`.
        You must have Developer Mode enabled to get that functionality.

        You can only use this command once per 10 seconds.
        """

        query = """
            SELECT     entry.channel_id,
                       entry.message_id,
                       entry.bot_message_id,
                       COUNT(*) OVER(PARTITION BY entry_id) AS "Stars"
            FROM       starrers
            INNER JOIN starboard_entries entry
            ON         entry.id = starrers.id
            WHERE      entry.guild_id = $1
            AND        (entry.message_id = $2 OR entry.bot_message_id = $2)
            LIMIT      1;
        """

        record = await ctx.db.fetchrow(query, ctx.guild.id, message)
        if not record:
            return await ctx.send('This message hasn\'t been starred.')

        bot_message_id = record['bot_message_id']
        if bot_message_id is not None:
            msg = await self.get_message(ctx.starboard.channel, bot_message_id)
            if msg:
                embed = msg.embeds[0] if msg.embeds else None
                return await ctx.send(msg.content, embed=embed)
            else:
                query = 'DELETE FROM starboard_entries WHERE message_id = $1;'
                await ctx.db.execute(query, record['message_id'])
                return

        channel = ctx.guild.get_channel(record['channel_id'])
        if not channel:
            return await ctx.send('The message\'s channel has been deleted.')

        msg = await self.get_message(channel, record['message_id'])
        if not msg:
            return await ctx.send('The message has been deleted.')

        content, embed = self.get_emoji_message(msg, record['Stars'])
        await ctx.send(content, embed=embed)

    @_star.command(name='who')
    @requires_starboard()
    async def _star_who(self, ctx, message: MessageID):
        """Shows who starred a message.

        The ID can either be the starred message ID or the message ID in the starboard channel.
        """

        query = """
            SELECT     starrers.author_id
            FROM       starrers
            INNER JOIN starboard_entries entry
            ON         entry.id = starrers.entry_id
            WHERE      entry.message_id = $1
            OR         entry.bot_message_id = $1;
        """
        records = await ctx.db.fetch(query, message)
        if not records:
            return await ctx.send('No one starred this message or you provided an invalid message ID.')

        members = [
            str(ctx.guild.get_member(r[0]))
            for r in records
            if ctx.guild.get_member(r[0])
        ]

        try:
            base = pluralize(**{'star': len(records)})
            if len(records) > len(members):
                title = f'{base} ({len(records) - len(members)} left server)'
            else:
                title = base

            pages = Paginator(ctx, entries=members, per_page=20, title=title)
            await pages.interact()
        except Exception as e:
            await ctx.send(e)

    @_star.command(name='migrate')
    @requires_starboard()
    async def _star_migrate(self, ctx):
        """Migrates the starboard to the newest version.

        If you don't do this, the starboard will be locked for you until you do so.

        __**Please note that this is an incredibly expensive operation! It will take a very long time!**__

        You must have the Manage Server permission to use this command.
        """

        if not ctx.starboard.needs_migration:
            return await ctx.send('You are already migrated.')

        _avatar_id = re.compile(r'\/avatars\/(?P<id>[0-9]{15,})')
        start = time.time()

        perms = ctx.starboard.channel.permissions_for(ctx.me)
        if not perms.read_message_history:
            return await ctx.send(f'Bot does not have Read Message History permissions in {ctx.starboard.channel.mention}.')

        await ctx.send('Please be patient, this is going to take a long time!')
        async with ctx.typing():
            channel = ctx.starboard.channel

            # As this will take a while..
            await ctx.release()

            current_messages = await channel.history(limit=None).filter(lambda m: m.channel_mentions).flatten()

            message_ids = [m.id for m in current_messages]
            channel_ids = [m.raw_channel_mentions[0] for m in current_messages]

            await ctx.acquire()
            query = 'DELETE FROM starboard_entries WHERE guild_id = $1 AND NOT (bot_message_id = ANY($2::BIGINT[]));'
            status = await ctx.db.execute(query, ctx.guild.id, message_ids)

            _, _, deleted = status.partition(' ')
            deleted = int(deleted)

            # Get the up-to-date resolution of bot_message_id -> message_id
            query = 'SELECT bot_message_id, message_id FROM starboard_entries WHERE guild_id = $1;'
            records = await ctx.db.fetch(query, ctx.guild.id)
            records = dict(records)

            author_ids = []

            needs_requests = collections.defaultdict(list)

            # Just ignore this gore here
            for index, message in enumerate(current_messages):
                if message.embeds:
                    icon_url = message.embeds[0].author.icon_url
                    if icon_url:
                        match = _avatar_id.search(icon_url)
                        if match:
                            author_ids.append(int(match.group('id')))
                            continue

                author_ids.append(None)
                message_id = records.get(message.id)
                if message_id:
                    needs_requests[channel_ids[index]].append(message_id)

            query = """
                UPDATE starboard_entries
                SET    channel_id = t.channel_id, author_id = t.author_id
                FROM   UNNEST($1::BIGINT[], $2::BIGINT[], $3::BIGINT[])
                AS     t(channel_id, message_id, author_id)
                WHERE  starboard_entries.guild_id = $4
                AND    starboard_entries.bot_message_id = t.message_id;
            """
            status = await ctx.db.execute(query, channel_ids, message_ids, author_ids, ctx.guild.id)
            _, _, updated = status.partition(' ')
            updated = int(updated)
            await ctx.release()

            needed_requests = sum(len(a) for a in needs_requests.values())
            bad_data = 0

            async def send_confirmation():
                """Sends the confirmation messages."""

                delta = time.time() - start

                await ctx.acquire()
                query = 'UPDATE starboard SET locked = FALSE WHERE id = $1;'
                await ctx.db.execute(query, ctx.guild.id)
                self.get_starboard.invalidate(self, ctx.guild.id)

                if ctx.bot_has_embed_links():
                    embed = (discord.Embed(title='Starboard Migration', color=discord.Color.gold())
                             .add_field(name='Deleted:', value=deleted)
                             .add_field(name='Updated:', value=updated)
                             .add_field(name='Requests:', value=needed_requests)

                             .add_field(name='Name:', value=ctx.guild.name)
                             .add_field(name='ID:', value=ctx.guild.id)
                             .add_field(name='Owner:', value=f'{ctx.guild.owner} (ID: {ctx.guild.owner.id})', inline=False)
                             .add_field(name='Failed Updates:', value=bad_data)
                             .set_footer(text=f'Took {delta:.2f}s to migrate.'))
                    await ctx.send(embed=embed)

                else:
                    await ctx.send(
                        f'{ctx.author.mention}, I\'m done migrating!\n'
                        f'Deleted {deleted} out of date entries.\n'
                        f'Updated {updated} entries to the new format ({bad_data} failures).'
                        f'Took {delta:.2f}s.'
                    )

            if needed_requests == 0:
                await send_confirmation()
                return

            me = ctx.guild.me

            data_to_pass = {}
            for channel_id, messages in needs_requests.items():
                channel = ctx.guild.get_channel(channel_id)
                if not channel:
                    needed_requests -= len(messages)
                    bad_data += len(messages)
                    continue

                perms = channel.permissions_for(me)
                if not (perms.read_message_history and perms.read_messages):
                    needed_requests -= len(messages)
                    bad_data += len(messages)
                    continue

                for message_id in sorted(messages):
                    msg = await self.get_message(channel, message_id)
                    if msg:
                        data_to_pass[message_id] = msg.author.id
                    else:
                        bad_data += 1

            query = """
                UPDATE starboard_entries
                SET    author_id = t.author_id
                FROM   UNNEST($1::BIGINT[], $2::BIGINT[])
                AS     t(message_id, author_id)
                WHERE  starboard_entries.message_id = t.message_id;
            """

            await ctx.acquire()
            status = await ctx.db.execute(query, list(data_to_pass.keys()), list(data_to_pass.values()))
            _, _, second_update = status.partition(' ')
            updated += int(second_update)
            updated = min(updated, len(current_messages))

            # Fuck yeah, it's finally over
            await ctx.release()
            await send_confirmation()

    @staticmethod
    def records_to_value(records, fmt=None, default='None!'):
        if not records:
            return default

        emoji = 0x1f947
        fmt = fmt or (lambda o: o)
        return '\n'.join(f'{chr(emoji + i)}: {fmt(r["ID"])} ({pluralize(**{"star": r["Stars"]})})' for i, r in enumerate(records))

    async def star_guild_stats(self, ctx):
        embed = discord.Embed(title='Starboard Server Stats', color=discord.Color.gold())
        embed.timestamp = ctx.starboard.channel.created_at
        embed.set_footer(text='Adding stars since')

        # messages starred
        query = 'SELECT COUNT(*) FROM starboard_entries WHERE guild_id = $1;'

        record = await ctx.db.fetchrow(query, ctx.guild.id)
        total_messages = record[0]

        # total stars given
        query = """
            SELECT     COUNT(*)
            FROM       starrers
            INNER JOIN starboard_entries entry
            ON         entry.id = starrers.entry_id
            WHERE      entry.guild_id = $1;
        """

        record = await ctx.db.fetchrow(query, ctx.guild.id)
        total_stars = record[0]

        embed.description = f'{pluralize(**{"message": total_messages})} starred with a total of {total_stars} stars.'

        # Why am I doing this to myself?
        query = """
            WITH t AS (
                SELECT
                    entry.author_id AS entry_author_id,
                    starrers.author_id,
                    entry.bot_message_id
                FROM       starrers
                INNER JOIN starboard_entries entry
                ON         entry.id = starrers.entry_id
                WHERE      entry.guild_id = $1
            )
            (
                SELECT     t.entry_author_id AS "ID", 1 AS "Type", COUNT(*) AS "Stars"
                FROM       t
                WHERE      t.entry_author_id IS NOT NULL
                GROUP BY   t.entry_author_id
                ORDER BY   "Stars"
                DESC LIMIT 3
            )
            UNION ALL
            (
                SELECT     t.author_id AS "ID", 2 AS "Type", COUNT(*) AS "Stars"
                FROM       t
                GROUP BY   t.author_id
                ORDER BY   "Stars"
                DESC LIMIT 3
            )
            UNION ALL
            (
                SELECT     t.bot_message_id AS "ID", 3 AS "Type", COUNT(*) AS "Stars"
                FROM       t
                WHERE      t.bot_message_id IS NOT NULL
                GROUP BY   t.bot_message_id
                ORDER BY   "Stars"
                DESC LIMIT 3
            );
        """

        records = await ctx.db.fetch(query, ctx.guild.id)
        starred_posts = [r for r in records if r['Type'] == 3]
        embed.add_field(name='Top Starred Posts:', value=self.records_to_value(starred_posts), inline=False)

        to_mention = lambda o: f'<@{o}>'

        star_receivers = [r for r in records if r['Type'] == 1]
        value = self.records_to_value(star_receivers, to_mention, default='No one!')
        embed.add_field(name='Top Star Receivers:', value=value, inline=False)

        star_givers = [r for r in records if r['Type'] == 2]
        value = self.records_to_value(star_givers, to_mention, default='No one!')
        embed.add_field(name='Top Star Givers:', value=value, inline=False)

        await ctx.send(embed=embed)

    async def star_member_stats(self, ctx, member):
        embed = (discord.Embed(color=discord.Color.gold())
                 .set_author(name=member.display_name, icon_url=member.avatar_url_as(format='png')))

        query = """
            WITH t AS (
                SELECT
                    entry.author_id AS entry_author_id,
                    starrers.author_id,
                    entry.message_id
                FROM       starrers
                INNER JOIN starboard_entries entry
                ON         entry.id = starrers.entry_id
                WHERE      entry.guild_id = $1
            )
            (
                SELECT     '0'::BIGINT AS "ID", COUNT(*) AS "Stars"
                FROM       t
                WHERE      t.entry_author_id = $2
            )
            UNION ALL
            (
                SELECT     '0'::BIGINT AS "ID", COUNT(*) AS "Stars"
                FROM       t
                WHERE      t.author_id = $2
            )
            UNION ALL
            (
                SELECT     t.message_id AS "ID", COUNT(*) AS "Stars"
                FROM       t
                WHERE      t.entry_author_id = $2
                GROUP BY   t.message_id
                ORDER BY   "Stars"
                DESC LIMIT 3
            );
        """

        records = await ctx.db.fetch(query, ctx.guild.id, member.id)
        received = records[0]['Stars']
        given = records[1]['Stars']
        top_three = records[2:]

        query = 'SELECT COUNT(*) FROM starboard_entries WHERE guild_id = $1 AND author_id = $2;'
        record = await ctx.db.fetchrow(query, ctx.guild.id, member.id)
        messages_starred = record[0]

        embed.add_field(name='Messages Starred:', value=messages_starred)
        embed.add_field(name='Stars Received:', value=received)
        embed.add_field(name='Stars given:', value=given)

        embed.add_field(name='Top Starred Posts:', value=self.records_to_value(top_three), inline=False)

        await ctx.send(embed=embed)

    @_star.command(name='stats')
    @requires_starboard()
    async def _star_stats(self, ctx, *, member: discord.Member = None):
        """Shows statistics on the starboard usage of the server or a member."""

        if not member:
            await self.star_guild_stats(ctx)
        else:
            await self.star_member_stats(ctx, member)

    @_star.command(name='random')
    @requires_starboard()
    async def _star_random(self, ctx):
        """Shows a random starred message."""

        query = """
            SELECT bot_message_id
            FROM   starboard_entries
            WHERE  guild_id = $1
            AND    bot_message_id IS NOT NULL
            OFFSET FLOOR(RANDOM() * (
                SELECT COUNT(*)
                FROM   starboard_entries
                WHERE  guild_id = $1
                AND    bot_message_id IS NOT NULL
            ))
            LIMIT  1;
        """

        record = await ctx.db.fetchrow(query, ctx.guild.id)
        if not record:
            return await ctx.send('Couldn\'t find anything.')

        message_id = record[0]
        message = await self.get_message(ctx.starboard.channel, message_id)
        if not message:
            return await ctx.send(f'Message {message_id} has been deleted somehow.')

        if message.embeds:
            await ctx.send(message.content, embed=message.embeds[0])
        else:
            await ctx.send(message.content)

    @_star.command(name='lock')
    @commands.has_permissions(manage_guild=True)
    @requires_starboard()
    async def _star_lock(self, ctx):
        """Locks the starboard from being processed.

        This is a moderation tool that allows you to temporarily disable the starboard to aid in dealing with star spam.

        When the starboard is locked, no new entries are added to the starboard
        as the bot will no longer listen to reactions or star/unstar commands.

        To unlock the starboard, use the unlock command.

        To use this command, you need the Manage Server permission.
        """

        if ctx.starboard.needs_migration:
            return await ctx.send('Your starboard requires migration.')

        query = 'UPDATE starboard SET locked = TRUE WHERE id = $1;'
        await ctx.db.execute(query, ctx.guild.id)
        self.get_starboard.invalidate(self, ctx.guild.id)

        await ctx.send('Starboard is now locked.')

    @_star.command(name='unlock')
    @commands.has_permissions(manage_guild=True)
    @requires_starboard()
    async def _star_unlock(self, ctx):
        """Unlocks the starboard for re-processing.

        To use this command, you need the Manage Server permission.
        """

        if ctx.starboard.needs_migration:
            return await ctx.send('Your starboard requires migration.')

        query = 'UPDATE starboard SET locked = FALSE WHERE id = $1;'
        await ctx.db.execute(query, ctx.guild.id)
        self.get_starboard.invalidate(self, ctx.guild.id)

        await ctx.send('Starboard is now unlocked.')

    @_star.command(name='limit')
    @commands.has_permissions(manage_guild=True)
    @requires_starboard()
    async def _star_limit(self, ctx, stars: int):
        """Sets the minimum number of stars required to show up.

        When this limit is set, messages must have this number or more stars to show up in the starboard.

        You cannot have a negative limit and the maximum star limit you can set is 30.

        Note that messages that previously didn't meet the limit but now do will still not show up in the starboard until starred again.

        You must have the Manage Server permission to use this command.
        """

        if ctx.starboard.needs_migration:
            return await ctx.send('You starboard requires migration.')

        stars = min(max(stars, 1), 25)
        query = 'UPDATE starboard SET threshold = $2 WHERE id = $1;'
        await ctx.db.execute(query, ctx.guild.id, stars)
        self.get_starboard.invalidate(self, ctx.guild.id)

        await ctx.send(f'Messages now require {pluralize(**{"star": stars})} to show up in the starboard.')

    @_star.command(name='age')
    @commands.has_permissions(manage_guild=True)
    @requires_starboard()
    async def _star_age(self, ctx, number: int, units='days'):
        """Sets the maximum age of a message valid for starring.

        By default, the maximum age is 7 days.
        Any message older than this specified age is invalid of being starred.

        To set the limit, you must specify a number followed by a unit.
        The valid units are "days", "weeks", "months", or "years". They do not have to be pluralized.
        The default unit is "days".

        The number cannot be negative, and it must be a maximum of 35.
        If the unit is years, then the cap is 10 years.

        You cannot mix and match units.

        You must have the Manage Server permission to use this command.
        """

        valid_units = ('days', 'weeks', 'months', 'years')

        if units[-1] != 's':
            units = units + 's'

        if units not in valid_units:
            return await ctx.send(f'Not a valid unit! I expect only {human_join(valid_units)}.')

        number = min(max(number, 1), 35)

        if units == 'years' and number > 10:
            return await ctx.send('The maximum is 10 years!')

        query = f"UPDATE starboard SET max_age = '{number} {units}'::INTERVAL WHERE id = $1;"
        await ctx.db.execute(query, ctx.guild.id)
        self.get_starboard.invalidate(self, ctx.guild.id)

        if number == 1:
            age = f'1 {units[:-1]}'
        else:
            age = f'{number} {units}'

        await ctx.send(f'Messages must be less than {age} old to be starred.')

    @_star.command(name='jump')
    @requires_starboard()
    async def _star_jump(self, ctx, message: MessageID):
        """Shows a link to jump to a starred message given its ID.

        The ID can either be the starred message or the message in the starboard channel.
        """

        query = """
            SELECT
                entry.channel_id,
                entry.message_id
            FROM       starrers
            INNER JOIN starboard_entries entry
            ON         entry.id = starrers.entry_id
            WHERE      entry.guild_id = $1
            AND        (entry.message_id = $2 OR entry.bot_message_id = $2)
            LIMIT      1;
        """

        record = await ctx.db.fetchrow(query, ctx.guild.id, message)
        if not record:
            return await ctx.send('This message hasn\'t been starred.')

        await ctx.send(f'https://discordapp.com/channels/{ctx.guild.id}/{record["channel_id"]}/{record["message_id"]}')

    @commands.command(name='star_announce', hidden=True)
    @commands.is_owner()
    async def _star_announce(self, ctx, *, message):
        """Announce something in every starboard."""

        query = 'SELECT id, channel_id FROM starboard;'
        records = await ctx.db.fetch(query)
        await ctx.release()

        to_send = []
        for guild_id, channel_id in records:
            guild = self.bot.get_guild(guild_id)
            if guild:
                channel = self.bot.get_channel(channel_id)

                if channel and channel.permissions_for(guild.me).send_messages:
                    to_send.append(channel)

        await ctx.send(f'Preparing to send to {len(to_send)} channels (out of {len(records)}).')

        success = 0
        for channel in to_send:
            try:
                await channel.send(message)
            except Exception:  # muh pycodestyle
                pass
            else:
                success += 1

        await ctx.send(f'Successfully sent to {success} channels (out of {len(to_send)}).')


def setup(bot):
    bot.add_cog(StarBoard(bot))