Ejemplo n.º 1
0
    async def userstats(self, ctx, member: typing.Optional[discord.Member] = None, **flags):
        if member is None:
            member = ctx.author
        clauses = []
        role_filter = flags.get('role').title()
        faction_filter = flags.get('faction').title()

        with self.bot.db.conn.cursor() as cur:

            clauses.append(cur.mogrify(
                'player_id=%s', (str(member.id), )).decode('utf-8'))
            if role_filter != '':
                clauses.append(cur.mogrify(
                    'rolename=%s', (role_filter, )).decode('utf-8'))
            if faction_filter != '':
                clauses.append(cur.mogrify(
                    'faction=%s', (faction_filter, )).decode('utf-8'))
            clauses = ' AND '.join(clauses)

            query = ('SELECT result, COUNT(RESULT) FROM players'
                     ' WHERE {} GROUP BY result ORDER BY result DESC;'.format(clauses))
            cur.execute(query)
            results = cur.fetchall()
            wins = next((result[1]
                         for result in results if result[0]), 0)
            losses = next((result[1]
                           for result in results if not result[0]), 0)
            total = wins + losses

            if total == 0:
                return await ctx.send('No games played!')
            await ctx.send(f'Games: {total}\nWins: {wins}\nWinrate: {round(100 * wins/total)}%')
Ejemplo n.º 2
0
Archivo: util.py Proyecto: Narfee/neo
 async def avatar(self, ctx, **flags):
     """Get your own, or another user's avatar"""
     target = await BetterUserConverter().convert(ctx, flags.get("target"))
     new_size = constrained_round(flags["size"])
     formats = ["png", "jpeg", "webp", "jpg"]
     if f := flags.get("format"):
         main_format = f
Ejemplo n.º 3
0
 async def snipe(self, ctx, **flags):
     """Shows the most recently deleted messages in a given channel"""
     # i know i shouldnt be using json for this
     channel = flags.get('channel') or ctx.channel
     with open('./json_files/snipes.json', 'r') as f:
         snipes = json.load(f)
     try:
         channel_snipes = snipes[str(channel.id)]
     except KeyError:
         return await ctx.send(f"{channel} has no deleted messages.")
     embeds = []
     for snipe in reversed(channel_snipes[:flags.get('limit')]):
         author = self.bot.get_user(int(
             snipe['author'])) or await self.bot.fetch_user(
                 int(snipe['author']))
         embed = discord.Embed(colour=self.bot.colour)
         desc = snipe['content']
         if not desc and snipe.get('embed'):
             desc = '`<Embedded Message>`'
         embed.description = desc
         since = dt.strptime(snipe['created_at'], '%Y-%m-%d %H:%M:%S.%f')
         embed.set_author(name=f"{author} said in {str(channel)}",
                          icon_url=author.avatar_url)
         embed.timestamp = since
         embeds.append(embed)
     source = paginator.EmbedSource(embeds, footer=False)
     await paginator.CatchAllMenu(source).start(ctx)
Ejemplo n.º 4
0
 async def hl_block(ctx, **flags):
     """Add or remove a user from your list of people who won't highlight you, or just view the list
     Use the --add flag to add a user, and use --remove to do the opposite"""
     if not flags.get('add') and not flags.get('remove'):
         if b := ctx.bot.user_cache[ctx.author.id]['hl_blocks']:
             blocked = [ctx.bot.get_user(i).__str__() for i in b]
         else:
             blocked = ["No blocked users"]
         await ctx.quick_menu(blocked, 10, delete_message_after=True)
         return
Ejemplo n.º 5
0
 async def hl_whitelist(ctx, **flags):
     """Whitelist a guild for highlighting
     This will restrict highlights to only be allowed from guilds on the list"""
     if not flags.get("add") and not flags.get("remove"):
         if b := ctx.bot.user_cache[ctx.author.id]["hl_whitelist"]:
             whitelisted = [f"{ctx.bot.get_guild(i)} ({i})" for i in b]
         else:
             whitelisted = ["Highlight guild whitelist is empty"]
         await ctx.paginate(whitelisted, 10, delete_message_after=True)
         return
Ejemplo n.º 6
0
 async def purge(self, ctx, amount: int, **flags):
     """Purges a given amount of messages."""
     amount += 1
     await ctx.message.delete()
     if user := flags.get('user'):
         await ctx.channel.purge(limit=amount,
                                 check=lambda msg: msg.author == user)
         return await ctx.send(
             f'{ctx.tick()} **{amount - 1}** messages by {user} have been purged',
             delete_after=3)
Ejemplo n.º 7
0
 async def args_edit(self, ctx, **flags):
     """Edit the bot"""
     if pres := flags.get('presence'):
         if type_dict.get(pres[0]) is None:
             await self.bot.change_presence(status=ctx.me.status)
         elif type_dict.get(pres[0]) == 'streaming':
             pres.pop(0)
             await self.bot.change_presence(activity=discord.Streaming(
                 name=' '.join(pres), url='https://www.twitch.tv/#'))
         else:
             await self.bot.change_presence(status=ctx.me.status,
                                            activity=discord.Activity(
                                                type=type_dict[pres.pop(0)],
                                                name=' '.join(pres)))
Ejemplo n.º 8
0
async def oldest_scores(ctx, **flags):
    level = flags.get("level")
    offset = 0
    message = await ctx.send(embed=discord.Embed(
        title=f"Processing Data...", colour=discord.Colour(0x3586ff)))
    scores = get_oldest_scores_leaderboard(unbroken=flags["unbreaking"])
    if level:
        scores = compute_oldest_ranks([
            score for score in scores
            if score["level_short_name"] == str(level)
        ])
        l = all_levels.getByShortName(str(level))
        if l and flags["uploadhistory"]:
            data = {
                "any": l.leaderboard["any"]["top_history"],
                "unbroken": l.leaderboard["unbroken"]["top_history"],
            }
            with open("data/temp.temp", "w") as f:
                json.dump(data, f)
            with open("data/temp.temp", "rb") as f:
                await ctx.send(
                    file=discord.File(f, filename=f"oldest_data_{level}.json"))
    now = time.time()
    await message.delete()
    pages = menus.MenuPages(source=OldestLeaderboardViewer(
        scores, offset, flags["unbreaking"], flags["mobile"]),
                            clear_reactions_after=True)
    await pages.start(ctx)
Ejemplo n.º 9
0
 async def weather(self, ctx, city, **flags):
     """
     Shows the weather in your city.
     If you want the temperature to display in farenheit, add `-unit f` at the end of your command usage.
     If you want kelvin, add `-unit k` at the end. (Celsius is the default)
     """
     try:
         async with self.http as http:
             data = await http.get(
                 f"http://api.openweathermap.org/data/2.5/weather?appid={self.bot.config.weather}&q={city}")
         await http.close()
         if not str(unit := flags.get('unit') or 'c').startswith(('c', 'k', 'f')):
             return await ctx.send(
                 f"**{unit}** is an invalid unit! Please make sure your unit starts with either **c**, **k** or **f**")
         embed = discord.Embed(colour=self.bot.colour)
         temperature = round(cyberformat.get_temperature(data['main']['temp'], unit), 1)
         feels_like = round(cyberformat.get_temperature(data['main']['feels_like'], unit), 1)
         embed.title = data['name']
         weather = data['weather'][0]
         sunrise = dt.utcfromtimestamp(data['sys']['sunrise']).strftime("%I:%M %p")
         sunset = dt.utcfromtimestamp(data['sys']['sunset']).strftime("%I:%M %p")
         embed.description = f"**{weather['main'].title()}** - {weather['description'].capitalize()}\n"
         embed.description += f"<:temperature:742933558221340723> **{temperature}**° {unit[:1].capitalize()} (Feels Like **{feels_like}**° {unit[:1].capitalize()})\n"
         embed.description += f"☀️ Sunrise: **{sunrise}** UTC • Sunset: **{sunset}** UTC"
         embed.set_thumbnail(url="https://i.dlpng.com/static/png/6552264_preview.png")
         await ctx.send(embed=embed)
Ejemplo n.º 10
0
    async def specs(self,
                    ctx,
                    user: Member = None,
                    flags: flags.FlagParser(remove=bool) = flags.EmptyFlags):
        user = user if user else ctx.author
        uspecs = await self.bot.db.fetch('SELECT * FROM specs WHERE uid=$1;',
                                         user.id)
        if not uspecs:
            return await ctx.error(
                f'Specs not found for that user. Tell them to fill in this form\n<https://inv.wtf/sk1spec>'
            )
        else:
            if isinstance(flags, dict) and flags.get(
                    'remove',
                    False) and ctx.author.guild_permissions.manage_messages:
                con = await self.bot.db.acquire()
                async with con.transaction():
                    query = 'DELETE FROM specs WHERE uid=$1;'
                    await self.bot.db.execute(query, user.id)
                await self.bot.db.release(con)
                await user.remove_roles(
                    self.guild.get_role(595626786549792793),
                    reason=f'Specs removed by {ctx.author}')
                return await ctx.success(
                    f'Successfully removed specs for {user}')
            uspecs = uspecs[0]

            def escape(text: str):
                text = discord.utils.escape_markdown(text)
                text = re.sub(r'<a?:[a-zA-Z0-9\_]+:([0-9]+)>', '', text, 0,
                              re.MULTILINE)
                return text

            embed = discord.Embed(
                color=user.color,
                timestamp=datetime.datetime.now(
                    datetime.timezone.utc)).set_author(
                        name=str(user),
                        icon_url=user.avatar_url_as(static_format='png',
                                                    size=2048),
                        url=f'https://inv.wtf/sk1spec').add_field(
                            name='» CPU',
                            value=escape(uspecs['cpu'][:1024]),
                            inline=False).add_field(
                                name='» GPU',
                                value=escape(uspecs['gpu'][:1024]),
                                inline=False).add_field(
                                    name='» RAM',
                                    value=escape(uspecs['ram'][:1024]),
                                    inline=False).add_field(
                                        name='» Operating System',
                                        value=escape(uspecs['os'][:1024]),
                                        inline=False)
            return await ctx.send(embed=embed)
Ejemplo n.º 11
0
Archivo: dev.py Proyecto: Narfee/neo
 async def _dev_extensions(self, ctx, **flags):
     """Manage extensions"""
     async with ctx.loading():
         mode_mapping = {
             "r": self.bot.reload_extension,
             "l": self.bot.load_extension,
             "u": self.bot.unload_extension,
         }
         if flags.get("pull"):
             await do_shell("git pull")
         mode = mode_mapping.get(flags["mode"])
         extensions = (neo.conf["exts"] if flags["extension"][0] == "~" else
                       flags["extension"])
         for ext in extensions:
             mode(ext)
Ejemplo n.º 12
0
 async def _dev_extensions(self, ctx, **flags):
     """Manage extensions"""
     async with ctx.loading():
         mode_mapping = {
             'r': self.bot.reload_extension,
             'l': self.bot.load_extension,
             'u': self.bot.unload_extension
         }
         if flags.get('pull'):
             await do_shell('git pull')
         mode = mode_mapping.get(flags['mode'])
         extensions = conf.get(
             'exts') if flags['extension'][0] == '~' else flags['extension']
         for ext in extensions:
             mode(ext)
Ejemplo n.º 13
0
            await ctx.bot.user_cache.refresh()

    @flags.add_flag("-a", "--add", nargs="*")
    @flags.add_flag("-r", "--remove", nargs="*")
    @commands.command(name="whitelist", aliases=["wl"], cls=flags.FlagCommand)
    async def hl_whitelist(ctx, **flags):
        """Whitelist a guild for highlighting
        This will restrict highlights to only be allowed from guilds on the list"""
        if not flags.get("add") and not flags.get("remove"):
            if b := ctx.bot.user_cache[ctx.author.id]["hl_whitelist"]:
                whitelisted = [f"{ctx.bot.get_guild(i)} ({i})" for i in b]
            else:
                whitelisted = ["Highlight guild whitelist is empty"]
            await ctx.paginate(whitelisted, 10, delete_message_after=True)
            return
        strategy = "array_append" if flags.get("add") else "array_remove"
        snowflake = (flags.get("add") or flags.get("remove"))[0]
        async with ctx.loading():
            await ctx.bot.pool.execute(
                f"UPDATE user_data SET hl_whitelist = {strategy}(hl_whitelist, $1) WHERE "
                "user_id=$2",
                int(snowflake),
                ctx.author.id,
            )
            await ctx.bot.user_cache.refresh()

    @commands.command(name="remove", aliases=["rm", "delete", "del", "yeet"])
    async def remove_highlight(ctx, highlight_index: commands.Greedy[int]):
        """
        Remove one, or multiple highlights by index
        """
Ejemplo n.º 14
0
        """Edit the bot"""
        if pres := flags.get('presence'):
            if type_dict.get(pres[0]) is None:
                await self.bot.change_presence(status=ctx.me.status)
            elif type_dict.get(pres[0]) == 'streaming':
                pres.pop(0)
                await self.bot.change_presence(activity=discord.Streaming(
                    name=' '.join(pres), url='https://www.twitch.tv/#'))
            else:
                await self.bot.change_presence(status=ctx.me.status,
                                               activity=discord.Activity(
                                                   type=type_dict[pres.pop(0)],
                                                   name=' '.join(pres)))
        if nick := flags.get('nick'):
            await ctx.me.edit(nick=nick if nick != 'None' else None)
        if stat := flags.get('status'):
            await self.bot.change_presence(status=status_dict[stat.lower()],
                                           activity=ctx.me.activity)
        await ctx.message.add_reaction(ctx.tick(True))

    @commands.group(invoke_without_command=True)
    async def sudo(self, ctx, target: Union[discord.Member, discord.User,
                                            None], *, command):
        """Run command as another user"""
        if not isinstance(target, (discord.Member, discord.User)):
            new_ctx = await copy_ctx(ctx, command, author=ctx.author)
            await new_ctx.reinvoke()
            return
        new_ctx = await copy_ctx(ctx, command, author=target)
        await self.bot.invoke(new_ctx)
Ejemplo n.º 15
0
Archivo: dev.py Proyecto: Narfee/neo
    async def args_edit(self, ctx, **flags):
        """Edit the bot"""
        if pres := flags.get("presence"):
            if type_dict.get(pres[0]) is None:
                await self.bot.change_presence(status=ctx.me.status)
            elif type_dict.get(pres[0]) == "streaming":
                pres.pop(0)
                await self.bot.change_presence(activity=discord.Streaming(
                    name=" ".join(pres), url="https://www.twitch.tv/#"))
            else:
                await self.bot.change_presence(
                    status=ctx.me.status,
                    activity=discord.Activity(type=type_dict[pres.pop(0)],
                                              name=" ".join(pres)),
                )
        if nick := flags.get("nick"):
            await ctx.me.edit(nick=nick if nick != "None" else None)
        if stat := flags.get("status"):
            await self.bot.change_presence(status=status_dict[stat.lower()],
                                           activity=ctx.me.activity)
        await ctx.message.add_reaction(ctx.tick(True))

    @commands.group(invoke_without_command=True)
    async def sudo(self, ctx, target: Union[discord.Member, discord.User,
                                            None], *, command):
        """Run command as another user, or with all checks bypassed"""
        if not isinstance(target, (discord.Member, discord.User)):
            new_ctx = await copy_ctx(ctx, command, author=ctx.author)
            await new_ctx.reinvoke()
            return
        new_ctx = await copy_ctx(ctx, command, author=target)
Ejemplo n.º 16
0
class Dev(commands.Cog):
    """Commands made to assist with bot development"""
    def __init__(self, bot):
        self.bot = bot
        self.scope = {}
        self.retain = True
        self._last_result = None

    async def cog_check(self, ctx):
        return await self.bot.is_owner(ctx.author)

    @commands.command(aliases=['sh'])
    async def shell(self, ctx, *, args: CBStripConverter):
        """Invokes the system shell, attempting to run the inputted command"""
        hl_lang = 'sh'
        if 'cat' in args:
            hl_lang = return_lang_hl(args)
        if 'git diff' in args:
            hl_lang = 'diff'
        async with ctx.loading(tick=False):
            stdout, stderr = await do_shell(args)
            output = clean_bytes(stdout) + '\n' + textwrap.indent(
                clean_bytes(stderr), '[stderr] ')
            pages = group(output, 1500)
            pages = [ctx.codeblock(page, hl_lang) for page in pages]
        await ctx.quick_menu(pages, 1, delete_message_after=True, timeout=1800)

    @commands.command(name='eval')
    async def eval_(self, ctx, *, body: CBStripConverter):
        """Runs code that you input to the command"""
        env = {
            'bot': self.bot,
            'ctx': ctx,
            'channel': ctx.channel,
            'author': ctx.author,
            'guild': ctx.guild,
            'message': ctx.message,
            '_': self._last_result
        }
        env.update(globals())
        if self.retain:
            env.update(self.scope)
        stdout = io.StringIO()
        to_return = None
        to_compile = f'async def func(scope, should_retain=True):' \
                     f'\n  try:' \
                     f'\n{textwrap.indent(body, "    ")}' \
                     f'\n  finally:' \
                     f'\n    if not should_retain:' \
                     f'\n      return' \
                     f'\n    scope.update(locals())'
        async with ctx.loading(exc_ignore=HandleTb):
            try:
                import_expression.exec(to_compile, env)
            except Exception as e:
                raise HandleTb(ctx, e)
            evaluated_func = env['func']
            try:
                with redirect_stdout(stdout):
                    result = await evaluated_func(self.scope,
                                                  self.retain) or ''
            except Exception as e:
                raise HandleTb(ctx, e)
            else:
                value = stdout.getvalue() or ''
                self._last_result = result
                to_return = f'{value}{result}'
        if to_return:
            pages = group(to_return, 1500)
            pages = [ctx.codeblock(page, 'py') for page in pages]
            await ctx.quick_menu(pages,
                                 1,
                                 delete_message_after=True,
                                 timeout=1800)

    @commands.command()
    async def debug(self, ctx, *, command_string):
        """Runs a command, checking for errors and returning exec time"""
        start = time.perf_counter()
        new_ctx = await copy_ctx(ctx, command_string)
        stdout = io.StringIO()
        try:
            with redirect_stdout(stdout):
                await new_ctx.reinvoke()
        except Exception:
            await ctx.message.add_reaction('❗')
            value = stdout.getvalue()
            paginator = commands.Paginator(prefix='```py')
            for line in (value + traceback.format_exc()).split('\n'):
                paginator.add_line(line)
            for page in paginator.pages:
                await ctx.author.send(page)
            return
        end = time.perf_counter()
        await ctx.send(f'Cmd `{command_string}` executed in {end - start:.3f}s'
                       )

    @commands.command()
    async def sql(self, ctx, *, query: CBStripConverter):
        """Run SQL statements"""
        is_multistatement = query.count(';') > 1
        if is_multistatement:
            strategy = self.bot.conn.execute
        else:
            strategy = self.bot.conn.fetch

        start = time.perf_counter()
        results = await strategy(query)
        dt = (time.perf_counter() - start) * 1000.0

        rows = len(results)
        if is_multistatement or rows == 0:
            return await ctx.send(f'`{dt:.2f}ms: {results}`')
        rkeys = [*results[0].keys()]
        headers = [
            textwrap.shorten(col, width=40 // len(rkeys), placeholder='')
            for col in rkeys
        ]
        r2 = [list(r.values()) for r in results]
        r = []
        for item in r2:
            for i in item:
                r.append(
                    textwrap.shorten(str(i),
                                     width=40 // len(rkeys),
                                     placeholder=''))
        r = group(r, len(rkeys))
        table = tabulate(r, headers=headers, tablefmt='pretty')
        pages = [ctx.codeblock(page) for page in group(table, 1500)]
        await ctx.quick_menu(
            pages,
            1,
            delete_message_after=True,
            timeout=300,
            template=discord.Embed(color=discord.Color.main).set_author(
                name=f'Returned {rows} {pluralize("row", rows)} in {dt:.2f}ms')
        )

    @commands.group(name='dev', invoke_without_command=True)
    async def dev_command_group(self, ctx):
        """Some dev commands"""
        await ctx.send(
            "We get it buddy, you're super cool because you can use the dev commands"
        )

    @dev_command_group.command(name='delete', aliases=['del'])
    async def delete_bot_msg(self, ctx, message_ids: commands.Greedy[int]):
        for m_id in message_ids:
            converter = commands.MessageConverter()
            m = await converter.convert(ctx, str(m_id))
            if not m.author.bot:
                raise commands.CommandError(
                    'I can only delete my own messages')
            await m.delete()
        await ctx.message.add_reaction(ctx.tick(True))

    @dev_command_group.command(name='source', aliases=['src'])
    async def _dev_src(self, ctx, *, obj):
        new_ctx = await copy_ctx(ctx, f'eval return inspect!.getsource({obj})')
        await new_ctx.reinvoke()

    @dev_command_group.group(name='scope', invoke_without_command=True)
    async def _dev_scope(self, ctx, toggle: BoolConverter = None):
        if toggle is None:
            pages = group(str(self.scope), 1500)
            pages = [ctx.codeblock(page, 'py') for page in pages]
            await ctx.quick_menu(pages,
                                 1,
                                 template=discord.Embed(
                                     title=f'Retain: {self.retain}',
                                     color=discord.Color.main),
                                 delete_message_after=True,
                                 timeout=300)
            return
        async with ctx.loading():
            self.retain = toggle

    @_dev_scope.command(name='flush')
    async def _clear_scope(self, ctx):
        async with ctx.loading():
            self.scope = {}

    @flags.add_flag('-s',
                    '--status',
                    default='online',
                    choices=['online', 'offline', 'dnd', 'idle'])
    @flags.add_flag('-p', '--presence', nargs='+', dest='presence')
    @flags.add_flag('-n', '--nick', nargs='?', const='None')
    @flags.command(name='edit')
    async def args_edit(self, ctx, **flags):
        """Edit the bot"""
        if pres := flags.get('presence'):
            if type_dict.get(pres[0]) is None:
                await self.bot.change_presence(status=ctx.me.status)
            elif type_dict.get(pres[0]) == 'streaming':
                pres.pop(0)
                await self.bot.change_presence(activity=discord.Streaming(
                    name=' '.join(pres), url='https://www.twitch.tv/#'))
            else:
                await self.bot.change_presence(status=ctx.me.status,
                                               activity=discord.Activity(
                                                   type=type_dict[pres.pop(0)],
                                                   name=' '.join(pres)))
        if nick := flags.get('nick'):
            await ctx.me.edit(nick=nick if nick != 'None' else None)
Ejemplo n.º 17
0
class HighlightCommands(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @flags.add_flag('highlight', nargs='+')
    @flags.add_flag('-re', '--regex', action='store_true')
    @commands.command(cls=flags.FlagCommand)
    async def add(ctx, **flags):
        """
        Add a new highlight! When a highlighted word is used, you'll get notified!
        If the --regex flag is passed, the highlight will be compiled as a regex
        """
        subbed = re.sub(fr"{ctx.prefix}h(igh)?l(ight)? add", '', ctx.message.content)
        highlight_words = re.sub(r"--?re(gex)?", '', subbed).strip()
        if flags['regex']:
            check_regex(highlight_words)
        else:
            highlight_words = re.escape(highlight_words)
        active = await ctx.bot.conn.fetch('SELECT kw FROM highlights WHERE user_id=$1', ctx.author.id)
        if len(active) >= MAX_HIGHLIGHTS:
            raise commands.CommandError(f'You may only have {MAX_HIGHLIGHTS} highlights at a time')
        if highlight_words in [rec['kw'] for rec in active]:
            raise commands.CommandError('You already have a highlight with this trigger')
        await ctx.bot.conn.execute(
            'INSERT INTO highlights(user_id, kw) VALUES ( $1, $2 )',
            ctx.author.id, fr"{highlight_words}")
        ctx.bot.dispatch('hl_update')
        await ctx.message.add_reaction(ctx.tick(True))

    @commands.command(name='exclude', aliases=['mute', 'ignore', 'exc'])
    async def exclude_guild(ctx, highlight_index, guild_id: int = None):
        """Add and remove guilds to be ignored from highlight notifications.
        Currently ignored guilds will be un-ignored if passed a second time
        """
        if not index_check(highlight_index):
            raise commands.CommandError('Specify a highlight by its index (found in your list of highlights)')
        highlight_index = int(highlight_index)
        guild_id = guild_id or ctx.guild.id
        user_hl = [hl for hl in ctx.bot.get_cog("HlMon").cache if hl.user_id == ctx.author.id]
        current = user_hl[highlight_index - 1].exc_guilds
        strategy = "array_remove" if current and guild_id in current else "array_append"
        await ctx.bot.conn.execute(f'UPDATE highlights SET exclude_guild = {strategy}(exclude_guild, $1) WHERE '
                                   'user_id=$2 AND kw=$3',
                                   guild_id, ctx.author.id, user_hl[highlight_index - 1].kw)
        ctx.bot.dispatch('hl_update')
        await ctx.message.add_reaction(ctx.tick(True))

    @flags.add_flag('-a', '--add', nargs='*')
    @flags.add_flag('-r', '--remove', nargs='*')
    @commands.command(name='block', aliases=['blocks'], cls=flags.FlagCommand)
    async def hl_block(ctx, **flags):
        """Add or remove a user from your list of people who won't highlight you, or just view the list
        Use the --add flag to add a user, and use --remove to do the opposite"""
        if not flags.get('add') and not flags.get('remove'):
            if b := ctx.bot.user_cache[ctx.author.id]['hl_blocks']:
                blocked = [ctx.bot.get_user(i).__str__() for i in b]
            else:
                blocked = ["No blocked users"]
            await ctx.quick_menu(blocked, 10, delete_message_after=True)
            return
        strategy = 'array_append' if flags.get('add') else 'array_remove'
        person = await commands.UserConverter().convert(ctx, (flags.get('add') or flags.get('remove'))[0])
        async with ctx.loading():
            await ctx.bot.conn.execute(f"UPDATE user_data SET hl_blocks = {strategy}(hl_blocks, $1) WHERE "
                                       "user_id=$2", person.id, ctx.author.id)
            await ctx.bot.build_user_cache()